Files
orca/.ciagent/CLARIFY_v0.12.md
T
Jon Chery cf0df0f157 docs(P00): v0.12 security-hardening phase 0 (specify/clarify/research/ideate/plan/grill)
---ci---
project: orca
phase: 0
milestone: v0.12
status: specify
---/ci---

Threat-model review of entire surface incl OS (25 findings F1..F25).
Adopts R-021 (no Orca credentials: human=OIDC, machine=mTLS/SPIFFE).
Bundled Dex + WebAuthn (passkeys) as default password-free authenticator.
Master key seal-to-OIDC + Shamir 3-of-5 recovery.
30 net-new requirements (REQ-119..REQ-148). 29 phases. Binding conditions C-29..C-38.
2026-08-07 10:45:07 +00:00

6.5 KiB

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://<cluster>/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:<port>/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 <domain>. 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.