Vault
forze[vault] implements the secrets contract against HashiCorp Vault (KV v2).
It supplies a SecretsPort so the rest of Forze — especially the routed,
per-tenant clients — can resolve credentials by reference instead of holding
them in config.
Works with OpenBao too: the integration suite passes unchanged against it (verified on OpenBao 2.6.1 — KV v2, Transit, key provisioning, JWT signing, token renewal).
Install¶
uv add 'forze[vault]'
Needs a Vault server with a KV v2 mount.
Settings¶
VaultSettings is the mountable form of VaultConfig: address, token, mount points and
namespace as a pydantic model. The address must be https:// unless it is a loopback one
— the token rides on every request and every response carries a secret, so http:// to
anything but this machine puts both on the wire. On a loopback address the client stops
honouring HTTP_PROXY, since requests would otherwise route even a localhost request
(and its token) through the proxy. See
connection settings.
Wire it¶
Build a client with its config, register the deps module (which publishes a
SecretsPort), and wire the lifecycle step:
from forze.application.execution import DepsRegistry, LifecyclePlan
from forze_vault import VaultClient, VaultConfig, VaultDepsModule, vault_lifecycle_step
vault = VaultClient(config=VaultConfig(url="https://vault.example.com", token="…"))
deps = DepsRegistry.from_modules(VaultDepsModule(client=vault))
lifecycle = LifecyclePlan.from_steps(vault_lifecycle_step())
What it provides¶
| Contract | Implementation | Dep key |
|---|---|---|
Secrets (resolve_str, exists) |
VaultKvSecrets (KV v2) |
SecretsDepKey |
Versioned reads (resolve_versioned, current_version) |
VaultKvSecrets (native KV v2 versions) |
SecretsDepKey |
Control-plane writes (put, rotator-facing) |
VaultKvSecrets |
SecretsAdminDepKey |
| Dynamic credentials (leases) | VaultDynamicSecrets (database engine) |
SecretsLeaseDepKey (opt-in) |
| Raw client | VaultClient |
VaultClientDepKey |
| Key management (envelope encryption) | VaultTransitKeyManagement (Transit) |
KeyManagementDepKey |
| Per-tenant KEK provisioning | VaultTransitTenantProvisioner (Transit) |
via TenantProvisionerPort |
| Token signing (RS256 / ES256) | VaultTransitSigner (Transit) |
via the identity authn signer |
Notes¶
- KV v2 only. The mount is set once on
VaultConfig.mount_point; aSecretRef.pathis mount-relative. - The client needs the lifecycle step —
VaultDepsModuleonly registers an already-constructed client; it doesn't initialize it. - This is what powers per-tenant secret routing (
secret_ref_for_tenant) for the routed Postgres/Mongo/HTTP/… clients — see Multi-tenancy. - Transit is a separate mount from KV.
VaultTransitKeyManagementis the KMS backend for envelope encryption (the KEK never leaves Vault),VaultTransitTenantProvisionercreates a tenant's Transit key on onboarding, andVaultTransitSignersigns JWTs (RS256/ES256) without the private key leaving Vault. - KV v2 assigns native version tokens, so the secrets lifecycle plane
(watchers, hot reload, the rotator) works over Vault without content hashing —
and
current_versionis served from KV metadata, never reading the payload. See Credential rotation. - Dynamic credentials need the database secrets engine enabled
(
VaultConfig.database_mount, defaultdatabase); registerVaultDynamicSecretsviaVaultDepsModule(dynamic_secrets=…)and pair it with the kit lease manager. Where you adopt leases for a backend, short TTLs are the rotation — skip the rotator there.