Skip to main content

ADR-0003 · Push-based secret rotation and cloud secret-manager adapters (candidate)

Status: proposed (candidate), revision 0.1 (2026-05-15) · Full normative text

:::caution Planned API — Phase 3 candidate This ADR is a tracking placeholder for the Phase 3 discussion. No normative decisions are made here; the page documents the shape of the open questions so context7 and operators know which names are reserved across bindings. None of the types or hooks described on this page exist in the v0.4 / v0.5 bindings yet. The candidate becomes binding only when every open question has an accepted answer and the status flips to accepted. :::

The "candidate" status borrows the convention used by IETF Internet-Drafts: issues raised against the document are tracked here, addressed through revisions, and the ADR is promoted only when the design is stable enough to bind implementations.

Why a separate ADR

ADR-0002 landed Phase 2 of the secrets surface: a pull-based SecretSource contract, EnvSecretSource and VaultSource adapters, lazy SecretRef resolution, and Config.refresh_secrets() for operator-driven rotation. Token-renewal background tasks and push-based change notification were explicitly deferred.

The deferral rests on two arguments:

  1. No production data yet. Until at least one operator runs the Phase 2 surface against rotation cadences observed in real deployments, every choice (poll interval, notification fan-out, retry / back-off, multi-region replicas) is design by guesswork.
  2. Subscription model still settling. ADR-0001 §7 specifies a subscription contract but Phase 1 bindings return an inactive handle. Implementing real subscriptions for config changes lands before secret subscriptions to reuse the plumbing.

Scope (four loosely coupled areas)

Phase 3 covers four areas. Each can land independently (separate PRs against this ADR), but the design choices in one affect the others enough to keep them in a single document.

A. Push-based rotation channel

ADR-0002 §11 sketched an on_secret_change(scheme, path, callback) extension to the existing Subscription handle from ADR-0001 §7.2.

The Phase 3 question set:

  • What does the SecretChangeEvent payload look like (path, scheme, source_id, new value vs notification-only)?
  • Which backends push? Vault Agent caching + lease watcher, AWS-SM EventBridge events, GCP Secret Manager Pub/Sub, K8s informer. Each has a different latency profile (Vault: seconds; AWS-SM: 10–60 s through EventBridge; Kubernetes: watch-based, sub-second).
  • Fall-back to polling when push is unavailable.
  • Coalescing semantics — rapid back-to-back rotations within a single TTL window should produce one operator-visible event, not N.

B. Cloud secret-manager adapters

_meta/secret_schemes.yaml reserves three Phase 3 schemes:

  • awssmAwsSecretsManagerSource — JSON-typed secrets, versioning (AWSCURRENT / AWSPENDING / AWSPREVIOUS), rotation lambdas, IAM-based auth.
  • gcpsmGcpSecretManagerSource — versioned secrets, per-key IAM, multi-region replicas, resource-name addressing (projects/<p>/secrets/<n>/versions/<v>).
  • k8ssecretK8sSecretSource — in-cluster Secret objects, namespace-scoped RBAC, watch-based change notification.

Each adapter mirrors VaultSource in shape (lazy resolve, JSON envelope handling, #field projection, optional ?version= query), but the auth model and the rotation-notification channel differ.

C. Token-renewal background hooks

Phase 2 deferred renewal entirely (see per-binding ADRs: config-python/adr/0001-vault-source.md §4, config-typescript/adr/0001-vault-source.md §4, config-go/adr/0001-vault-source.md §4). Phase 3 introduces:

  • An optional start() / close() lifecycle on SecretSource for adapters that need background goroutines / asyncio tasks / setInterval timers.
  • A renewal cadence the adapter computes from upstream metadata (Vault token TTL, AWS-SM rotation schedule).
  • Cancellation semantics — Config.close() MUST stop renewal tasks cleanly; partial-shutdown leaks must surface as observable errors.

Observability: metrics / events for renewal_attempt, renewal_failed, push_received, push_dropped, polling_fallback_active, aligned with logger-spec's secret_resolve_* family.

D. Composite and dynamic-secret patterns

Two derived patterns surface once §A-C exist:

  • CompositeSecretSource — fan-out to multiple backends under one scheme (e.g. vault-primary + vault-dr with automatic fail-over).
  • Dynamic secrets with leases (Vault database/creds/...) — a lease lifecycle for short-TTL credentials that the application must release on shutdown.

Pre-conditions for acceptance

The ADR moves from proposed (candidate) to accepted when:

  1. Phase 2 has been running in at least one production deployment for a rotation period that exposes the TTL math (~30 days for typical Vault deployments).
  2. ADR-0001 §7 subscriptions have at least one Phase 1 binding shipping real (non-inactive) subscriptions — the plumbing is reused, not invented.
  3. Each of the four areas (§A push, §B adapters, §C renewal, §D composite) has a written-out decision answering every open question with a Q-numbered argument.
  • ADR-0001 — base spec, subscription model in §7.
  • ADR-0002 — Phase 2 pull-based secret resolution. §11 sketches the push extension this ADR will formalise.

Normative source

The full candidate text — open questions Q1–Q9, pre-conditions for acceptance, the staged revision plan: config-spec/adr/0003-push-rotation-and-cloud-secret-managers.md.