Enhancement Proposal

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 + KeyCustody from 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 dedicated SecretStore seam) 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 SecretStore trait/seam instead of reusing KeyCustody + 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

  • SecretStore seam vs reuse (the one architectural fork).
  • Envelope-key rotation: re-encryption strategy and timing for the use-secret class.