The model at a glance
Before the full matrix, here is the one-line model: an editor does everything across the feature surface except access-control administration and a small set of admin-only deployment and infrastructure actions; a viewer sees that same surface read-only; and both always keep self-service over their own credentials.Admin-only means deployment-level. A handful of genuinely dangerous,
infrastructure actions are reserved for an admin and are never grantable to a
non-admin role. Everything else — the day-to-day feature-configuration surface —
an editor reaches through its role grants (a per-tag
read / write level),
because an editor is trusted to build and run the features it is granted.
A viewer reaches that same editor surface at
read only. Both an editor and a
viewer always keep self-service — their own API keys, their own identity, and
their own password.
Identity types
There are seven actors. Each is distinguished by how it authenticates and whether it is an admin — the admin verdict is a condition-free* policy that
carries no owner claim, computed server-side and stamped on the request, never
taken from a provider’s word.
The three composed layers
A role composes three parts, and enforcement is their intersection, fail-closed:- A base-tier ceiling (non-editable) — owner-scoping for a minted key, the
reserved
/api/authcontrol-plane gate, and the viewer read-only ceiling. This is the security posture a role inherits from its tier (admin,editor, orviewer); it is never authored through the grant map. - A route action-class (declared at registration) — every authed route
declares one of
read,write,fenced, orsecret.read/writeare the grantable classes;fenced/secretare the admin-only fence. A route that declares no class fails boot — there is no allow-by-omission. - A per-tag access-level map (editable) — the role’s level on each feature
group:
none,read, orwrite. This is the part an admin edits.
GET/HEAD/OPTIONS → read, POST/PUT/PATCH/DELETE → write — so the
same route classes read and write uniformly.
Permissions matrix
Rows are feature areas; columns are the identity types. Every cell reads against the current seeded roles. The cell words mean:by role — an owned key reaches a feature exactly where its owner’s live
role allows, intersected with the owner’s scopes and re-evaluated on every
request. There is no per-key grant map: a key follows its owner’s role up and
down as an admin edits it. Isolation to the caller’s own runs, interactions,
and notifications is the only hard resource confinement.
The grantable per-tag surface
The resource is a feature group (a route’s tag); the action is derived from the HTTP method — the same shape as Kubernetes RBAC, AWS IAM, or GitHub fine-grained permissions. A role’s level on a tag decides its reach:writeadmits the tag’s reads and its non-fenced writes;readadmits the tag’s reads only (GET / HEAD / OPTIONS);none(the default for any tag not in the map) admits nothing.
none for every non-admin role until
an admin grants it. The seeded editor and viewer roles derive their maps from
the live route registry, so a new grantable feature area joins their default
reach automatically; a role an operator authored does not, and an admin opens it
explicitly.
The admin-only fence
Two route action-classes are never grantable — no per-tag level opens them for any non-admin role. They cover the infrastructure mutations, the role and policy control plane, and the deployment’s env/settings store — not the everyday feature-configuration surface an editor drives through each feature’s own routes:fenced— the admin-only mutations. Running a tool directly (POST /api/run-tool); reloading or removing a tool; writing and reloading config (POST /api/config/env,/api/config/reload); replacing the manifest; reloading failed MCPs and deregistering an MCP; reloading the fleet; marketplace install / uninstall / update; backup import / export; and the role and policy control plane — creating, editing, deleting, and rolling back roles, and rolling back a user’s policy. Editing scopes, the route table, and the public-route pins is notfenced— those routes are ordinaryread/writeclass, held admin-only by the base-tier/api/authcontrol-plane ceiling described below.secret— the admin-only reads, over two admin-owned stores. The bulk role / policy reads: the roles listing (GET /api/auth/roles), the role version histories, and a user’s policy version history — their payload carries raw jq policy bodies, so a bulk read is admin-equivalent. And the deployment config store: the stored environment (GET /api/config/env) and the per-settings-class values (GET /api/config/settings-schema). Environment and settings are one store — the deployment’s environment plus each settings class’s values, including deployment secrets such as the connector encryption key — so only an admin reads or writes it.
fenced, and a route that declares nothing fails
boot rather than defaulting open.
An editor configures every feature; it does not own the deployment store. An
editor keeps full feature functionality — it configures connectors, MCP and
sub-MCP config, presets, hooks, schedules, and tool extensions through each
feature’s own grantable routes and stores, never through the env/settings
store. Running a tool through the async run surface (
POST /api/tool-runs),
scheduling a tool, setting the MCP config, reloading a single MCP server, mounting
a sub-MCP, setting tool extensions, registering hooks, and reading the hooks
listing are ordinary grantable read/write routes an editor reaches. What stays
admin-only is the deployment config store — reading the env and settings
(GET /api/config/env, GET /api/config/settings-schema) and the env write —
alongside the pre-existing infrastructure mutations (a direct tool run, tool
reload / remove, fleet reload, manifest replace, MCP reload-failed / deregister,
marketplace, backup) and access-control administration.The control-plane (/api/auth) gate
The editor and viewer base tiers default-deny the whole /api/auth area
and carve back in only a fixed self-service list: managing one’s own API keys
and claim links, the identity projection (me, capabilities, tokens-payload,
the read-only GET /api/auth/scopes), changing one’s own password, and logout.
Everything else under /api/auth — editing scopes, the route table,
public-route pins, policy history and rollback, and role and account management —
falls to the default-deny, so only an admin reaches it. This ceiling is separate
from the action-class fence, and it holds even for a viewer: the self-service
surface stays reachable regardless of the read-only ceiling.
How a request is judged
The verdict composes the layers with AND, evaluated fail-closed — a backend error at any layer raises and surfaces as a clean deny, never a silent default.1
Authenticate and resolve the role
The credential resolves to an identity and its policy. The policy carries a
live role-name pointer, not a frozen copy — enforcement reads the role’s
current grant map at request time. For an owned key, the governing
role is the owner’s, and the key’s scopes are intersected with the
owner’s right here, so a change to the owner takes effect on the key’s next
call.
2
The base-tier ceiling
The role’s base-tier condition applies: owner-scoping, the
/api/auth
control-plane gate, and — for a viewer — the read-only ceiling. An admin
is condition-free and skips this pass.3
The per-tag level + the fence
The request’s route resolves to its feature tags and action-class. A
fenced or secret route denies every non-admin outright. Otherwise the
governing role’s level on the route’s tag must satisfy the method’s derived
action. An admin skips this pass entirely.4
The route→scope table
The request path maps to scope ids, and the guard admits the request only if
the identity’s scopes cover every one. Because every role-holder carries
*, this gate only meaningfully constrains scoped keys. A route with
no mapping fails closed for every ordinary identity — the sole exception
is the stamped super-admin, admitted so a root can never lock itself out of a
route it can map anyway.401; an authenticated-but-forbidden
request returns a fixed 403. The full reason is logged server-side only.
Roles are live and editable
A role is not a fixed template. An admin can create, edit, delete, and roll back roles — through the Studio or thetai roles CLI —
and every edit is live: it changes every current holder’s authorization on
their next request, with no reassignment. Editing a role also re-attenuates every
key its holders own, since an owned key follows its owner’s live role. The
reserved admin role is permanent — undeletable, unrenamable, always full
control — and multiple admins are allowed; the last admin can never be
removed. See accounts for the editing surface and the
assignment flow.
An account is not an API key
Two things are easy to confuse because both hand someone a working identity. They are different in kind — who creates them, what sets their authority, and how far that authority reaches.
An account is the human side: only an admin creates one, and its authority is
the role the admin assigns by name. The account’s
enforced policy carries a live pointer to that role, not a copy — editing the
role changes the account’s access on its next request, and moving the account to a
different role re-points it immediately.
An API key is a credential any user mints to delegate a slice of its
own access to a teammate, a device, or a script. A key minted by a non-admin
is an owned key: attenuated to a subset of its
creator’s authority, capped live (the cap is re-applied every request, so it
follows the owner up and down), and isolated to its own slice of the runs,
interactions, and notifications surfaces. A key can never exceed the user who
created it, and an owned key can never itself mint further keys. Editing an
account’s role therefore also re-attenuates every key that account owns, without
touching the keys themselves.
See also
- Access control — the request gate, scopes, and the jq policy model.
- Owned keys — capped sub-identities, attenuation, and data isolation.
- Accounts — human accounts, sessions, and the editable role model.
- Set up access control — turn on the gate and write a policy.
tai roles— create, edit, and roll back roles from the terminal.- HTTP API reference — the routes behind each surface.

