Run an Aeva Mainnet validator
From fresh Ubuntu machines to a validator of Aeva Mainnet (aeva-1): a
sentry that talks to the network, a validator that talks only to its
sentry, and one transaction from your operator key that creates the
validator. The parameters below were read from aeva-1 on
27 September 2026.
At launch, aeva-1 has four validators run by the project. Anyone can add
a validator by bonding AEVA: see what you need
first.
| Network | Aeva Mainnet |
|---|---|
| Chain id | aeva-1 (EVM chain id 2383) |
| Release | v1.0.1-beta.1 at releases.aevachain.com, signed with the Aeva release key 1B6F54CB7B98D7EC |
| Genesis | genesis.aevachain.app/aeva-1/genesis.json, SHA-256 d7d491b078bcb9d16f888077c95404c8f2ba35b4354a6bf853a80b9f2eaf11fa |
| Seed | seed-1.mainnet.aevachain.com:26656 |
| Public RPC / REST | https://rpc.aevachain.app, https://api.aevachain.app |
| Explorer | aevascan.com/validators |
| Staking app | aevachain.app/stake |
Before you start: AEVA to bond
A validator bonds its own AEVA. On aeva-1, AEVA reaches an outside account
one way: the bridge from Robinhood Chain, where $AEVA (the ERC-20) lives.
The bridge is not open to holders yet. Governance proposal 1 (passed
27 September 2026) activated its routes on aeva-1 in owner-test mode: on
Robinhood Chain the $AEVA route lets only the operators' test wallets
through (the AevaBridgeGuard contract) until the project's Safe opens it to
everyone. Until then you can set up and sync your sentry and validator
(steps 1–7), but you cannot bond. Nothing on this page, and no application,
grants AEVA. aevachain.app/validators
reads the bridge's state from both chains; to check it yourself:
# anywhere: the routes on aeva-1, then the guard on Robinhood Chain (…01 = open to everyone)
curl -s https://api.aevachain.app/aeva/bridge/v1/routes | jq -r '.routes[] | "\(.route_id) \(.status)"'
curl -s -X POST -H 'content-type: application/json' https://rpc.mainnet.chain.robinhood.com \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"0x566a2c2de79414545FEe55B51445ad1d1045dD52","data":"0xfcfff16f"},"latest"]}' | jq -r .result
How much to bond: at least 1 AEVA (voting power counts whole AEVA), plus some AEVA left liquid for fees (a create-validator costs well under 0.1 AEVA at the fee floor). The 50 validators with the most bonded AEVA sign blocks.
What validators earn: 40 % of every fee goes to the stakers (60 % is burned), less the 2 % community tax, and the chain's two reward pools pay the stakers on the schedules governance proposal 1 switched on: the validator reward pool from its passing, the staking-incentives pool from block 10,161 (the table has both). Both are split by stake, and each validator keeps its commission on its delegators' share. What that comes to per bonded AEVA falls as more AEVA bonds; aevachain.app/validators shows it live. Governance can change the schedules.
Chain parameters
Read from aeva-1 (/cosmos/staking/v1beta1/params,
/cosmos/slashing/v1beta1/params and the modules named below). Governance
can change them; the slashing section says what they mean
in practice.
| Rule | Parameter | On aeva-1 | What it means for you |
|---|---|---|---|
| Active set | staking.max_validators |
50 |
The 50 validators with the most bonded AEVA sign blocks and earn; the others wait, unbonded. |
| Unbonding | staking.unbonding_time |
1814400s |
21 days: unbonded AEVA stays locked, earns nothing and can still be slashed for a fault committed before. |
| Minimum commission | staking.min_commission_rate |
0.05 |
5 %: the chain refuses a validator with a lower commission. |
| Signing window | slashing.signed_blocks_window |
30000 |
Downtime is counted over the last 30,000 blocks. |
| Minimum signed | slashing.min_signed_per_window |
0.05 |
5 %: a validator that misses more than 28,500 of the last 30,000 blocks is jailed. |
| Downtime jail | slashing.downtime_jail_duration |
600s |
10 minutes at least; an unjail transaction then brings the validator back. |
| Downtime slash | slashing.slash_fraction_downtime |
0.0001 |
0.01 % of the validator's stake, its delegators' included, when it is jailed. |
| Double-sign slash | slashing.slash_fraction_double_sign |
0.05 |
5 % of the stake, delegators' included, and the validator is tombstoned: it can never sign again. |
| Evidence age | consensus.evidence.max_age_duration |
1814400s |
A double sign can be proven and punished for at least 21 days after it happened. |
| Consensus key | consensus.validator.pub_key_types |
ed25519 |
The validator's consensus key is an ed25519 key (tmkms and Horcrux sign it). |
| Fee floor | feemarket.min_gas_price |
25000000000 |
25 gwei: every transaction pays at least 25,000,000,000 aaeva per unit of gas. |
| Fee split | rewards.fee_burn_bps |
6000 |
60 % of every fee is burned; 40 % goes to the stakers. |
| Validator reward pool | rewards.validator_pool.amount_per_epoch |
520833333333333333 |
0.52 AEVA every block to the stakers: 12.5 million AEVA per 24,000,000 blocks (x/rewards' year). |
| Staking-incentives pool | rewards.staking_pool.amount_per_epoch |
11076833333333333333333 |
11,076.83 AEVA every 66,461 blocks from block 10,161, to the stakers: 4 million AEVA per 24,000,000 blocks. |
| Community tax | distribution.community_tax |
0.02 |
2 % of the stakers' share goes to the community pool. |
1. Machines
| machine | count | size | network |
|---|---|---|---|
| validator | 1 | 8 cores, 32 GB RAM, 1 TB NVMe | no public ports; peers only with its sentries, over a private network |
| sentry | 2, in two regions (one is enough to start) | 4 cores, 16 GB RAM, 500 GB NVMe | public TCP 26656 |
| operator machine | 1 | any | holds the operator key; never the validator |
Ubuntu 22.04 or 24.04, amd64 or arm64. Put the validator and its sentries on a private network (a VPC or WireGuard) and keep the clocks synchronised (chrony against at least two sources). Every command below is run by a user with sudo; the first line of each block says on which machine.
2. The network, in your shell
Set these in the shell you use on each machine; every later step reads them.
# on every machine
export CHAIN_ID=aeva-1
export EVM_CHAIN_ID=2383
export AEVA_RELEASE=v1.0.1-beta.1
export GENESIS_URL=https://genesis.aevachain.app/aeva-1/genesis.json
export GENESIS_SHA256=d7d491b078bcb9d16f888077c95404c8f2ba35b4354a6bf853a80b9f2eaf11fa
export SEED=d08511f3a8ee9a243c04c5a95748958399ca1457@seed-1.mainnet.aevachain.com:26656
export NODE=https://rpc.aevachain.app:443
3. Install aevad, checking its signature
The release's SHA256SUMS is signed with the Aeva release key
1B6F54CB7B98D7EC; the command pins that key, so a folder with a different
key or a changed file fails here and nothing is installed.
# on every machine
sudo apt-get update && sudo apt-get install -y curl jq minisign
ARCH=$(dpkg --print-architecture)
cd "$(mktemp -d)"
REL=https://releases.aevachain.com/$AEVA_RELEASE
curl -fsSLO "$REL/aevad-$AEVA_RELEASE-linux-$ARCH" -O "$REL/SHA256SUMS" -O "$REL/SHA256SUMS.minisig"
minisign -Vm SHA256SUMS -P RWTs15h7y1RvG7qjUf4pzjpT4HVqmexAoScwZ7AhEMaxVKDMKhRdvBb+
grep " aevad-$AEVA_RELEASE-linux-$ARCH\$" SHA256SUMS | sha256sum -c -
sudo install -m 0755 "aevad-$AEVA_RELEASE-linux-$ARCH" /usr/local/bin/aevad
aevad version
aevad version prints v1.0.1-beta.1. PROVENANCE.md in the release folder
says how to rebuild the binary byte for byte from its tag.
4. A service user, a home and the genesis
# on the sentry and the validator
sudo useradd --system --create-home --home-dir /var/lib/aeva --shell /usr/sbin/nologin aeva
sudo -u aeva aevad init <your-moniker> --chain-id "$CHAIN_ID" --home /var/lib/aeva
cd "$(mktemp -d)"
curl -fsSL "$GENESIS_URL" -o genesis.json
echo "$GENESIS_SHA256 genesis.json" | sha256sum -c -
sudo install -o aeva -g aeva -m 0644 genesis.json /var/lib/aeva/config/genesis.json
sudo -u aeva aevad comet show-node-id --home /var/lib/aeva
The last line prints the machine's node id: note the sentry's and the
validator's, the next step uses both. sha256sum -c must say genesis.json: OK.
5. Configure
Settings every aeva-1 node runs with: the app mempool (the node refuses to
start with CometBFT's default), 1 s between blocks, the fee floor, the EVM
chain id (the binary refuses any other for aeva-1), pebbledb, and
Prometheus metrics on port 26660. REST, gRPC and JSON-RPC stay off: a
validator and its sentries serve nobody.
# on the sentry and the validator
C=/var/lib/aeva/config/config.toml
A=/var/lib/aeva/config/app.toml
sudo sed -i \
-e 's|^timeout_commit = .*|timeout_commit = "1s"|' \
-e '/^\[mempool\]/,/^\[/ s|^type = .*|type = "app"|' \
-e 's|^db_backend = .*|db_backend = "pebbledb"|' \
-e '/^\[tx_index\]/,/^\[/ s|^indexer = .*|indexer = "null"|' \
-e '/^\[instrumentation\]/,/^\[/ s|^prometheus = .*|prometheus = true|' \
"$C"
sudo sed -i \
-e 's|^minimum-gas-prices = .*|minimum-gas-prices = "25000000000aaeva"|' \
-e 's|^iavl-cache-size = .*|iavl-cache-size = 100000|' \
-e 's|^app-db-backend = .*|app-db-backend = "pebbledb"|' \
-e "/^\[evm\]/,/^\[/ s|^evm-chain-id = .*|evm-chain-id = $EVM_CHAIN_ID|" \
-e '/^\[api\]/,/^\[/ s|^enable = .*|enable = false|' \
-e '/^\[grpc\]/,/^\[/ s|^enable = .*|enable = false|' \
-e '/^\[json-rpc\]/,/^\[/ s|^enable = .*|enable = false|' \
"$A"
grep -E '^(timeout_commit|type|db_backend|indexer|prometheus) = ' "$C"
grep -E '^(minimum-gas-prices|iavl-cache-size|app-db-backend|evm-chain-id) = ' "$A"
The sentry: public, it finds the network through the seed and keeps your
validator's node id to itself (private_peer_ids: never gossiped;
unconditional_peer_ids: always accepted). It also serves state-sync
snapshots.
# on the sentry
C=/var/lib/aeva/config/config.toml
A=/var/lib/aeva/config/app.toml
sudo sed -i \
-e "s|^seeds = .*|seeds = \"$SEED\"|" \
-e "s|^persistent_peers = .*|persistent_peers = \"$SEED\"|" \
-e 's|^external_address = .*|external_address = "<sentry-public-ip>:26656"|' \
-e 's|^private_peer_ids = .*|private_peer_ids = "<validator-node-id>"|' \
-e 's|^unconditional_peer_ids = .*|unconditional_peer_ids = "<validator-node-id>"|' \
-e 's|^pex = .*|pex = true|' \
-e 's|^max_num_inbound_peers = .*|max_num_inbound_peers = 100|' \
-e 's|^max_num_outbound_peers = .*|max_num_outbound_peers = 30|' \
"$C"
sudo sed -i -e '/^\[state-sync\]/,/^\[/ s|^snapshot-interval = .*|snapshot-interval = 1000|' "$A"
The validator: it dials its sentry over the private network and nothing
else (no peer exchange), and keeps 10,000 blocks of state. With a second
sentry, list both in persistent_peers, separated by a comma.
# on the validator
C=/var/lib/aeva/config/config.toml
A=/var/lib/aeva/config/app.toml
sudo sed -i \
-e 's|^persistent_peers = .*|persistent_peers = "<sentry-node-id>@<sentry-private-ip>:26656"|' \
-e 's|^pex = .*|pex = false|' \
-e 's|^max_num_inbound_peers = .*|max_num_inbound_peers = 10|' \
-e 's|^max_num_outbound_peers = .*|max_num_outbound_peers = 10|' \
"$C"
sudo sed -i \
-e 's|^pruning = .*|pruning = "custom"|' \
-e 's|^pruning-keep-recent = .*|pruning-keep-recent = "10000"|' \
-e 's|^pruning-interval = .*|pruning-interval = "100"|' \
"$A"
The firewall: on the sentry, 26656 open to everyone; on the validator, 26656 from its sentries only; 26660 (metrics) from your monitoring host only; SSH from your admin address only.
# firewall, on the sentry
sudo ufw allow from <admin-ip> to any port 22 proto tcp
sudo ufw allow 26656/tcp
sudo ufw allow from <monitoring-ip> to any port 26660 proto tcp
sudo ufw enable
# firewall, on the validator
sudo ufw default deny incoming
sudo ufw allow from <admin-ip> to any port 22 proto tcp
sudo ufw allow from <sentry-private-ip> to any port 26656 proto tcp
sudo ufw allow from <monitoring-ip> to any port 26660 proto tcp
sudo ufw enable
6. The keys that sign blocks
aevad init wrote the validator's consensus key to
/var/lib/aeva/config/priv_validator_key.json and its signing state to
/var/lib/aeva/data/priv_validator_state.json. The rule that matters: one
copy of that key signs, ever. The same key on two running machines signs
two blocks at one height, which is a double sign: 5 % of the stake slashed
and the validator tombstoned for good. Back the key up offline; never start a
restored copy while the original can still run; move
priv_validator_state.json with the key.
For mainnet stakes, keep the key off the validator: a remote signer (tmkms with a YubiHSM 2, or a 2-of-3 Horcrux cluster) holds it and dials the validator on a private port. The validator then carries no key:
# remote signer, on the validator
C=/var/lib/aeva/config/config.toml
sudo sed -i -e 's|^priv_validator_laddr = .*|priv_validator_laddr = "tcp://<validator-private-ip>:26659"|' "$C"
7. Start: the sentry, then the validator
# on the sentry and the validator
sudo tee /etc/systemd/system/aevad.service >/dev/null <<'EOF'
[Unit]
Description=Aeva node
After=network-online.target
Wants=network-online.target
[Service]
User=aeva
ExecStart=/usr/local/bin/aevad start --home /var/lib/aeva
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable aevad
# on the sentry
sudo systemctl start aevad
until [ "$(curl -s 127.0.0.1:26657/status | jq -r .result.sync_info.catching_up)" = false ]; do sleep 10; done
curl -s 127.0.0.1:26657/status | jq '.result.sync_info | {latest_block_height, catching_up}'
# on the validator
sudo systemctl start aevad
until [ "$(curl -s 127.0.0.1:26657/status | jq -r .result.sync_info.catching_up)" = false ]; do sleep 10; done
curl -s 127.0.0.1:26657/net_info | jq '[.result.peers[].node_info.moniker]'
Both replay the chain from its first block (aeva-1 has run
v1.0.1-beta.1 since then) and stop waiting once they are at the top. The
validator's only peer is its sentry. Logs: journalctl -u aevad -f. After
the network's first software upgrade, a new node syncs by state sync from
the sentries' snapshots instead.
8. Your operator key
The operator key owns the validator: it bonds, sets the commission, and unjails. It is an ordinary account key (eth_secp256k1), kept on the operator machine in an encrypted keyring (a hardware wallet works through MetaMask for later operations).
# on the operator machine
aevad keys add operator --keyring-backend file
aevad keys show operator -a --keyring-backend file
Write the mnemonic down offline: it is shown once. The second line prints
the operator's aeva1… address; this is where your AEVA must arrive (over
the bridge, once it is open). Check it:
# on the operator machine
aevad query bank balances "$(aevad keys show operator -a --keyring-backend file)" --node "$NODE"
9. Create the validator
The validator's consensus public key (not the private key):
# on the validator
sudo -u aeva aevad comet show-validator --home /var/lib/aeva
Copy its output into validator.json on the operator machine. amount is
the self-bond in aaeva (1 AEVA = 10^18 aaeva: 1,000 AEVA is
1000000000000000000000). The minimum commission on aeva-1 is 5 %; the
maximum rate and the maximum daily change can never be changed later.
# on the operator machine
cat > validator.json <<'EOF'
{
"pubkey": <consensus-pubkey>,
"amount": "<self-bond>aaeva",
"moniker": "<your-moniker>",
"website": "<website>",
"security": "<security-contact>",
"details": "",
"commission-rate": "0.05",
"commission-max-rate": "0.20",
"commission-max-change-rate": "0.01",
"min-self-delegation": "1"
}
EOF
# on the operator machine
aevad tx staking create-validator validator.json --from operator --keyring-backend file \
--chain-id "$CHAIN_ID" --node "$NODE" \
--gas auto --gas-adjustment 1.5 --gas-prices 25000000000aaeva -y
A commission below the minimum is refused before anything is sent. Once the transaction is in a block:
# on the operator machine
aevad query staking validator "$(aevad keys show operator --bech val -a --keyring-backend file)" --node "$NODE" -o json \
| jq '.validator | {moniker: .description.moniker, status, jailed, tokens, commission: .commission.commission_rates.rate}'
BOND_STATUS_BONDED means it is in the active set. Your validator appears
on aevachain.app/stake, where anyone can
delegate to it, and on the explorer's
validators page.
10. Check that it signs
# on the validator
until [ "$(curl -s 127.0.0.1:26657/status | jq -r .result.validator_info.voting_power)" != 0 ]; do sleep 5; done
ADDR=$(curl -s 127.0.0.1:26657/status | jq -r .result.validator_info.address)
TOP=$(curl -s 127.0.0.1:26657/status | jq -r .result.sync_info.latest_block_height)
for h in $(seq $((TOP - 19)) "$TOP"); do curl -s "127.0.0.1:26657/commit?height=$h"; done \
| jq -s --arg a "$ADDR" '[.[].result.signed_header.commit.signatures[] | select(.validator_address == $a and .block_id_flag == 2)] | length'
The first line waits for voting power (two blocks after the create-validator); the last prints how many of the last 20 blocks your validator signed: 20 after half a minute of signing.
11. Monitoring
Scrape port 26660 on the validator and each sentry with Prometheus. The lines that matter:
# on the validator
curl -s 127.0.0.1:26660/metrics | grep -E '^cometbft_(consensus_(height|validator_power|validator_last_signed_height|validator_missed_blocks)|p2p_peers)\{'
| alert on | metric |
|---|---|
| the node stopped following the chain | cometbft_consensus_height not rising for 1 minute |
| your validator stopped signing | cometbft_consensus_validator_last_signed_height behind cometbft_consensus_height by more than 10 blocks |
| blocks not signed for | cometbft_consensus_validator_missed_blocks rising fast (created with the first one) |
| the validator lost its sentries | cometbft_p2p_peers below the number of sentries |
| a sentry is isolated | cometbft_p2p_peers below 3 on a sentry |
Also: disk space (alert at 80 %), memory, the clock (chrony), and restarts of the service. The chain's own count of your missed blocks:
# on the operator machine
aevad query slashing signing-info '<consensus-pubkey>' --node "$NODE" -o json
missed_blocks_counter counts over the last 30,000 blocks; the validator is
jailed when it passes 28,500. It counts only blocks your validator did not
vote on at all. The node's cometbft_consensus_validator_missed_blocks also
counts nil votes: a validator several relay hops from the proposer
sometimes receives the others' votes before the block and votes nil. That
costs nothing (a nil vote is a signed vote, for the chain and for rewards),
but a steady rise means your sentries are slow or badly connected.
Slashing risks
Double signing is the one that costs real stake: 5 % of everything bonded to the validator, delegators' AEVA included, and a tombstone: that validator can never sign again, and your operator must create a new one with a new consensus key. It happens when one consensus key signs on two machines: a standby started while the primary still runs, a restored backup, a migration without stopping the old node first, or two signers in a remote signer setup that are not a threshold cluster. It can be proven for at least 21 days after the fact. Avoid it by construction: one active signer, a failover that is manual, and the signing state moved with the key.
Downtime is cheaper but public: a validator that misses more than 28,500 of the last 30,000 blocks is jailed for at least 10 minutes and slashed 0.01 %. At about 1.2 s per block that is about 9.5 hours of downtime within the window. A jailed validator signs nothing and earns nothing until it sends an unjail, once the jail time has passed and the node signs again:
# only after a downtime jail, on the operator machine
aevad tx slashing unjail --from operator --keyring-backend file --chain-id "$CHAIN_ID" --node "$NODE" \
--gas auto --gas-adjustment 1.5 --gas-prices 25000000000aaeva -y
Unbonding takes 21 days, for you and your delegators, and AEVA that is unbonding can still be slashed for a fault committed while it was bonded.
Upgrades
The network upgrades by governance: a software-upgrade proposal names a height, every node stops there, and the new release (installed with the same signature check as step 3) takes over. Follow the proposals on aevachain.app/governance; your validator's vote counts with its stake.
Each release folder's README.md says what to do: download and check the
new binary before the height (install it beside the running one, not over
it: a new binary refuses to start before its upgrade height), then at the
halt install it as /usr/local/bin/aevad and restart. The first upgrade,
plan v1.1.0 (release v1.1.0-locks: lock periods for staked AEVA,
switched on by the upgrade), follows exactly that.
The foundation's delegation programme
The Aeva foundation takes applications from operators for its delegation programme at aevachain.app/validators/apply: who operates the validator, their experience, the infrastructure, where it runs, and how to reach them. Nothing about the delegations is decided yet: which validators, how much, when and for how long. An application is not a commitment from either side and does not reserve a delegation. Applications are encrypted to the foundation's key before they are stored; no list of applicants is published.