KMS Backends
Pick a signing backend per Org via a single trait. The KMS layer exposes the KmsClient async trait — four production backends (AWS, GCP, Vault, PKCS#11) plus an in-memory backend for tests — and a KmsKeyProvider adapter that fronts the JWT signer.
URI scheme
Backend selection is declarative — a single tagged enum (KmsConfig) round-trips through YAML or environment, and build_client(&cfg) returns a boxed trait object. The CMEK row stores the same selection as a URI string for human readability.
| Scheme | Backend | Cargo feature | Identifier shape |
|---|---|---|---|
| aws-kms: | AWS KMS (CMK or alias) | aws-kms | arn:aws:kms:us-east-1:…:key/… |
| gcp-kms: | GCP Cloud KMS | gcp-kms | projects/p/locations/l/keyRings/r/cryptoKeys/k/cryptoKeyVersions/1 |
| vault:// | HashiCorp Vault Transit | vault | vault://vault.internal:8200/tetrapus-jwt |
| pkcs11:// | PKCS#11 HSM (Yubikey, CloudHSM, Luna, SoftHSM) | pkcs11 | pkcs11:///usr/lib/softhsm2.so?slot=0&label=jwt |
Status
AWS KMS, GCP Cloud KMS, and Vault Transit are wired and exercised by integration tests. The PKCS#11 client is a stub — the cryptoki module loads and the URI scheme is reserved, but every signing call returns KmsError::Other("pkcs11 sign_es256 not yet implemented"). YubiHSM / CloudHSM bring-up with SoftHSM-in-CI is a tracked follow-up.
Layering
The KMS layer fronts the existing KeyProvider trait that JwtSigner already consumes. Nothing on the auth path knows whether the actual ECDSA operation happens in-process, against an SDK over HTTPS, or inside an HSM.
Key rotation
Rotation is a two-state lifecycle: the new key becomes active and starts signing new tokens; the previous key is moved to expired but its public half stays in JWKS for the grace period so in-flight access tokens still verify. list_active_keys() includes both states by contract.
cmek_bindings table
A KMS selection is stored per-Org in cmek_bindings. The same row holds the URI, the provider-native key id, and the scope the binding applies to (jwt, data, audit). See CMEK / BYOK for the scope semantics.
| Column | Type | Notes |
|---|---|---|
| org_id | UUID | FK orgs(id), cascade |
| kms_uri | TEXT | Scheme + endpoint |
| key_arn_or_id | TEXT | Provider-native id |
| scope | TEXT | jwt | data | audit |
| active | BOOLEAN | DEFAULT TRUE |
| rotated_at | TIMESTAMPTZ | NULL until first rotation |
Related
- – CMEK / BYOK — per-Org binding semantics
- – FIPS 140-3 — backend swap to aws-lc-rs
- – Audit Attestation — KMS-signed Merkle roots
- – Back to Enterprise
Questions?
Reach out for help with integration, deployment, or custom domain codecs.