--- id: AUTHN.1 status: ratified ratified: 2026-09-01 (Jason Woltje; PRD rev1 ratification PR) --- # AUTHN.1 — Authentication accounts Agent-side provider credentials: the accounts seats use to reach providers. (Human login identity is D10 territory — better-auth as system of record — and is out of this section's scope.) ## Authentication configuration surface (WebUI page + CLI) | Control | Notes | |---|---| | in-browser OAuth establishment | the OAuth flow runs in-browser; whether the backing terminal flow is tmux-bridged is open: [[GOV.5-open-questions]] Q-N1 | | configured accounts list | provider, mode (OAuth/API), status, holder | | force renew | | | deactivate | deactivated accounts drop out of every seat/harness selector | | allowed harnesses | which harnesses may use this account | ## Custody rules - Secrets live with the **credential broker** (OpenBao/Vault or flat files per deployment mode — D3), never in the brain tree, never in manifests, never in Postgres records. Enforced role manifests declare `credentials: {store: none, providerTokens: denied}` — the enforced roles hold no credentials at all; accounts are a launcher/broker concern. - Multi-account per provider is a requirement (onboarding D4 already captures multi-account enrollment). - Account shape in the seat record (single account vs per-provider map) is open: [[GOV.5-open-questions]] Q-D2. ## Credential-broker custody rules (pulled 2026-08-31, generalized from the vault draft) - Reads require a token scoped to the needed paths; provisioning and writes go through a declared channel with documented purpose. An ordinary role never mints credentials or creates production paths. - Canonical secret path: `environment / service / component / secret-name`, lowercase kebab-case, nothing sensitive encoded in the path; environments never cross-reference each other's mounts. Standard field names (`username`/`password`, `token`, `host`/`port`/`url`). - Only the needed field is extracted into the consuming process; values are never echoed to logs or transcripts — read success is proven by field presence and digest, never by printing the value. - Least privilege, short-lived tokens, no local copies, immediate rotation on compromise; every access audited by the broker. ## E2 inputs (triage 2026-08-31; see [[GOV.2-docs-inventory]]) **Canonical ground truth**: `ADMIN-GUIDE/security/sso-providers.md` (D10 ground truth: better-auth + Authentik/WorkOS/Keycloak OIDC). **Pending pulls**: DRAFT S2 `identity-lifecycle.md` (D10 + the #1430 bootstrap fix) and `custody-schema.md`; brain `docs/guides/proposed/operations/vault.md` (credential-broker custody rules this section states without operational detail). ## S2 contract feed (extraction 2026-08-31) Full extraction record: lane `S2-EXTRACTION-2026-08-31.md` (per-contract cores, dependency edges, ruling cross-checks). Pulls binding on this section (identity-lifecycle, contract 4, plus wizard AUTHN clauses): - better-auth tables are the **only** account system of record (D10); IdPs are login methods only; account creation grants nothing. - `registration_mode` open/invite/closed, defaults **closed** post-bootstrap, forced closed during the epoch, enforced at a better-auth hook. - Bootstrap/first-admin invariant (#1430): zero-to-one-admin exactly once per epoch, one atomic transaction, durable fail-closed `bootstrap_state`, re-runnable; first-admin-via-SSO runs as a bootstrap-writer transaction, never JIT. **v1 first admin is password-only — a disclosed PRD deviation.** - JIT defaults OFF per-provider always; JIT users get `member`, never elevated; **role/authorization attributes are never mapped from IdP claims**. Linking keyed `(issuer, subject)`; explicit linking = step-up reauth ≤10 min; automatic linking gated by off-by-default `trusted_for_linking` + verified email. - Deactivation (ban) must bound all entry paths — **live defect: the admin-bearer-token path does not check banned status**. Deletion deferred; the existing hard-delete endpoint and `mosaic auth users delete` are mandated for removal. ## Seat auth shape ruling (Q-D2, Jason 2026-09-01) Per-provider map in `profile.json`, values are credential-broker references — never secret material. The broker custody rules above govern resolution; extraction stays field-scoped and digest-proven.