Signer key custody and KMS
The verifier signs message hashes with one key. You choose where that key lives with keystoreBackend:
postgres(the default): the cell generates the key on first boot and keeps it in Postgres.kms: the key lives in your cloud KMS, never leaves it, and the cell signs through it.
On a managed cloud, KMS is strongly recommended. The Postgres keystore is supported, but it puts the key in your database and its backups.
KMS support by cloud
- GKE: GCP KMS is supported and recommended.
- EKS: AWS KMS is supported and recommended.
- AKS: an Azure Key Vault signer is in progress; use the Postgres keystore for now.
The Postgres keystore
The cell generates the signing key on first boot and stores it in the
bootstrapper database. Your Postgres
backups therefore contain the signing key, so encrypt them and restrict who can read them. Leave
signer_address: auto so the verifier accepts the key it generated. There is nothing to derive before deploy:
once the cell is running, read the signer address from the verifier log and register it on-chain (the second
pass in Deploy your first cell).
Set the KMS signer address explicitly
With KMS the key exists before the cell does, so you compute its address first and set signer_address to it
rather than auto. The verifier then checks on boot that the KMS key matches that address and refuses to start
if it does not, which catches a wrong key ID before the cell signs anything. With auto the verifier signs with
whatever key the ID points to.
The signing key must be secp256k1: an EC_SIGN_SECP256K1_SHA256 key on GCP KMS, or an ECC_SECG_P256K1 signing
key on AWS KMS. Derive its address in three steps.
-
Fetch the public key. GCP KMS writes it as PEM directly:
gcloud kms keys versions get-public-key 1 \ --key <key> --keyring <keyring> --location <location> \ --public-key-format=pem --output-file public.pemAWS KMS returns base64-encoded DER, so decode it and convert to PEM:
aws kms get-public-key --key-id <key-id-or-arn> \ --query PublicKey --output text | base64 --decode > public.der openssl pkey -pubin -inform DER -in public.der -outform PEM -out public.pem -
Derive the address from
public.pem: take the X and Y coordinates of the uncompressed point (32 bytes each, without the0x04prefix byte), hash the 64 bytes with Keccak-256, and keep the last 20. The off-chain kit's Signer Address documentation has a short Python script that does exactly this and prints the resulting address. -
Set
signer_addressto that address, then complete the on-chain signature config in step 1 of the deploy. KMS needs no second pass.
KMS permissions
Scope every grant to the one signing key, and give the pod its access through workload identity so nothing holds a long-lived credential.
- GKE: the pod needs
roles/cloudkms.signerVerifierandroles/cloudkms.vieweron the key.signerVerifieralone is not enough; withoutviewerthe verifier crash-loops oncloudkms.cryptoKeyVersions.get. The operator who derives the address needs onlyroles/cloudkms.publicKeyViewer(viewerdoes not include public-key reads), and should never holdsignerVerifier. See the Cloud KMS permissions and roles reference and Grant a role on a resource. - EKS: the pod needs
kms:Sign,kms:GetPublicKey, andkms:DescribeKey, restricted to the key ARN. Do not grantkms:ListKeysor a wildcard resource. The operator who derives the address needs onlykms:GetPublicKey. See the AWS KMS permissions reference and key policies.