Key Rotation
Rotate secret versions under a fresh data key, rewrap the master key, and seal or unseal the builtin vault.
Key rotation keeps credentials and encryption keys moving without dropping the app. On the builtin
vault driver you rotate a secret version (fresh data key) or the master key (KEK rewrap).
Contract rotate: "90d" is a Console cadence hint — it does not rotate by itself.
For operators and Flows that must cut over keys safely — prefer CLI for master material; use
fx.vault.rotate for path versions inside privileged Flows.
The one rule
Never pass master keys as CLI argv in shared shells — they land in history. Prefer oke vault unseal --key -, the env OKE_VAULT_MASTER_KEY, or the hidden prompt.
Smallest Example
Initialize and set a secret (builtin driver)
# drivers.vault = "vault" — SQL-backed AES-256-GCM
oke vault init # prints master key once — store out of band
export OKE_VAULT_MASTER_KEY=… # or --key - from stdin
oke vault set STRIPE_KEY
# prompts for value, or: oke vault set STRIPE_KEY sk_live_…Rotate to a new version
oke vault rotate STRIPE_KEY sk_live_new_key
# → oke vault: rotated STRIPE_KEY → v2 (fresh data key)Omit the value to re-encrypt the current cleartext under a new data key (version bumps, readers still see the same string until you change it).
Confirm status
oke vault status
# initialized, unsealed, kek version, secret countProgressive Patterns
From CLI path rotate to Flow mutations, cadence hints, and master rewrap:
oke vault rotate prod/api/stripe sk_live_new
oke vault rotate prod/api/stripe # same cleartext, fresh DEKNeeds an unsealed builtin backend. Missing path:
oke vault: no such secret: prod/api/stripeVersion physics
Rotate — version vs master
fresh DEK · KEK rewrapidlerotatedonesecret versionoke vault rotate STRIPE_KEYpathSTRIPE_KEYversionv1DEKcurrentcleartextunchanged*
New version under a fresh data key. Pass a value to change what readers see; omit to re-encrypt only.
master keyoke vault rotate-masterkekVersionv1DEKswrappedsecretssame cleartextoperator·
New KEK generation — every DEK re-wrapped. Print the new master once; update OKE_VAULT_MASTER_KEY.
* Cleartext changes only when you pass a new value to rotate / fx.vault.rotate.
Builtin storage encrypts each version with its own data key (DEK), wrapped by a KEK derived from the master key:
| Operation | What changes | Readers see |
|---|---|---|
set / rotate + value | New version + (rotate) fresh DEK | New cleartext on next get |
rotate without value | New version + fresh DEK | Same cleartext |
rotate-master | New KEK; DEKs re-wrapped | Same cleartexts; new master required |
delete | Crypto-shred path | Subsequent get misses |
Paths are slash-separated with no leading slash (prod/api/stripe). Invalid paths throw
VaultError INVALID_PATH.
Consequence: secret access is not journaled — durable Flow replay re-reads live vault state after a rotate instead of replaying a stale credential from the journal.
Seal & unseal
| Command / state | Meaning |
|---|---|
oke vault init | Create backend state; print master key once |
oke vault seal | Drop in-memory master; reads fail with SEALED |
oke vault unseal | Restore master from --key / env / prompt |
oke vault status | initialized, sealed flag, kekVersion, secretCount |
oke vault unseal --key - # read base64 master from stdin
oke vault status --jsonCLI reference
Env / dotenv bag loop:
| Command | Purpose |
|---|---|
oke vault set <NAME> [value] | Write / overwrite a name |
oke vault list | List names (never values) |
oke vault import <file> | Bulk import |
oke vault key rotate | Env-loop key helper |
Builtin encrypted store:
| Command | Purpose |
|---|---|
oke vault init | First-time initialize |
oke vault status [--json] | Seal / KEK / counts |
oke vault seal / unseal | Master lifecycle |
oke vault rotate <path> [value] | Version + fresh DEK |
oke vault rotate-master | KEK rewrap |
oke vault audit … | Audit trail / verify / purge |
oke vault purge-expired | Drop expired rows |
oke vault backup / restore | File snapshot |
--url overrides the SQL URL (DATABASE_URL / OKE_STORE_SQL_URL).
Troubleshooting
Rotate/get targeted a path that was never set. oke vault list (or Console) for live paths;
remember per-tenant storage uses {tenantId}/{name}.
Process holds no master key. Export OKE_VAULT_MASTER_KEY or oke vault unseal before
rotate-master / reads that need the adapter.
Another master-rotation lease or batch is in flight. Wait, or resume with oke vault rotate-master --new-key (stdin - preferred).
Console or fx.vault.rotate hit a non-builtin bag (env / memory / managed). Pin
drivers.vault to "vault" and ensure SQL is available.
Same as above — mutations need the encrypted adapter. fx.vault.get alone works on any driver.
Dry-run refuses vault writes so a live secret is never mutated. Use a real run for rotation.
A version’s absolute expiry passed on the builtin adapter. Rotate or set a new value; purge
expired rows with oke vault purge-expired when cleaning storage.
Learn more
- Secrets — contracts and
fx.vault.get - Vault overview — drivers and resolution
- Errors —
VaultSealed·VaultRotateBusy·VaultUnsupported - Environment variables —
OKE_VAULT_MASTER_KEY