MAQPNADocs

Key management#

Every key that can sign an identity, a checkpoint or a licence, or decrypt stored data, is customer-held. MAQPNA never holds, escrows or phones home with a key. Keys are referenced either by a file path mounted from a Kubernetes Secret, or by a key URI resolved by pkg/keys.

Key URIs#

URI Algorithm Status
file:///abs/path Ed25519, PKCS#8 PEM Implemented. The file is refused if it is readable by others or writable by the group (0600 or 0400; 0440 tolerated for Secret volumes with fsGroup)
pkcs11:token=…;object=…?module-path=…&pin-source=file:… (RFC 7512) — Planned. The URI is parsed and validated (an inline pin-value is rejected), but signing returns ErrNotConfigured until a backend is registered with keys.RegisterPKCS11. No backend is registered in the released binaries
kms://<provider>/<key-id> — Planned. Parsed and validated; signing returns ErrNotConfigured until a backend is registered with keys.RegisterKMS. None is registered in the released binaries
azurekv://<vault>/<key>[/<version>] ES256 (ECDSA P-256 on a Key Vault HSM key) Implemented for licence signing only (ParseJWSKeyURI, used by maqpna license issue). The key never leaves the HSM; the signer calls the Key Vault REST API (7.4) without an SDK

Azure Key Vault authentication, first that applies: AZURE_TENANT_ID + AZURE_CLIENT_ID + AZURE_CLIENT_SECRET (client credentials); AZURE_TENANT_ID + AZURE_CLIENT_ID + AZURE_FEDERATED_TOKEN_FILE (workload identity); then az account get-access-token. For a sovereign cloud, give the vault's full host name.

Key IDs are RFC 7638 JWK thumbprints everywhere, so a kid in a token, checkpoint or card names exactly one public key.

Sealers encrypt data at rest. keys.ParseSealerURI accepts file:/// (a 32-byte AES-256 key) and, when registered, pkcs11: or kms://. AESGCMSealer uses AES-256-GCM with a random 96-bit nonce and key ID aes256gcm:<16 hex>; a Keyring holds one primary key and any retired keys, so data sealed under an old key still opens after rotation.

Which component holds which key#

flowchart LR
    subgraph IDN["maqpna-identity"]
      K1["identity signing key<br/>Ed25519, -key file"]
      K2["tenant signing keys<br/>Secret maqpna-tenant-keys"]
    end
    subgraph GWN["maqpna-gateway"]
      K3["audit checkpoint key<br/>auditCheckpointKey URI"]
      K4["HMAC checkpoint key<br/>MAQPNA_AUDIT_HMAC_KEY"]
      K5["token vault key<br/>tokenVault.keyURI sealer"]
      K6["capture key<br/>captureArgs key sealer"]
      K7["Txn-Token key<br/>txnTokens.signingKey"]
      K8["A2A card key<br/>a2a.cardSigningKey"]
    end
    subgraph ATN["maqpna-attest"]
      K9["response signing key<br/>-response-signing-key file"]
    end
    subgraph CLI["maqpna CLI, offline"]
      K10["licence signing key<br/>file or azurekv URI"]
      K11["usage report key<br/>file URI"]
    end
    K1 -- "JWKS /.well-known/jwks.json" --> V1["gateway, broker verify tokens"]
    K3 -- "JWKS /v1/audit/jwks" --> V2["auditors verify checkpoints<br/>and erasure certificates"]
    K8 -- "JWKS /a2a/jwks.json" --> V3["remote A2A peers"]
    K9 -- "public key to the operator" --> V4["attest agents in the VM"]
Key Held by Signs or protects Published as
Identity signing key Identity broker (-key, Ed25519 PEM; identity.key.mode generate, helm or existingSecret) Session, exchanged and delegated tokens GET /.well-known/jwks.json
Tenant signing keys Identity broker (Secret maqpna-tenant-keys, <tenant>.pem, generated by the operator or copied from signingKeySecretRef) Tokens for a tenant's namespaces, in its trust domain Same JWKS
Audit checkpoint key Gateway (auditCheckpointKey, key URI) Ed25519 JWS ledger checkpoints and memory erasure certificates GET /v1/audit/jwks
HMAC checkpoint key Gateway (MAQPNA_AUDIT_HMAC_KEY, from the chart's Secret) HMAC checkpoints in <ledger>.checkpoints Not published (symmetric)
Token vault key Gateway (tokenVault.keyURI, sealer) Users' OAuth tokens, bound to realm, user, provider and field —
Capture key Gateway (captureArgs, sealer) DLP-redacted argument captures for replay —
Txn-Token key Gateway (txnTokens.signingKey, key URI) Transaction tokens to upstreams; give the broker its public key with -txn-token-keys —
A2A card key Gateway (a2a.cardSigningKey, key URI) Signed agent cards GET /a2a/jwks.json
Attest response key Attestation service (-response-signing-key, Ed25519 PEM) Attest and renew responses, so an on-path host cannot substitute a sealed token Public key to the operator (--attest-response-pubkey-file)
Licence signing key Vendor or service provider, offline (maqpna license issue) Licence JWS (EdDSA or ES256) Embedded trusted key in the binaries
Usage report key Service provider, offline (maqpna usage report) Signed usage reports (EdDSA JWS) Given to the party that verifies
MAQPNA_BROKER_TOKEN Operator, broker, attestation service Bearer for mint and release calls (not a signing key) —

Rotation#

maqpna keys rotate rotates a key without downtime. Every key lives in a Secret mounted as a directory, and the servers also load sibling files next to the active key, so a rotation edits the Secret and restarts the Deployment that reads it:

Kind Sibling file Effect
identity previous-<kid>.pem Published in the broker JWKS, so tokens signed with the old key verify until they expire
audit-checkpoint retired-<kid>.pub.pem Kept in /v1/audit/jwks, so old checkpoints stay verifiable
vault retired-<id> Still opens records sealed under it; --drop-old removes it after re-sealing
attest pub.pem with two keys Attest agents trust the old and the new response key during the grace period
  1. maqpna keys rotate identity generates a new key and keeps the old one as previous-<kid>.pem.
  2. It restarts the broker, then waits for gateways to pick up the new JWKS (--jwks-wait, default 5m30s) or restarts them (--restart-gateway).
  3. After the grace period (--grace, default 1 hour, longer than the maximum token TTL), --finish removes the old key.
  4. Progress is recorded in the Secret annotation maqpna.com/key-rotation (kind, phase prepared/swapped/done, oldKid, newKid), so an interrupted rotation can be resumed.

What you see#

$ maqpna keys rotate identity -n maqpna-system --dry-run

prints the plan without changing anything. The usage lines (from cmd/maqpna/keys.go):

maqpna keys rotate identity         [-n NS] [--grace 1h] [--jwks-wait 5m30s | --restart-gateway] [--no-finish | --finish [--force]]
maqpna keys rotate audit-checkpoint [-n NS] [--secret NAME]
maqpna keys rotate vault            [-n NS] [--secret NAME] [--drop-old]
maqpna keys rotate attest           [-n NS] --secret NAME [--grace 24h] [--no-finish | --finish [--force]]

See maqpna keys rotate, maqpna keygen and maqpna license issue.