Files
stack/docs/PRDs/2026-08-31_PRD_rev1/AUTHN.1-auth-accounts.md
T
jason.woltje 13968e9a8b
ci/woodpecker/pr/ci Pipeline failed
docs: PRD rev1 review Q90 — Prettier-format the bundle, add rev0 archive README
Addresses CPS-PRD-REV1-REVIEW-Q90 findings F1 and F3 on PR #1491:

F1 (format gate): `prettier --write` on the 18 rev1 bundle documents.
Whitespace, table padding, list markers and emphasis delimiters only — a
normalized token comparison of every file before/after shows zero content
difference, so the ratified text is unchanged and no re-ratification applies.

F3 (rev0 relative links): the archived rev0 PRD stays byte-identical
(sha256 60cc2f98...36afdf verified) per GOV.1 archive-never-edit. A sibling
docs/PRDs/2026-08-26_PRD_rev0/README.md records the digest, the original
location, the owner disposition that the in-file links are intentionally
historical, and a resolution table for all 9 targets (11 occurrences), each
verified to exist. Future relocated archives ship the same README instead of
edited bytes.

F2 (test step) is investigated separately against the CI log.
2026-09-02 09:40:46 -05:00

86 lines
5.0 KiB
Markdown

---
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.