Bring-your-own KMS key

Register a customer-managed Key Encryption Key (KEK) so every blob your tenant uploads is wrapped against a key in your own AWS / Azure / GCP account.

Updated 2026-04-26

Kodori uses envelope encryption for every blob: each upload gets a fresh AES-256-GCM Data Encryption Key (DEK), and that DEK is wrapped against your tenant's Key Encryption Key (KEK). The plaintext DEK lives only in memory during encrypt / decrypt; revoking your KEK instantly makes every blob unreadable, regardless of where the encrypted bytes physically reside.

Open /encryption (owner / admin only) to register a KMS-managed KEK. Supported providers:

- **AWS KMS** — accepts a Customer-Managed Key ARN, region, and an optional cross-account role ARN if Kodori needs to sts:AssumeRole into your account. - **Azure Key Vault** — accepts the key URL (https://<vault>.vault.azure.net/keys/<name>/<version>). - **GCP KMS** — accepts the CryptoKey resource path (projects/.../locations/.../keyRings/.../cryptoKeys/...).

Until a tenant registers a real KMS, the deployment uses an envelope key derived from AUTH_SECRET — kodori-managed. Every blob is still envelope-encrypted; you don't have direct revocation control. Registering a real KMS is the upgrade path.

Rotation is additive: registering a new key auto-retires the prior active. Existing wrapped DEKs continue to unwrap with the retired key; new uploads wrap against the new active. There's no big-bang re-encryption.

**Lifecycle audit.** Every change to your key custody emits a hash-chained audit event so SOC 2 / 21 CFR Part 11 evidence is captured automatically. Three event types: `tenant-kms.registered` (first-time BYO-key adoption), `tenant-kms.rotated` (carries from/to keyIdSuffix + provider in the payload — surfaces on /audit as an inline diff chip via the D196 badge), `tenant-kms.disabled` (keyRowId + previousStatus). Filter /audit to types=tenant-kms.* for "every change to key custody in YYYY." Full keyIds (ARNs / Key Vault URLs / GCP CryptoKey paths) stay in tenant_kms_keys — only the last 12 chars surface in the audit payload to avoid leaking sensitive identifiers in /audit.

**Re-wrap pipeline (planned).** Today rotation is additive — the retired key stays load-bearing because existing wrapped DEKs reference it. The re-wrap pipeline (live AWS / Azure / GCP KMS SDK calls + walk every wrapped DEK to re-encrypt against the new KEK so the retired key can be safely deleted from your KMS) is documented at `docs/plans/tenant-key-rewrap-plan.md` for delivery as part of the SOC 2 Type I engagement. Contact hello@kumokodo.ai to enable cloud KMS for your tenant.