
The Studio sign-in screen at /login.
Sign-in methods
The screen reads the server’s public login-methods listing and draws one control per declared method:- A credentials form — the provider names the fields (for example email and password) and the route to post them to. Submitting mints a session and lands you on the screen you were headed for.
- A redirect button — a single button that navigates to a provider’s sign-in flow (for example an OIDC authorize route). The provider owns the round trip; the callback returns a session.
First-run owner
On a fresh deployment there are no accounts yet, so the login listing reports a bootstrap state and the screen shows a one-time owner-creation form instead of a normal login. Completing it creates the initial admin — the owner — and the bootstrap form disappears the moment the owner exists. From then on the screen shows the ordinary sign-in methods.Invites
An admin grows the user set from the users-admin page. An invited person opens their invite link, which carries an invite token; the login screen recognises the token and shows a set-your-password form that activates the account. Invites are single-use and time-bounded — an expired or already-used invite is refused.Claim links
A claim link is how an owned key reaches a new device without the raw secret ever travelling in a URL. The link has the shape<origin>/login#claim=<token> —
the token rides the URL fragment, so it is never part of the request URL and
never reaches a server access log. When the sign-in screen loads with a #claim=
fragment, it
recognises the token, exchanges it once at the always-public
POST /api/login/claim, receives the raw key, and signs the Studio in with it —
the same held-in-the-browser session the key field produces, with no typing.
The exchange is one-time and time-bounded: the token burns on first use and
carries a short TTL. A token that was already claimed, is unknown, or has expired
all fail the same way — the screen reports the claim link as no longer valid and
falls back to the ordinary sign-in controls. The failures are deliberately
indistinguishable, so a stale link gives no signal an attacker could probe; when
one fails, mint a fresh key and a fresh claim link. Claim links are created in
the API-keys create dialog (or via
tai keys claim-link).
API-key fallback
The API-key field is always available, behind an “Use an API key instead” toggle when login methods are present. Pasting a deployment key signs the Studio in exactly as it always has — the key is held in the browser and sent on every request. This path is permanent: it is how an operator reaches a server that has no accounts provider, and how the headless, machine-key path stays usable alongside human login. See API keys for provisioning keys.Deploy notes
To turn on human login, a deployment enables an accounts provider and lists it in the auth-provider chain:- Add the accounts provider’s module to the server’s lifecycle and router modules so its login routes and session validation are mounted.
- Include the provider in
ACCESS_CONTROL_AUTH_PROVIDERS— the ordered auth-provider list the gate resolves credentials against — so its sessions are validated. - Apply the accounts plugin’s own schema as its README describes. A Postgres-backed provider ships its own migrations/DDL and documents how to apply them in its repository; run that step per the plugin’s own instructions.
The concrete settings, schema, and apply steps for a specific accounts provider
live in that plugin’s own repository README — this page covers the
skeleton-side wiring that every accounts provider plugs into.
See also
- Accounts — the session, ownership, and role model behind sign-in.
- Access control — multi-provider resolution and the public login namespace.
- API keys — the key fallback and owned credentials.


