Skip to main content
The Studio has no user database of its own. It authenticates with an API key provisioned on the server, holds it in the browser, and sends it on every request. The key is a full-access credential — treat it like a password.

Signing in

The operator gets a full-privilege API key from the server — the owner key tai setup prints once — then pastes it into the Studio at /login. The key is held in memory by default; the remember on this device opt-in persists it to the browser session so later navigations in the tab stay authenticated. When the server also runs an accounts provider, the key field sits behind the “Use an API key instead” toggle on the sign-in screen — a permanent fallback alongside human login.
The first key is a full-privilege credential, including tool execution. The Studio stores it client-side and sends it on every request — anyone with the key can run any tool on the server. Provision it out of band and treat it like a password.

Keys are owned credentials

Once an accounts provider is enabled, an API key is a credential owned by an account rather than a free-standing secret. Ownership changes what a key may hold and who may mint it:
  • A key can never out-scope its owner. A key a non-admin mints is capped at a subset of its owner’s own authority; a request for more is rejected at mint time. The cap is re-checked on every call, so if the owner’s role is narrowed — or the owner is disabled — the key is attenuated with them on its next request.
  • Machine keys belong to a service principal. For automation an admin creates a service principal — a non-human identity that holds keys but never logs in — and mints keys for it. That keeps the machine-to-machine path working while making an owner-bound key the default.
  • Key breadth is not admin. A wide ["*"] scope on a key does not by itself make its holder an administrator of the key surface; the owner’s role decides it. A ["*"] key owned by an administrator, with no condition on the key, is an administrator and can list, edit, and revoke other owners’ keys. A ["*"] key owned by any other user, one that carries a condition, or a narrower key, cannot.

Provisioning more keys

Once signed in, the operator provisions further keys — full-privilege or scoped — from the API-keys tab of the Settings screen. That tab drives the server’s /api/auth provisioning endpoints, so a key minted in the Studio is the same key you could mint from the tai keys CLI. When you mint a key, the create dialog asks which principal owns it — yourself by default; an admin may pick a service principal or create one inline. Every field in the dialog carries a one-line hint saying what it is for and whether it is optional. The Key user ID is required: a new, unique ID the key acts as, such as ci-runner, and it cannot be changed later. It is not the same field as the Principal user ID in the inline create-service-principal form, which names the principal and is optional — leave it empty to generate one. Scopes are picked one by one, or with Full access (all scopes), which mints the key with the universal ["*"] scope. While it is ticked the individual scopes are hidden and a warning says what the key holds: every scope, including scopes added later, yet no further than its owner’s access. If the owner is an administrator and the key has no condition, the key is an administrator too and can list, edit, and revoke other owners’ keys (see Key breadth is not admin above). The box is offered to an admin, or to a user whose own access already holds *; the edit dialog opens a key that holds * with it ticked, and the keys table shows that scope as a Full access badge. The keys table carries a Principal column. Each row shows the principal that owns the key — a human or a service principal, by kind and display name. A key whose identity record was lost in a restore shows no principal and is marked orphaned until the backup is re-imported. Ownership is the whole story of what a key may hold and who may revoke it: an owned key is capped at a subset of its owner’s authority and dies when its owner does. The scope picker in the create dialog is capped to your own scopes, and a non-admin is offered Full access only when it holds * itself, so a non-admin cannot mint a key wider than itself.
Key provisioning and access control run on the server’s Redis-backed auth store. A server booted without access control accepts any key; a deployment that enforces it provisions keys and policies through this tab.
The create dialog is also where you hand a freshly minted key to another device without ever putting the secret in a URL. The moment a key is created its raw sk-… value is shown once, and the dialog offers a Create claim link (QR) step: it turns that raw key into a one-time, short-lived claim link and renders it as a QR code. Scanning the code opens the sign-in page on the other device, which exchanges the claim token for the key and signs in — the token rides the URL fragment and burns on first use, so the credential never reaches a server log.
The API-keys create dialog showing a newly minted key and its QR claim-link step.The API-keys create dialog showing a newly minted key and its QR claim-link step.

The mint dialog's claim-link step: a freshly created key turned into a one-time QR claim link.

The claim-link step lives only in the mint dialog, never as a per-row action on an existing key. Keys are stored hash-only — the server keeps a hash, never the raw secret — so the plaintext key that a claim link must carry exists only in the moment right after creation. There is no way to regenerate a claim link for a key already in the table; mint a new key instead. See the owned-keys guide for the full claim flow, including the tai auth claim and CLI legs. See access control for the jq policy model behind scoped keys, owned keys for the delegation model, and the CLI reference for provisioning keys from a terminal.