Roles and permissions
The four roles, how a permission decision is reached, and every gate a request passes through.
The four roles
Strictly ordered. Each includes everything below it, which means a comparison is "at least this role" rather than a set membership test.
viewer < developer < admin < owner| Role | Adds |
|---|---|
| See that resources exist, and their metadata. No values, no credentials. |
| Read and write secrets, use every registered resource, execute runbooks, request access. |
| Register infrastructure, manage members, approve access and executions, read the audit log. |
| Billing, subscription changes, and deleting the workspace. |
A fifth name, requester, appears on access-request endpoints. It is not a role - it is a floor meaning "any member", used where the point is that anyone may ask.
How a decision is reached
- 1
Which workspace is this request acting in?
Resolved from the account's current workspace, or from the organization named in the route. No membership means no role at all - not a default one.
- 2
What is their workspace role?
From the membership record. This is their baseline everywhere.
- 3
Is there a project override?
A per-project membership replaces the workspace role for that project. It can lower as well as raise.
- 4
Is there an active temporary grant?
An approved, unexpired access request can raise the effective role for its project until it expires.
- 5
Does the token permit it?
Abilities narrow the result. They never widen it - a viewer with
secrets:revealstill reveals nothing. - 6
Does the plan include it?
Feature and limit gates run last, so a permitted action on an unentitled plan returns a paywall rather than a permission error.
The gates
Every check is middleware, applied at the route. A controller cannot forget to call one, and reading the route file tells you exactly what protects an endpoint.
| Gate | Checks | Refusal |
|---|---|---|
| TLS |
|
| Client version not withdrawn |
|
| Token valid and unrevoked |
|
| Account and workspace not suspended |
|
| A workspace exists to act in |
|
| Workspace role, for console routes |
|
| Membership of a named organization |
|
| Effective role on a project |
|
| Tier includes the feature |
|
| Under the tier's cap |
|
| Plaintext request rate |
|
Why a forbidden resource is a 404
Listing endpoints return only what you can see, so a resource you have no access to is absent rather than forbidden. Fetching one directly returns 404 for the same reason.
A 403 on a resource you cannot see would confirm it exists, which is enough to enumerate another team's infrastructure by alias. The two cases are deliberately indistinguishable.
The platform control plane
Platform administration is a separate identity system, not a role in this hierarchy. Being a workspace owner grants nothing there, and being a platform admin grants nothing inside a customer workspace.
It has its own login, its own session store, its own middleware group, its own audit log, and step-up authentication for sensitive operations. Its abilities are metadata-only by construction - see Security model.
Self-hosting means you operate it. See Running it.
In practice
Invite as developer by default. Admin is for people who register infrastructure and approve access.
Use per-project overrides for the exceptions rather than promoting someone workspace-wide.
Keep production a separate project, so developer access to staging is not developer access to production.
Scope tokens to the job. A deploy pipeline rarely needs more than
secrets:read.Prefer a temporary grant over a permanent promotion. It expires without anyone remembering to remove it.
Something here wrong or missing? Tell us.