Documentation Index
Fetch the complete documentation index at: https://docs.knoxcall.com/llms.txt
Use this file to discover all available pages before exploring further.
Tenant Master Key
Every KnoxCall tenant has its own master key — a 32-byte symmetric key that wraps every other piece of encrypted material the tenant owns. Secrets, database admin passwords, SSH keys, CA private keys, transit-key versions: all of it is wrapped (KCT1 envelope format) by the tenant master key. This is the load-bearing isolation primitive in KnoxCall. A bug in the control plane, a stolen backup, or a malicious insider hitting the wrong row in the database can’t leak data across tenants — every tenant’s data is wrapped with its key, and you can’t unwrap one tenant’s blob with another tenant’s key. The Vault parallel: this is your unseal key, except per-tenant rather than per-cluster, and unwrapped on demand from a configurable backing store rather than at startup.How it’s stored
The master key isn’t stored in plaintext anywhere. It’s stored wrapped in thetenant_master_keys table, and the wrapping method is configurable per tenant. There are three:
| Wrap method | What wraps the master key | Where the wrapping key lives | Sovereignty |
|---|---|---|---|
global | MASTER_KEY_B64 (the deployment’s env var) | KnoxCall’s process env | Default — same as today, plus per-tenant isolation |
operator_kms | A KMS key in KnoxCall’s cloud account | AWS / GCP / Azure (KnoxCall-operated) | Strong (KMS audit, hardware-backed) |
customer_kms | A KMS key in your cloud account | AWS / GCP / Azure (yours) | Strongest — BYOK |
operator_kms or customer_kms. Without explicit configuration, every tenant uses global — same crypto today, plus per-tenant isolation.
How it’s used
Every per-tenant crypto operation goes throughTenantMasterKeyManager. The flow:
- Caller wants to encrypt or decrypt something for tenant
T. - Manager checks its in-memory cache for
T’s unwrapped master key. - Cache hit → use the cached key.
- Cache miss → load the wrapped row, dispatch to the correct provider (global / AWS / future GCP / future Azure) to unwrap, cache for an hour.
- Concurrent misses for the same tenant deduplicate — N parallel requests after a cache flush trigger one unwrap, not N.
global and operator_kms wrap methods; customer-tunable in [60, 3600] seconds for those methods. Exception: customer_kms (BYOK) tenants have a fixed 5-minute TTL — this is not customer-tunable, and is intentional to bound plaintext exposure if the customer revokes KMS access.
Two practical implications:
- First call after a cache miss is slow (KMS round-trip). Subsequent calls are local.
- You can revoke KnoxCall’s KMS access mid-flight, and the next cache flush makes the tenant immediately unable to unwrap anything. The tenant goes “sealed.”
Sealed state
If unwrapping fails (KMS access denied, KMS unreachable, key disabled), the manager recordstenant_kms_config.sealed_since. For customer_kms (BYOK) tenants, the 5-minute cache TTL means the tenant becomes sealed within 5 minutes of losing KMS access. While sealed:
- New encrypts / decrypts for that tenant fail with a clear error.
- The cache still serves any unexpired entries — the tenant doesn’t go dark instantly, only after the cache TTL.
- The admin UI’s Key Management section shows the sealed state and the last KMS error.
Versioning
Master keys are versioned per tenant. Each rotation creates a new row at the next version, marks the previous oneretired, and sets the new one active.
- Active — current version. New wraps use this. One per tenant.
- Retired — older but still usable for unwrap of legacy ciphertext.
- Revoked — explicitly destroyed. Cryptographic erasure: data wrapped by this version is permanently unreadable.
Cryptographic erasure (the kill switch)
Revoking a tenant master key version is destructive. It’s the right-to-be-forgotten primitive, and it cascades:What never leaves KnoxCall
These never appear in any API response:- The unwrapped master key bytes.
- The wrapped blob (visible in DB rows; not exposed to API).
- The KMS provider’s response payload.
GET /admin/key-management/keys/{version}/material and there never will be.
Audit-trail markers
| Audit action | What it means |
|---|---|
tenant_master_key.create | New master key minted (rotation or initial provision) |
tenant_master_key.kms_config_update | KMS config changed (e.g. switched from global to customer_kms_aws) |
tenant_master_key.revoke | Cryptographic erasure of a version |
tenant_master_key.cache_clear | Operator forced an unwrap on next access (e.g. to verify KMS still works) |
byok.kms.onboard | Customer KMS provider onboarded (BYOK) |
byok.kms.rotate | Tenant master key rotated under BYOK config |
byok.kms.unseal | Tenant unsealed after KMS access restored (seal itself is not logged — only sealed_since is set on the config row) |
What’s shipped today
- ✅ Per-tenant master keys for all three wrap methods (rows, manager, cache, sealed-state).
- ✅
globalwrap (the deployment-wideMASTER_KEY_B64). - ✅ AWS KMS provider (
operator_kms_aws,customer_kms_aws). - ✅ GCP Cloud KMS provider (
operator_kms_gcp,customer_kms_gcp) — Workload Identity Federation. - ✅ Azure Key Vault provider (
operator_kms_azure,customer_kms_azure) — Federated Credentials. - ✅ Cryptographic erasure with audit trail.
- ✅ Tenant KMS dashboard — manage BYOK config at Settings → Security → Tenant KMS.
- ⏳ Bulk rewrap migration: when you rotate a master key, existing data stays wrapped under the retired version. A migration tool to rewrap everything to the new active version is on the roadmap (the manual escape valve is to do it via
rewrap/ per-resource update).
See also
- BYOK & KMS providers →
- Crypto Keys overview → — what
cloud-onlyvsbundledmeans in the context of the master key - Certificate Authority overview → — how the master key protects CA private keys