Summary
Add a HashiCorp Vault KV2 backend to the secret_store credential provider. Because KV2 is a de-facto standard wire protocol, one backend covers three targets at once:
- OVHcloud Secret Manager — GA (2026), exposes a HashiCorp Vault KV2-compatible API; relevant to growing European adoption.
- HashiCorp Vault — the KV2 secrets engine itself (widely used in EU on-prem / regulated environments).
- OpenBao — the open-source Vault fork (same KV2 API).
Inherits decisions from the credential-provider epic #92; extends the secret_store provider (#97), sibling to the AWS Parameter Store backend (#157). Frame this as "KV2 protocol backend", not "OVH support" — OVH is one consumer of the same interface.
Do we support it today? No. Current secret_store backends: AWS Secrets Manager, AWS Systems Manager Parameter Store, Azure Key Vault, GCP Secret Manager.
Cross-cutting decision for the epic/keystone (#92 / #93) — read first
This backend departs from a core epic principle and that decision must be made at the umbrella, not silently here:
Open design decisions (resolve in the spec, before code)
- Reference disambiguation. A KV2 secret is an HTTPS URL like
https://vault.example.com/v1/<mount>/data/<path>, which structurally overlaps the Azure Key Vault form (https://<vault>.vault.azure.net/secrets/<name>). Backend routing currently infers from secret_ref shape, so KV2 needs a clean discriminator — candidates: a vault+kv2:// scheme, or requiring the /v1/.../data/ path marker. (Note: internal/config/secretref_test.go already rejects a bare vault:// scheme — the namespace was anticipated.)
- Auth method + where its credential lives. Decide which Vault auth methods to support first (token via
VAULT_TOKEN / token file? AppRole? Kubernetes auth?) and how that credential is supplied without putting a long-lived secret in signals.yaml (env/file/mounted, parallel to how the DB password is kept out of config today).
- KV2 payload mapping. KV2 returns a versioned JSON object of key/value pairs under
data.data. Map onto the existing secret_json_key extraction; decide version selection (latest vs pinned) and how it interacts with the cache/TTL model (KV2 has lease/version semantics).
- Transport / topology. Vault CA trust, Vault namespace (Enterprise / HCP), and OVHcloud-specific endpoint/region details.
Scope (STDD — spec first)
- Spec: a KV2 sub-spec (or an extension of
credential-provider-secret-store.md) covering the ref form, the new auth model + its invariants/failure modes, payload mapping, and NFRs — plus the keystone note on the stored-auth-credential trade-off.
- Acceptance cases (normal / boundary / invalid / failure), each referencing a spec ID.
- Failing tests with an injected fake KV2 client (no live network, per NFR003).
- Implementation:
SecretBackendHashiCorpVaultKV2 enum, ref parsing/disambiguation, a hashicorpVaultKV2Fetcher implementing secretFetcher, the Vault auth wiring, config + validation, failure taxonomy, guidance text.
- Live smoke (env-gated) against a real Vault/OpenBao and against OVHcloud Secret Manager's KV2 endpoint.
- Docs: a per-target recipe in
docs/database-connections.md (Vault / OpenBao / OVHcloud), with auth setup and least-privilege policy.
Acceptance criteria
- A
secret_store target with a KV2 reference fetches the DB password from Vault/OpenBao/OVHcloud and connects, with no DB password in config.
- The KV2 reference is routed unambiguously (no collision with the Azure Key Vault form).
- The Vault auth credential is supplied out-of-config (env/file/mounted), never inline in
signals.yaml.
secret_json_key extraction works against the KV2 data.data object.
- Secret values are never logged/persisted; only metadata.
- Unit tests pass with a fake KV2 client; live smoke passes against Vault/OpenBao and OVHcloud.
References
Summary
Add a HashiCorp Vault KV2 backend to the
secret_storecredential provider. Because KV2 is a de-facto standard wire protocol, one backend covers three targets at once:Inherits decisions from the credential-provider epic #92; extends the
secret_storeprovider (#97), sibling to the AWS Parameter Store backend (#157). Frame this as "KV2 protocol backend", not "OVH support" — OVH is one consumer of the same interface.Do we support it today? No. Current
secret_storebackends: AWS Secrets Manager, AWS Systems Manager Parameter Store, Azure Key Vault, GCP Secret Manager.Cross-cutting decision for the epic/keystone (#92 / #93) — read first
This backend departs from a core epic principle and that decision must be made at the umbrella, not silently here:
role_id+secret_id), or Kubernetes-auth JWT — i.e. a stored (or mounted) auth credential to then fetch the DB password.Open design decisions (resolve in the spec, before code)
https://vault.example.com/v1/<mount>/data/<path>, which structurally overlaps the Azure Key Vault form (https://<vault>.vault.azure.net/secrets/<name>). Backend routing currently infers fromsecret_refshape, so KV2 needs a clean discriminator — candidates: avault+kv2://scheme, or requiring the/v1/.../data/path marker. (Note:internal/config/secretref_test.goalready rejects a barevault://scheme — the namespace was anticipated.)VAULT_TOKEN/ token file? AppRole? Kubernetes auth?) and how that credential is supplied without putting a long-lived secret insignals.yaml(env/file/mounted, parallel to how the DB password is kept out of config today).data.data. Map onto the existingsecret_json_keyextraction; decide version selection (latest vs pinned) and how it interacts with the cache/TTL model (KV2 has lease/version semantics).Scope (STDD — spec first)
credential-provider-secret-store.md) covering the ref form, the new auth model + its invariants/failure modes, payload mapping, and NFRs — plus the keystone note on the stored-auth-credential trade-off.SecretBackendHashiCorpVaultKV2enum, ref parsing/disambiguation, ahashicorpVaultKV2FetcherimplementingsecretFetcher, the Vault auth wiring, config + validation, failure taxonomy, guidance text.docs/database-connections.md(Vault / OpenBao / OVHcloud), with auth setup and least-privilege policy.Acceptance criteria
secret_storetarget with a KV2 reference fetches the DB password from Vault/OpenBao/OVHcloud and connects, with no DB password in config.signals.yaml.secret_json_keyextraction works against the KV2data.dataobject.References