Proposal: Secret handling for non-signing secrets
KeyCustody(ADR-0003) covers signing keys and ADR-0015 covers password/TOTP secrets — but the many other secrets an identity platform holds have no storage or rotation decision. This proposal closes that by applying existing primitives (Argon2id from ADR-0015, envelope encryption +KeyCustodyfrom ADR-0003 / ADR-0021, the directory from ADR-0011, admin rotation from ADR-0021) to every secret class. It introduces no new architecture; the one architectural fork (a dedicatedSecretStoreseam) is called out and deferred to an ADR.
Motivation
OAuth client secrets, upstream-IdP federation credentials (ADR-0019), SCIM bearer tokens (ADR-0018), the break-glass principal (ADR-0021), and backend/KMS/NATS credentials are all secrets with no recorded handling. A dump of a reversibly-stored client-secret table is mass relying-party impersonation — this is a correctness-level gap, not a detail.
Design — classify by how the secret is used
- Verify-only secrets (OAuth client secrets, API keys): stored hashed (Argon2id, ADR-0015), never reversibly; verified at the token endpoint. A store dump yields no usable secret.
- Use secrets (upstream-IdP client secrets for federation, SCIM bearer tokens Vord
presents, webhook signing keys): Vord must send these, so they are
envelope-encrypted at rest under a data-encryption key held by
KeyCustody(ADR-0003), ciphertext in the directory (ADR-0011), decrypted only at point of use, with per-tenant key separation (ADR-0012 / ADR-0023). - Bootstrap / deployment secrets (KMS and NATS credentials, the break-glass principal, backend DB creds): the operator's secret manager / deployment substrate (ADR-0024) — injected at runtime, never in Vord config or the repository. Out of Vord's runtime store entirely.
- Rotation for every class via the admin API (ADR-0021); client-secret rotation supports an overlap window (two valid secrets during cutover), mirroring key rotation (ADR-0023).
- No secrets in logs, telemetry, or events — a standing rule: the event stream (ADR-0022) and observability redact secrets, tokens, and PII; enforced by a CI check. This proposal is the rule's canonical owner; proposal 0009 (webhook payload redaction) and proposal 0012 (telemetry) reference and apply it rather than restate it.
Alternatives considered
- A dedicated
SecretStoretrait/seam instead of reusingKeyCustody+ the directory — cleaner separation, but it is new architecture; deferred to an ADR if a distinct seam is wanted. This proposal deliberately reuses existing seams. - Plaintext secrets behind disk encryption only — rejected (insufficient; no in-application boundary, no per-tenant separation).
Graduation criteria
No reversibly-stored client secret at rest; usable secrets are envelope-encrypted under
KeyCustody with per-tenant separation; bootstrap secrets are absent from config and the
repo; rotation works with overlap. Abuse-case tests (ADR-0028): a directory/store dump
yields only hashes (verify-only) or ciphertext (use secrets); a "no secrets in logs"
check passes.
Backward compatibility
Reuses existing primitives — no new architecture, no token/format change — unless the
SecretStore-seam option is taken (then an ADR).
Open questions
SecretStoreseam vs reuse (the one architectural fork).- Envelope-key rotation: re-encryption strategy and timing for the use-secret class.