Scopes and project restrictions
The two scope catalogs, wildcards, and why a key is bounded by both its scopes and its owner.
A scope is a permission the panel already has, with a prefix saying which catalog it comes from:
project:apps:deploy a project permission, checked per project
platform:projects:view a platform permission, checked globallyThe prefix is not decoration. quotas:view exists in both catalogs and means different things — one project’s budget versus every user’s. Without the prefix, one string would silently authorise the wider of the two.
Wildcards
Action wildcards are allowed: project:apps:* covers every action on applications, including actions added in a later release. Resource wildcards are not: project:* would be “everything”, which is precisely the grant a reviewer needs to see spelled out.
project:ghost:* — is refused too. It would sit in a key’s scope list looking like a grant while conferring nothing.The scopes you will actually use
GET /api/api-keys/scopes on your own instance is the authoritative list — it is generated from the live permission catalogs, so it never drifts. A few that come up constantly:
| Scope | Reaches |
|---|---|
project:apps:view apps:create apps:delete | List and read, create and update, delete applications. |
project:apps:deploy | POST /deployments, redeploy, rollback. |
project:apps:restart | Start, stop, restart. |
project:apps:logs | Container logs. |
project:apps:env | Read and write environment variables. |
project:deployments:view | Deployment list and detail. |
project:databases:view databases:create databases:manage databases:delete | The database surface — manage covers start/stop, credentials, export and import. |
project:domains:view domains:manage domains:delete | Domains, verification, DNS sync. |
project:backups:view backups:create backups:restore backups:delete | Backup schedules, runs and restores. |
platform:projects:view platform:projects:manage | The project list itself, and creating or updating projects. |
Two ceilings, not one
A key’s effective rights are `owner ∩ scopes`.
The scope check is an extra filter on top of the account’s own permissions, never a replacement for them. Every downstream check — role, platform permission, project membership — runs against the live user row behind the key, exactly as it would for that person’s own session. Consequences worth knowing:
- Granting
project:apps:deleteto a key owned by someone with read-only access to a project grants nothing there. - Demote or ban the owner and their keys stop working on the very next request — the user row is re-read per request, not cached from mint time.
- A
projectIdsrestriction narrows the key *below* its owner. It can never widen it.
How the project restriction is enforced
Wherever a request names a project — the route (/projects/:id, :projectId), the query string (?projectId=), or the body — and, failing all three, on the owning project of the resource.
POST /api/applications/:id/restart names an application, not a project; the guard resolves that application’s project before deciding. Same for databases, domains, backups, instances, and deployments — which resolve through their application. A key pinned to project A cannot restart an application in project B by using the application’s id.
Where a request names no project at all — GET /api/deployments answers “everything I can see” — the restriction is applied to the rows: anything outside the key’s projects is dropped from the response.
What is reachable by a key at all
An endpoint is key-callable only if it was explicitly opened. There is no “allow unless denied” — every endpoint written in future is session-only until someone deliberately opens it, which is what stops a CI token from quietly becoming a full account over a few releases.
Currently open:
| Area | Base path and coverage |
|---|---|
| Applications | /api/applications — create, list, read, update, delete, logs, env, start/stop/restart, redeploy, rollback |
| Deployments | /api/deployments — trigger, list, read |
| Projects | /api/projects — create, list, read, update, members, usage, mesh, RBAC catalog |
| Databases | /api/databases — create, list, read, lifecycle, credentials, export/import, delete |
| Domains | /api/domains — add, list, read, update, verify, health, records, DNS sync, delete |
| Backups | /api/backups — schedules, runs, manifests, restore, verify, export, per-project storage config, delete |
Handlers outside that list — exec into a container, move an application between servers, transfer a project, edit roles, anything under /api/admin — answer a key with:
{
"statusCode": 403,
"message": "This endpoint is not available to API keys. Use a session, or open it with @RequireApiScope."
}