Skip to main content
This guide rotates a PoA validator’s consensus key using the enterprise/poa module. PoA rotation drops three of the staking rails: there is no fee, no rate limit, and no rotation history. PoA chains have no slashing or evidence handling to protect. The operator can rotate its own key, and the chain admin can rotate any validator’s key. For how rotation works in general, see Key rotation. Commands use simd. Substitute your chain’s binary. Examples use ~/.node for the validator node’s home, ~/.poa-newkey for the scratch home holding the new key, and val for the operator key name.

Prerequisites

  • The chain runs enterprise/poa v1.1 or later.
  • jq and curl. The commands extract the new public key with jq and watch the validator set with curl.
  • A running PoA validator you operate, with the chain’s binary on your PATH and a secondary machine or scratch directory for the new key.
  • The new key’s type is in the chain’s consensus params. See Enable ML-DSA keys.
  • The signer is the validator’s operator, or the chain admin. Any other sender is rejected.

1. Generate a new consensus key

Create the new key on a secondary machine or offline. Do not touch the live node’s priv_validator_key.json yet:
To rotate to a post-quantum key, add --consensus-key-algo ml_dsa_65 to the command above. See Migrate a validator to ML-DSA.
The important output is ~/.poa-newkey/config/priv_validator_key.json, which is the new consensus key. Confirm it and read its public key:

2. Submit the rotation

The command takes the new public key as base64 plus its type, and identifies the validator by operator address. Both derive from earlier steps:
To rotate to a post-quantum key, pass ml_dsa_65 instead of ed25519 in the command above. See Migrate a validator to ML-DSA.
The operator address is a regular account address (cosmos1...), not a cosmosvaloper1... address, so read it with simd keys show val -a. Add standard transaction flags as your setup requires: --chain-id and --keyring-backend if your client config does not supply them, and --fees (or --gas-prices) to meet the chain’s minimum gas price. The transaction re-keys the validator’s state and migrates its accrued fees in the same block. Power, metadata, and the operator address are unchanged. To rotate as the admin instead, keep the command identical and sign with the admin key: --from admin. See Rotate as the admin.

3. Wait for the validator set to switch

The chain’s state swaps immediately, so simd q poa validators shows the new key in the same block the transaction lands. CometBFT applies the actual validator set change two blocks later, and only that switch governs when the node must sign with the new key. Until it happens, CometBFT still expects the old key, so keep the live node running untouched. Watch the CometBFT validator set until the new consensus key appears and the old one is gone:
The value should match the key from simd comet show-validator --home ~/.poa-newkey. Confirm the module re-keyed with simd q poa validators.
Do not swap the node’s key before the set switches (wait at least 2 blocks). Swapping early makes the node sign with a key CometBFT does not yet expect, and the validator misses blocks. Running two nodes that hold this validator’s signing keys at the same time risks a double sign. Never run more than one active signer for the validator.

4. Swap the node’s key

Once the new consensus address is in the set, stop the node, replace its key, and restart:
Confirm the node signs under the new consensus address after the restart. If you run a redundant standby, make a single switch. Keep the old-key node signing until the set updates, stop it fully, and only then let the new-key node start signing.
A validator with power 0 is outside the active set. Its rotation emits no validator set update, so there is no transition to time. Swap the node’s key first, then have the admin grant power.

Rotate as the admin

The admin rotates any validator’s key with the same command, signed by the admin key.
The admin override changes on-chain state only. Whoever runs the node must still swap priv_validator_key.json with the timing in steps 3 and 4. Otherwise the validator goes dark until its node signs with the new key. Coordinate the node-side swap with the operator before submitting, unless the goal is to cut off a compromised key.

Verify

The rotation emits an event with the operator address, the old consensus address, and the new one. Confirm the set carries the new consensus address and the node is signing:

What can go wrong

  • The transaction is rejected as unauthorized: the signer is neither the validator’s operator nor the admin.
  • The transaction is rejected for the key itself: the new key equals the current one, is already used by another validator, or its type is not in the chain’s consensus params.
  • The validator misses blocks right after the swap: the node’s key was replaced before the set switched. Restore the old key, wait for the set, then swap again.
  • The validator goes dark after an admin rotation: the node still holds the old key. Swap priv_validator_key.json and restart.

Next steps