Skip to main content

Use a remote signer

With a remote signer, the Prysm validator client never holds private keys. It sends each signing request to Web3Signer, which holds the keys, checks its slashing-protection database, and returns the signature. Use it when keys must live on a separate, hardened host, in a cloud key vault, or in an HSM.

Operating system
Network

Before you begin​

  • A synced beacon node. See the Quickstart.
  • Your validator keystores and their slashing-protection history if they signed anywhere before.
  • A fee recipient.
Validator client with Web3Signer as remote signerThe Prysm validator client gets duties from the beacon node, but holds no private keys. For every signature it sends a signing request over HTTP to Web3Signer, which holds the keys, checks its own slashing protection database in PostgreSQL, and returns the signature.Execution clientBeacon nodePrysmValidator clientPrysm, no private keysWeb3Signerholds the keys, :9000PostgreSQLslashing protectionEngineduties, blockssigncheck and record

Set up Web3Signer​

Set up Web3Signer by following its documentation. It needs Java 25 or later (or Docker), a PostgreSQL database for slashing protection, and one key configuration file per keystore. A typical start command:

bin/web3signer \
--key-store-path=/var/lib/web3signer/keys \
--http-listen-host=127.0.0.1 \
--http-listen-port=9000 \
eth2 \
--network=mainnet \
--slashing-protection-db-url="jdbc:postgresql://localhost/web3signer" \
--slashing-protection-db-username=<DB_USER> \
--slashing-protection-db-password=<DB_PASSWORD>

The network must match Prysm's. This returns 200 when it's up:

curl http://127.0.0.1:9000/upcheck

This lists the keys it holds:

curl http://127.0.0.1:9000/api/v1/eth2/publicKeys

Point the validator client at Web3Signer​

FlagWhat to set
--validators-external-signer-urlWeb3Signer's URL, for example http://127.0.0.1:9000. With it set, Prysm doesn't load a local wallet.
--validators-external-signer-public-keysThe public keys this client validates with, comma-separated. Or a URL that returns them, such as http://127.0.0.1:9000/api/v1/eth2/publicKeys.
--validators-external-signer-key-fileAnd/or: a file with one public key per line. It must exist; Prysm watches it for changes. Keys added through the Keymanager API are saved here.
--validators-external-signer-poll-intervalWith a public keys URL: how often to fetch it again, for example 5m, so keys added to or removed from the signer are picked up without a restart. Off by default.

You can combine these sources: the validator client validates with the keys from all of them. A failed or empty answer from the URL keeps the current keys, so emptying the signer doesn't stop validating.

Add them to your validator client command and restart it:

--validators-external-signer-url=http://127.0.0.1:9000
--validators-external-signer-public-keys=<PUBKEY_1>,<PUBKEY_2>

Replace the placeholders with your 0x-prefixed public keys. Keep Web3Signer on the same host or LAN: every duty waits for a signature. The validator client still keeps its own slashing protection database, so you get two layers of protection.

Move slashing protection history with the keys

If the keys ever signed anywhere else, import that client's slashing protection history into Web3Signer and into Prysm before anything signs, and make sure the old client can never run the keys again. Refer tp tje Move your slashing protection history page.

Check that it works​

At startup, the validator client logs:

No Prysm wallet required for web3signer validation

After a few epochs, it logs this for your keys:

Submitted new attestations

Web3Signer's logs show the signing requests.

All remote signer flags are in the validator reference.