Skip to main content
An owned key is a capped sub-identity you hand to a teammate, a device, or a machine — it can never out-scope you, and it loses power the moment your own does. This guide mints one, checks what it may do, and moves it onto another device with a one-time QR claim link.

Prerequisites

Access control is on, and you are signed in to an account. As a non-admin owner, your credential is your login session: the Studio sign-in, or, for the CLI, the session token. Your own scopes are the ceiling for what you can delegate, and you may grant only scopes you hold yourself. An admin may use its admin key instead, and may also mint keys for a service principal. A non-admin’s key is itself an owned key, so it can never mint. Point TAI_SERVER_URL at your server and TAI_API_KEY at your session token (the CLI reads both from the environment). The CLI has no sign-in command of its own: the session token is what your accounts provider’s sign-in returns. GET /api/login/methods lists the provider’s sign-in methods and where each one submits; with the Postgres accounts provider, a password sign-in — POST /api/login/password with your email and password — returns it as data.token.

Mint a capped key

Mint a key owned by you. A non-admin owner may grant only a subset of its own scopes — the new key is automatically owned by the minter, and the raw sk-… value is returned exactly once, so capture it then.
examples/owned_keys/mint_owned_key.sh
Asking for a scope you do not hold yourself is rejected at mint time with a fixed 400 — the key is never silently narrowed to fit. From the Studio, the same mint lives on the API-keys tab of the Settings screen, where the scope picker is capped to your own scopes; its Full access (all scopes) box, which grants ["*"], is offered only to an admin or to a caller that holds * itself.
Ownership is exactly one level deep: an owned key can never itself mint keys. An admin may also mint a key for a service principal. See accounts for who may mint what.

Inspect what the key can do

A scoped key needs to know its own limits. tai auth whoami prints the caller’s capability projection — the routes, tools, and agents it can reach right now and its effective scopes, plus a mintable flag. Run it with the owned key (point TAI_API_KEY at the key you minted; the example keeps it in TAI_OWNED_KEY):
examples/owned_keys/whoami.sh
The projection is derived from the same store and policy the gate reads and is jq-exact, so it never lists a door the gate would then deny. It is also what the scoped Studio renders from — a limited key sees only the features it can reach. The mintable flag is not a per-caller grant: it reports whether this deployment’s identity provider can mint keys at all (a validator-only provider reports false). An owned key can never mint regardless — it sees mintable: true under a mint-capable provider yet still gets a 403 on any mint, because ownership is exactly one level deep.

Watch it die with the owner

An owned key never has to be revoked on its own — it is capped by its owner’s current policy on every request, so attenuating the owner attenuates the key on its very next call. Prove the mechanism end to end: disable the owner or empty the owner’s scopes, then make any call with the owned key. The gate re-evaluates the key against the owner’s now-empty standing and denies it with a 403 — the key is powerless while the owner is down, and recovers the instant the owner is restored. Deleting the owner principal is the final lever: every key it owned then fails to authenticate at all (401) and does not come back. (Revoking the owned key itself works the same way for that one key.) The owner is a principal, not a key: revoking one of the owner’s own keys changes nothing for the keys it owns. This is why you disable or delete a person once, rather than chasing down every key they were ever issued. To move a key onto another device without ever putting the secret in a URL, mint a one-time claim link. Its token rides the URL fragment (/login#claim=<token>), so it never reaches a server log, and the first exchange burns it.
examples/owned_keys/claim_link.sh
You may create a claim link only for a key you own (or, as an admin, any key); the submitted key is validated before the link is stored, so a bad key fails fast rather than producing a dead QR. Compose the absolute URL — or render a QR — from your own origin and the returned claim_path, then open it on the other device: the sign-in page recognises the #claim= fragment and exchanges the token for the key automatically.
The link is single-use and short-lived. A used, unknown, or expired token all answer the same 404 — deliberately indistinguishable, so the public exchange door gives an attacker no oracle to probe. There is no way to list or revoke a claim in flight: wait out the TTL, or revoke the underlying key (the exchange then refuses to hand out a revoked credential).

Grant scopes safely

Isolation makes some read scopes safe to grant to an owned key and leaves others operator-only. A restricted key sees only its own tool runs, interactions, and notifications, so those read scopes are safe. Never grant an owned key the observability, storage, presets, or schedules scopes — those surfaces are account-wide and unowned. The full table is in the owned-keys scope doctrine.

See also