Declarative config: onvibe.json An app's configuration — who can access it, which emails are authorized, which routes stay public, its scheduled jobs and its framing policy — lives in a single declarative file, onvibe.json, at the project root. **This is the only way to change those settings: there are no per-setting tools. You edit the file and deploy it. A version lock** stops you from wiping config you never saw. The golden rule: download → edit → redeploy Never write onvibe.json from scratch. Always start from the current one: 1. Download the live config with get_config({ project_id }). It returns the current onvibe.json including a version field (a hash of the live config). 2. Edit the parts you want to change. Keep the version field exactly as it is. 3. Deploy the edited onvibe.json (stage it and deploy). The platform applies it **only if version still matches the live config**. If it doesn't (someone/something changed the config since you downloaded), the deploy is rejected with CONFIG_OUT_OF_DATE — call get_config again, re-apply your edit on top of the fresh file, and redeploy. This guarantees you can't accidentally delete config by regenerating a partial file. // onvibe.json — from get_config; edit and redeploy { "version": "d2a37fc5c623712a", // KEEP AS-IS — the lock "title": "My Book Club", // display name on the login pages (default: the subdomain) "access": { "policy": "signup", // public signup allowlist password "publicPaths": ["/", "/d/*"], // routes open despite auth "allowlist": ["a@example.com"] // only for policy "allowlist" }, "crons": [ // scheduled jobs (full set; omit to remove all) { "name": "daily-digest", "schedule": "0 9 * * *", "path": "/cron/digest" } ], "security": { "frame": "deny" // deny (default) sameorigin allow } } What it covers - title — the app's display name, shown on the managed login/signup pages. Omit it to fall back to the subdomain (the project id). - access.policy — the managed access policy (see the managed-auth doc). - access.publicPaths — routes open to anonymous visitors even under auth. - access.allowlist — authorized emails (only under allowlist). - crons — the complete set of scheduled jobs. A versioned deploy makes the crons match this list exactly (adds new ones, removes any not listed). Omit the section (or use []) to remove all. - security.frame — anti-framing policy. deny (default, the app can't be embedded in an iframe), sameorigin (only same-origin framing), or allow (opt out — embeddable anywhere). Omitting it is the safe default (deny). Custom domains and public-fork publishing are not part of onvibe.json on purpose — they aren't cloneable config (a fork starts private; a domain is unique to one app). Manage those with their own tools (add_custom_domain, set_project_public_fork) or the dashboard. What is NOT in onvibe.json These are managed separately and must not be put in the file: - Environment variables (keys or values) — use set_env / the dashboard. Secrets never go in a committed config file. - Resources / tier — set by the plan, not by the app. - The shared password (for policy: "password") — a secret; set it with set_access_policy. The file can declare policy: "password", but not the password itself. No per-setting tools There is no set_public_paths, add_allowed_email, create_cron or delete_cron. Every one of those settings is a field in this file: download, edit that field, deploy. One mental model, one audit trail, and the file in your project always matches what's live. The one exception is set_access_policy, which stays only because of the *shared password* of policy: "password" — a secret, and secrets never go in the file. Use it just for that. Two related tools are about state, not config: - get_project({ project_id }) — schedule, next run and last run of each cron job. - run_cron({ project_id, name }) — trigger a job once, now, for testing.