CMEK / BYOK

Customer-managed encryption keys (CMEK), also called "bring your own key" (BYOK), let you keep root key material in your cloud KMS or HSM. Tetrapus never holds the private half. The cmek_bindings table stores one row per Org per scope; rotation, revocation, and audit happen entirely in the customer's KMS.

Three scopes

A CMEK binding has a scope field with three possible values. An Org typically has one binding per scope, all pointing at the same KMS but using distinct keys (separation of duties — a stolen JWT key shouldn't unlock at-rest data).

scope What it protects Algorithm Operation
jwt Access & refresh tokens ES256 sign / public key in JWKS
data Row-level PII at rest (envelope-encrypted) AES-256-GCM (DEK), wrap via KMS wrap / unwrap data keys
audit Attestation root signatures ES256 sign Merkle roots

cmek_bindings table

SQL Schema
CREATE TABLE cmek_bindings (
    id            UUID PRIMARY KEY,
    org_id        UUID NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
    kms_uri       TEXT NOT NULL,         -- aws-kms: / gcp-kms: / vault:// / pkcs11://
    key_arn_or_id TEXT NOT NULL,         -- provider-native id
    scope         TEXT NOT NULL CHECK (scope IN ('jwt','data','audit')),
    active        BOOLEAN NOT NULL DEFAULT TRUE,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    rotated_at    TIMESTAMPTZ
);
CREATE INDEX cmek_bindings_org_scope_idx ON cmek_bindings(org_id, scope);

Provisioning flow

graph LR CUST["Customer KMS admin"] -->|create CMK alias/tetrapus-jwt| KMS["Customer's AWS KMS"] CUST -->|grant Tetrapus IAM role kms:Sign + kms:GetPublicKey| KMS OPS["Tetrapus operator"] -->|tetrapus-admin keys bind --scope jwt --uri aws-kms:arn:…| API["/admin/keys"] API -->|fetch public key, persist row| DB["cmek_bindings"] DB --> JWS["JwtSigner uses KmsKeyProvider"] JWS -->|sign_es256| KMS

Rotation flow

Rotation is initiated in the customer's KMS — Tetrapus never owns the rotation schedule. A single command then points the binding at the new key id; the previous binding flips to active = false but its public key stays in the JWKS for the configured grace period (default 7 days) so in-flight tokens still verify.

Bash Rotate via admin CLI
tetrapus-admin keys rotate \
  --org-id  3f2a…  \
  --scope   jwt    \
  --new-key-id "arn:aws:kms:us-east-1:…:key/new-version"
# old binding: active=false, rotated_at=now()
# new binding: active=true, kid=jwk_jwt_2026q2_b
# JWKS now serves both public keys for 7d grace

Status

All three scopes are wired end-to-end against AWS KMS, GCP KMS, and Vault Transit. PKCS#11-backed CMEK is reserved (URIs validated, table populated) but blocked on the PKCS#11 client work — see the KMS Status callout.

Revocation

Because Tetrapus does not hold the private key, "revoke" means "remove our IAM grant in the customer KMS". Within seconds, the next signing call returns KmsError::Auth and the data plane begins rejecting new logins for that Org. Existing tokens continue to verify until they expire — operators who need an immediate cut-over should also bump the JWKS grace window down to zero on the affected scope.

Related

Questions?

Reach out for help with integration, deployment, or custom domain codecs.