# CLARIFY v0.12: Security Hardening (Zero-Trust Identity) **Status**: resolved (full autonomy, 2026-08-07). All 10 clarifications resolved with the operator's locked decisions (D-238..D-247). No open questions remain for Phase 0. The `--ideate` flag was passed; the threat-model review drove the requirements. ## Resolved clarifications ### C1 — Milestone version (resolved) **Question**: v0.11 is complete; the v0.11 PRD deferred the v1.0.0 tag for UAT sign-off. Is this security-hardening milestone v1.0 (the UAT gate) or a minor v0.12? **Decision**: **v0.12 (minor, not v1.0).** The v1.0.0 production-ready tag stays deferred for post-v0.12 UAT, exactly as v0.11's PRD specified. v0.12 is a minor feature milestone. Per-phase tags run on the previous minor's patch line (v0.11.x): P0 -> `v0.11.0`, P01 -> `v0.11.1`, ..., final phase patch = `v0.11.29` = the v0.12 milestone release (no separate `v0.12.0` tag, per feature-milestone rule). **Affected**: config.json milestone field, all tag computation. ### C2 — OIDC provider model (resolved) **Question**: Bring-your-own IdP, bundled opinionated provider, or both? **Decision**: **Bundled Dex by default, with BYO external IdP as a config override.** `orca auth init-idp` bootstraps a local Dex on the lead (systemd unit + config template + Traefik route). `oidc.issuer` in config can be repointed to an external IdP (Keycloak/Authentik/ Google/etc.) anytime. Orca stays minimal (no bundled opinionated provider beyond Dex); Dex is the OIDC frontend, not a full IdP. **Affected REQs**: REQ-144 (OIDC client + bundled Dex). ### C3 — Bundled Dex upstream authenticator (resolved) **Question**: Dex needs an upstream identity source. "No passwords anywhere" rules out a local password store. What is the password-free upstream? **Decision**: **WebAuthn (passkeys) connector.** Bundled Dex gets a custom `orca-webauthn-connector` (~300 LoC Go, `go-webauthn` library) that serves registration + login HTML/JS pages behind Traefik at `https:///orca/webauthn/{register,login}`. The WebAuthn ceremony (biometric/security key) produces a public-key credential; Dex maps the credential ID to an OIDC `sub`. **Passkeys are public-key credentials -- the private key never leaves the authenticator -- so the "no passwords/secrets" invariant (R-021) holds.** For BYO external IdP deployments, the operator's existing authenticator (WebAuthn, TOTP, LDAP, etc.) is used; Orca never sees the upstream credentials. **Affected REQs**: REQ-148 (WebAuthn connector). **Affected phase**: P05 (new phase; wave B grows from 4 to 5 phases). ### C4 — Master key sealing (resolved) **Question**: How is the secrets master key protected at rest, given "no passwords anywhere"? **Decision**: **Seal to OIDC + Shamir 3-of-5 recovery.** The master key (32 random bytes) is encrypted (sealed) with a key derived from an OIDC token exchange at unseal time. `orca cluster unseal` (operator authenticates via OIDC -> token exchange -> unwrap master key into memory -> zeroed on shutdown). `orca cluster seal` for manual re-seal. The sealed blob is stored at `ClusterDir()/master.key.sealed` (0600). The raw master key never touches disk. **Shamir recovery**: at seal time, 5 shards are printed and the operator stores them offline. If the IdP is permanently lost AND a quorum of 3 shards is unavailable, the cluster is unrecoverable by design (documented residual risk; no backdoor). For the mTLS-only offline path (no OIDC), the seal key is derived from the cluster's own CA -- the operator holds the CA (a cert, not a password). The Shamir recovery path applies to the OIDC-sealed mode. **Affected REQs**: REQ-147 (master key seal). **Affected phase**: P08. ### C5 — CLI browser flow (resolved) **Question**: How does the CLI do the OIDC browser flow? **Decision**: **OIDC authorization-code + PKCE + local loopback redirect.** `orca auth login` opens the default browser to the Dex WebAuthn endpoint. After the ceremony, Dex redirects to `127.0.0.1:/callback` (local loopback, ephemeral port). The CLI exchanges the auth code for a short-lived ID token (1h) + refresh token. Headless/CI fallback: device-code flow (no browser needed). **Affected REQs**: REQ-144, REQ-148. ### C6 — WebAuthn RP ID / secure context (resolved) **Question**: WebAuthn requires a secure context (HTTPS). Where is the RP ID rooted? **Decision**: **Traefik-served cluster domain (step-ca cert, R-017).** Traefik already provides HTTPS on `127.0.0.1:8443` (nft DNAT from `:443`). The RP ID is the cluster's Traefik-served domain, configurable via `orca auth init-idp --rp-id `. For localhost dev, the operator uses the bootstrapped step-ca cert (self-signed, but WebAuthn accepts it for non-registerable credentials in dev mode). **Affected REQs**: REQ-148. ### C7 — Passkey storage (resolved) **Question**: Where are WebAuthn credentials stored? **Decision**: **SQLite at `ClusterDir()/webauthn-credentials.db` (0600). Public keys only.** The DB stores credential IDs, public keys, sign counts, and AAGUIDs. No private keys, no secrets, no passphrase wrapping. 0600 file mode for integrity (tamper detection), not secrecy. **Affected REQs**: REQ-148. ### C8 — Breaking-change handling (resolved) **Question**: P07 (remove all password/token paths) is a breaking change. How are existing v0.11 clusters handled? **Decision**: **`orca upgrade` refuses v0.11 clusters using `--password`/bare-tokens without `--accept-identity-migration`.** The flag prints the cutover documentation and requires explicit confirmation. No silent breakage. Documented in `docs/oidc.md` and the migration guide. **Affected REQs**: REQ-146, REQ-137. ### C9 — Token storage at rest (resolved) **Question**: Where are OIDC tokens stored locally? **Decision**: **`~/.orca/credentials.json` (0600). Short-lived (1h) + refresh.** Standard OIDC token storage. 0600 file mode. Refresh handles rotation; no long-lived Orca-issued tokens (the IdP issues them; Orca only stores them). **Affected REQs**: REQ-144. ### C10 — Phase count (resolved) **Question**: The threat model surfaced ~25 fix areas + the zero-trust identity work + docs + tests + final. More than 20 phases is acceptable per operator guidance. How many? **Decision**: **29 phases** (P0 + P01..P27 + P28 final). The operator explicitly accepted "more than 20 phases is acceptable if warranted." The GRILL stage may split/merge as needed (as v0.11 grill split P10 into P10a/P10b). **Affected**: PLAN_v0.12.md, ROADMAP.md. ## Open questions None. All 10 clarifications resolved. Phase 0 proceeds to RESEARCH.