# Sign-in and permissions

How accounts, workspaces and source tokens are scoped.

Contract: console-v1. Stage: private evaluation. Source snapshot: 23fdccfaa88f69e858769f1dad70f82739e2740f.

## Session protocol

POST /api/v1/session accepts username and password. The response includes user and expires_at; the opaque salad_session cookie is HttpOnly, SameSite=Strict, Path=/, and expires after eight hours. Secure is set when configured. PostgreSQL stores session digests, not plaintext cookies. GET /api/v1/session resolves the current user; DELETE revokes the current session. No API key or OAuth client-credentials grant is implemented for general console access.

## CSRF applies before login too

Every non-GET/HEAD session-protocol mutation—including login and signup—requires Origin exactly equal to the configured console origin and X-Console-CSRF: 1. Except DELETE, it also requires Content-Type: application/json even for a bodyless POST; send {} for preflight, token rotation and enable/disable requests. A browser's same-origin fetch does not automatically add an Origin to every GET; GET needs neither CSRF nor JSON headers. Do not bypass this protocol by relaxing the origin check.

## Gated evaluation workspaces

GET /api/v1/session/options is public. POST /api/v1/session/signup requires workspace_name, username and password; password_confirmation is optional but must match if present. Usernames are lowercased ASCII [a-z0-9_.@+-], 3–64 bytes. Passwords are 12–128 UTF-8 bytes, not characters. Workspace names are nonblank, control-character-free and at most 128 UTF-8 bytes; names may collide. Tenant/role fields are rejected. A new isolated workspace receives its own operator role and no inherited evidence, rules, cases, sources or inventory. Duplicate username and the 32-workspace cap use generic workspace_creation_unavailable. No email verification is claimed.

## Password and access limits

Passwords are salted Argon2id hashes. Login/signup share a PostgreSQL limit of 20 attempts per minute; at most two password jobs run concurrently per process. Bootstrap accounts have a stricter 16-byte password minimum. An operator must explicitly rotate the configured bootstrap password version as well as the password; changing a secret alone does not reset the stored account. Existing expiring sessions survive password rotation. Production TLS/SSO and broader account lifecycle controls remain separate qualification gates.

## Source tokens are not agent admin keys

A session operator rotates a token with POST /api/v1/sources/{id}/token. It is shown once; only its SHA-256 hash persists. Rotation revokes the prior token. Bearer authentication permits only POST ingestion for that exact source and GET operation status belonging to that source and tenant. It grants no source configuration, query, rule, case or approval authority. Bearer ingestion does not require browser cookies, Origin or CSRF. Supplying a bearer token selects source-token authentication rather than falling back to a cookie.

## Human authority

Costs, source-token rotation and approval/rejection require the operator role. Other documented session routes still enforce tenant scope. Workspace operators do not gain another workspace's authority. Keep human approval sessions outside agent automation. System One is planned advisory support; inference is currently disabled, cannot authorize actions, and never gates deterministic delivery.
