Projects
One organization, many apps — each with its own schema, data planes, and keys.
A project is the isolation unit inside your organization — the shape you know from Neon or Supabase. Each app you build gets its own project, and each project gets its own root and child branches, schemas, data planes, and API keys. Two teams in one org can ship two apps that never see each other’s models, keys, or rows.
organization
├── project: default ← every org has one; pre-project apps live here
│ ├── production root
│ ├── development/preview branches
│ └── branch-bound schema, data plane, and credentials
└── project: my-app ← npx ablo init creates this for a new app
├── production root
├── development/preview branches
└── branch-bound schema, data plane, and credentials
The default project
Every organization has a default project. If you never create another one, everything works exactly as if projects didn’t exist — your keys, schema pushes, and registered databases all belong to it. Keys minted before projects existed are default-project keys automatically.
Keys belong to exactly one project
A key’s project is fixed at mint and can never be changed or overridden —
the same discipline as its immutable branch binding. Everything a runtime key mints
inherits its project: the short-lived session credentials (ek_), agent
keys (rk_), everything. There is no way to “switch projects” with an
existing key; you use a key minted for the project you mean.
Schema pushes, database registrations, reads, and writes all act on the key’s project:
npx ablo pushactivates the schema for the pushing key’s project — it can never demote another project’s schema.- Registering a database (
DATABASE_URL) attaches it to the key’s project and immutable branch. - A write or read against a model that belongs to another project in
your org fails with a typed
project_scope_denied— never a silent empty result, and never the misleading “unknown model, run ablo push”.
Branches belong to a project
Production is the protected root branch; development and preview branches are children. Each branch has its own rows, active schema artifact, claims, log, credentials, and optional registered database. A child copies the parent’s active schema when created and owns its history afterward, so a feature-branch push cannot change a sibling or production.
ablo dev derives a child from Git and mints an expiring sk_ credential
bound to its immutable branch id. Production runtimes use sk_ bound to
the root. There is no shared sandbox schema and no runtime environment switch.
CLI
npx ablo init creates a project for your app automatically (slug derived
from your package.json name; --project <slug> to choose, --no-project
to stay on the org default). Manage projects any time:
npx ablo projects list # all projects (default first)
npx ablo projects create my-app # create one (--name "Display Name")
npx ablo projects use my-app # set the ACTIVE project locally
npx ablo projects use default # back to the org default
npx ablo status # shows the active project
The active project is a local targeting preference in your CLI config: new
management and branch credentials you mint pick it up. It never changes what
an existing key can reach — project and branch scope are decided server-side at
mint. Use npx ablo whoami to confirm the exact target of the active
credential.
API
Projects are a control-plane resource authenticated with a management (mk_)
key:
curl https://api.abloatai.com/api/v1/projects \
-H "Authorization: Bearer $ABLO_API_KEY" # list
curl https://api.abloatai.com/api/v1/projects \
-H "Authorization: Bearer $ABLO_API_KEY" \
-H "content-type: application/json" \
-d '{"slug": "my-app", "name": "My App"}' # create
A create with a taken slug fails with project_slug_taken (409). Reads of
another org’s project ids 404 — never confirm existence across orgs.
Errors
| Code | Status | Meaning |
|---|---|---|
project_scope_denied |
403 | The model/resource belongs to another project in your org: use a key minted for that project. |
project_slug_taken |
409 | A project with this slug already exists in the organization. |