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.

  1. 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.pem
    

    AWS 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
    
  2. Derive the address from public.pem: take the X and Y coordinates of the uncompressed point (32 bytes each, without the 0x04 prefix 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.

  3. Set signer_address to 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.signerVerifier and roles/cloudkms.viewer on the key. signerVerifier alone is not enough; without viewer the verifier crash-loops on cloudkms.cryptoKeyVersions.get. The operator who derives the address needs only roles/cloudkms.publicKeyViewer (viewer does not include public-key reads), and should never hold signerVerifier. See the Cloud KMS permissions and roles reference and Grant a role on a resource.
  • EKS: the pod needs kms:Sign, kms:GetPublicKey, and kms:DescribeKey, restricted to the key ARN. Do not grant kms:ListKeys or a wildcard resource. The operator who derives the address needs only kms:GetPublicKey. See the AWS KMS permissions reference and key policies.

What's next

Get the latest Chainlink content straight to your inbox.