> ## Documentation Index
> Fetch the complete documentation index at: https://cosmos-docs-security-release.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate a validator to ML-DSA

> Move a validator's consensus key to the post-quantum ml_dsa_65 algorithm through an ordinary key rotation.

Migrating a validator to post-quantum signing is an ordinary key rotation with an ML-DSA target key. This guide adds the ML-DSA-specific steps around the standard procedure in [Rotate a consensus key, Staking](/sdk/next/keys/rotate-validator-key). For more information on ML-DSA, see [Post-quantum keys](/sdk/next/keys/post-quantum-keys).

## Prerequisites

* All prerequisites of the rotation procedure, including [jq](https://jqlang.org/) and [curl](https://curl.se/): no rotation in the current unbonding period, and fee funds on the operator account. See [Rotate a consensus key, Staking](/sdk/next/keys/rotate-validator-key).

## 1. Confirm the chain allows ML-DSA

Validator key types are a consensus parameter. Check that `ml_dsa_65` is in the list:

```shell theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
simd query consensus params
```

If `validator.pub_key_types` does not include `ml_dsa_65`, the rotation is rejected. To add the type, see [Enable ML-DSA keys](/sdk/next/keys/enable-ml-dsa-keys).

## 2. Rotate to an ML-DSA key

### On a staking chain

Follow [Rotate a consensus key, Staking](/sdk/next/keys/rotate-validator-key), replacing the `simd init` command in its step 1 with one that adds the `--consensus-key-algo` flag:

```shell theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
simd init rotation-node --chain-id my-chain-1 --consensus-key-algo ml_dsa_65 --home ~/.rotation-node
```

Everything else runs as written. The guide's rotation command derives the public key with `simd comet show-validator` on the second node's home, which now prints the ML-DSA key, so the rotation message carries it automatically.

### On a PoA chain

Follow [Rotate a consensus key, PoA](/sdk/next/keys/rotate-validator-key-poa) with two changes. Replace the `simd init` command in its step 1 with:

```shell theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
simd init poa-newkey --chain-id my-chain-1 --consensus-key-algo ml_dsa_65 --home ~/.poa-newkey
```

And pass `ml_dsa_65` instead of `ed25519` as the key type when submitting:

```shell theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
simd tx poa rotate-cons-pub-key "$(simd comet show-validator --home ~/.poa-newkey | jq -r .key)" ml_dsa_65 --operator-address "$(simd keys show val -a)" --from val
```

The cutover timing is unchanged: keep the node on the old key until the validator set switches, then swap the key file in place, exactly as the guide's steps 3 and 4 describe.

### On a remote signer

If the validator's consensus key lives in Cosmos-KMS rather than a local file, the second node gets its own signer and the public key derivation differs. See [Rotate a consensus key held in Cosmos-KMS](/sdk/next/kms/rotate-key-remote-signer).

{/* TODO(MATT): the PoA path has not run with a real ML-DSA key; test it against the #26590 branch with simd from #26604. */}

## 3. Verify

Check the key type in the validator set:

```shell theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
curl -s localhost:26657/validators | jq -r '.result.validators[].pub_key.type'
```

A migrated validator reports `cometbft/PubKeyMlDsa65` instead of `tendermint/PubKeyEd25519`. The chain's consensus is post-quantum secure once validators holding at least two thirds of voting power report a post-quantum type. For more information, see [Post-quantum keys](/sdk/next/keys/post-quantum-keys).

## What can go wrong

* The rotation is rejected for an unsupported key type: the chain does not list `ml_dsa_65` yet. See [Enable ML-DSA keys](/sdk/next/keys/enable-ml-dsa-keys).
* The rotation is rejected with a rotation limit error: a rotation already happened this unbonding period. Wait out the window.
* Anything else follows the standard rotation failure modes. See [Rotate a consensus key, Staking](/sdk/next/keys/rotate-validator-key).

## Next steps

* Track migration progress across the validator set with the allowed-vs-in-use commands. See [Enable ML-DSA keys](/sdk/next/keys/enable-ml-dsa-keys).
* Understand the storage and bandwidth costs the chain takes on as the set migrates. See [Post-quantum keys](/sdk/next/keys/post-quantum-keys).
