Envelope encryption (DEK/KEK)
Envelope encryption wraps a data-encryption key (DEK) with a key-encryption key (KEK) that never leaves a KMS, so only the wrapped DEK is stored and the raw key material is never persisted. Caisson's KmsKeyProvider generates a DEK per tenant, stores just its KEK-wrapped form, and unwraps the tenant's historical versions through the KMS port when a request context binds.
In code
const provider = new KmsKeyProvider(
kms,
new PgWrappedKeyStore(tx),
{ abortSignal },
);
await provider.ensureProvisioned(accountId);
return withKmsFieldCryptoContext(
provider,
accountId,
async (ctx) => sealField(ctx, "patient.ssn", plaintext),
{ abortSignal },
);How it holds
Three shipped cloud backends, one KMS port
KmsClient exposes just three methods (generateDataKey, decryptDataKey, scheduleKeyDeletion). AWS KMS, GCP KMS, and Azure Key Vault drivers ship today behind that port; Caisson's hosted production site wires Azure through the default Azure credential chain with required purge protection. The field-crypto column and envelope format never know which backend is live.
Only the wrapped DEK ever touches storage
generateDataKey returns the plaintext DEK and its KEK-wrapped form together; provisioning persists only wrappedKey to the WrappedKeyStore and zeroizes the generated plaintext in finally. Request binding unwraps historical DEKs into a disposable context that zeroizes every source and working buffer on exit; plaintext is never logged or written to disk.
Rotation bumps a version, it never re-encrypts
provision() increments the tenant's key version and wraps a fresh DEK under it; keyFor(tenantId, v) must still answer any past version forever, because the version travels inside the self-describing envelope, not provider state. No bulk re-encrypt job runs on rotation.
A per-scope KEK makes crypto-shred selective
Every KMS operation is scoped by a keyId (a tenant or subject id); cryptoShred validates that the destructive scope matches the recorded tenant or subject and KmsKeyProvider refuses an unprovisioned scope before the cloud call. Deleting that KEK leaves every other tenant's wrapped DEKs and ciphertext unaffected.