Skip to main content
Every request reaching a server is judged as one of a small, fixed set of identity types. This page names those types, shows what each may do across the feature surface, and explains the composed layers that produce the verdict. It builds on access control (the request gate), accounts (the humans behind it), and owned keys (capped sub-identities).

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.
The two columns below are independent lists — the left is the admin-only set, the right is what an editor drives through its grants: 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:
  1. A base-tier ceiling (non-editable) — owner-scoping for a minted key, the reserved /api/auth control-plane gate, and the viewer read-only ceiling. This is the security posture a role inherits from its tier (admin, editor, or viewer); it is never authored through the grant map.
  2. A route action-class (declared at registration) — every authed route declares one of read, write, fenced, or secret. read/write are the grantable classes; fenced/secret are the admin-only fence. A route that declares no class fails boot — there is no allow-by-omission.
  3. A per-tag access-level map (editable) — the role’s level on each feature group: none, read, or write. This is the part an admin edits.
The action a request needs is derived from its HTTP method centrally — GET/HEAD/OPTIONSread, POST/PUT/PATCH/DELETEwrite — 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:
  • write admits the tag’s reads and its non-fenced writes;
  • read admits the tag’s reads only (GET / HEAD / OPTIONS);
  • none (the default for any tag not in the map) admits nothing.
A role granted a tag reaches every grantable route under it — including a route added later under that same tag. Shipping a brand-new feature group is a deliberate release step: its tag is level 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 not fenced — those routes are ordinary read/write class, held admin-only by the base-tier /api/auth control-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.
Because the class is declared on the route, the fence is not a hand-maintained list an author can forget to extend: a new mutating route that should be admin-only declares 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.
An authentication failure returns a fixed 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 the tai 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