Skip to main content

Running an Earth node

Everything you need to sync a node on earth-1.

If you can't get a node syncing from this page alone, that's a bug in this page — please open an issue.

Not launched yet. Two values below are marked TBD and will be filled in with the launch release: the seed address and the genesis hash. Until then the network is a single validator and there is nothing to join.


1. Get the binary

Download from the latest release:

VERSION=v0.1.7 # use the launch tag
ARCH=amd64 # or arm64

curl -LO https://github.com/zenopie/earth-network-chain/releases/download/$VERSION/earthd_${VERSION}_linux_${ARCH}.tar.gz
curl -LO https://github.com/zenopie/earth-network-chain/releases/download/$VERSION/checksums.txt

sha256sum -c checksums.txt --ignore-missing # must say OK
tar xzf earthd_${VERSION}_linux_${ARCH}.tar.gz

sudo install -m755 earthd_${VERSION}_linux_${ARCH}/bin/earthd /usr/local/bin/
sudo install -m644 earthd_${VERSION}_linux_${ARCH}/lib/* /usr/local/lib/

Both lines matter. earthd links libwasmvm — the CosmWasm engine — as a shared library, and the two are version-locked: a node must never pair one release's earthd with another release's libwasmvm. The tarball ships the matching copy in lib/, along with the C++ runtime the proof verifier needs.

The binary looks for them at ../lib relative to itself, which is why installing to /usr/local/bin and /usr/local/lib works with no ldconfig and no LD_LIBRARY_PATH. Keep the pair together if you install somewhere else.

Check it:

earthd version --long

The version and commit it prints are how you answer "am I running what everyone else is running" — the only question that matters during an upgrade.

Building instead of downloading? You need cgo and the proof verifier:

sudo apt-get install -y clang python3 binutils libc++-dev libc++abi-dev
cd third_party/barretenberg-go && ./scripts/build-wrapper.sh --platform linux_amd64
cd ../.. && make install

There is also a container image at ghcr.io/zenopie/earth-network-chain, pinned by digest. See docker/README.md.


2. Initialise

earthd init "<your-moniker>" --chain-id earth-1

3. Install genesis — and check it

This is the step that decides whether you join earth-1 or start your own chain alone. A node whose genesis differs by one byte computes a different app hash and will never agree with anyone.

curl -L -o ~/.earth/config/genesis.json \
https://github.com/zenopie/earth-network-chain/releases/download/$VERSION/genesis.json

sha256sum ~/.earth/config/genesis.json

It must print:

TBD — published with the launch release

If it doesn't, stop. Don't work around it.

earthd genesis validate-genesis

4. Configure

Seeds and reachability — in ~/.earth/config/config.toml:

seeds = "[email protected]:26656"
# The address other nodes should dial to reach you. Set it if this node is
# behind NAT, a container, or a cloud provider that maps ports — otherwise
# CometBFT advertises the address it sees on itself, hands that to every peer
# it meets, and nobody can dial you back.
external_address = "your.host.or.ip:26656"

Port 26656 has to be reachable from outside for peers to connect to you. A node can sync without that — it dials out — but it will never be dialled, which means it contributes nothing to the network's connectivity and cannot serve state sync to anyone.

Running from the Docker image, these are SEEDS, PERSISTENT_PEERS and EXTERNAL_ADDRESS environment variables; the entrypoint writes them into config.toml on every start, so a restart is enough to change one.

Minimum gas price — in ~/.earth/config/app.toml. Required. The node will not start without it, and the error doesn't say which file to edit:

set min gas price in app.toml or flag or env variable

Below this value your node won't relay a transaction. This is a per-node setting, not a chain rule — there is no fee module, so the network's real floor is whatever most validators pick:

minimum-gas-prices = "0.005uerth"

Pruning — pick by what the node is for, in app.toml:

rolesetting
validatorpruning = "default"
public RPCpruning = "custom", pruning-keep-recent = "362880", pruning-interval = "100"
archivepruning = "nothing" — grows without limit

Snapshots — on by default, and worth leaving on:

snapshot-interval = 1000 # ~80 minutes at 5s blocks
snapshot-keep-recent = 5

This is what lets other people state-sync from you. 0 disables it. If everyone disables it, nobody can join without replaying the whole chain.

If you change pruning, check the states a snapshot needs are still kept — pruning = "default" keeps far more than the snapshot interval, so the two do not collide.

Don't enable enabled-unsafe-cors on a validator. It lets any website read your node and broadcast through it. If you're serving a browser app, run a separate read-only node for that.


4b. State sync (optional, but much faster)

Instead of replaying every block, fetch state at a recent height from a peer.

This chain benefits more than most. Replaying a block re-executes its transactions, and every passport registration verifies a zero-knowledge proof. A chain that mostly moves tokens replays quickly; this one re-runs a proof per registration, so replay cost grows with adoption.

Get a trust height and hash a little behind the tip:

RPC=https://rpc.erth.network
LATEST=$(curl -s $RPC/block | jq -r .result.block.header.height)
TRUST_HEIGHT=$(( LATEST - 2000 ))
TRUST_HASH=$(curl -s "$RPC/block?height=$TRUST_HEIGHT" | jq -r .result.block_id.hash)
echo "$TRUST_HEIGHT $TRUST_HASH"

Put them in ~/.earth/config/config.toml under [statesync]:

enable = true
rpc_servers = "https://rpc.erth.network:443,https://rpc.erth.network:443"
trust_height = <TRUST_HEIGHT>
trust_hash = "<TRUST_HASH>"
trust_period = "168h0m0s"

rpc_servers needs at least two entries — the same address twice is accepted, though two independent ones are better.

Then start with an empty data directory:

earthd tendermint unsafe-reset-all --home ~/.earth --keep-addr-book
earthd start

The log shows Discovering snapshots, then Fetching snapshot chunks. If it sits on Discovering snapshots with no progress, no peer is offering one — check snapshot-interval is non-zero on the node you are syncing from.


5. Start

earthd start

Watch it catch up:

curl -s localhost:26657/status | jq .result.sync_info

catching_up: false means you're synced.


Hardware

validatorpublic RPCarchive
CPU4 cores4 cores8 cores
RAM16 GB16 GB32 GB
Disk500 GB SSD1 TB SSD2 TB+ SSD

SSD, not spinning disk — the node fsyncs every block.

One thing specific to this chain: every passport registration verifies a zero-knowledge proof on-chain, which is CPU-heavy and cannot be skipped. Budget more CPU than a chain of this size would normally need.


Becoming a validator

Sync first. A node that isn't caught up can't validate.

Write validator.json:

{
"pubkey": PASTE_OUTPUT_OF_show-validator,
"amount": "1000000uerth",
"moniker": "<your-moniker>",
"commission-rate": "0.1",
"commission-max-rate": "0.2",
"commission-max-change-rate": "0.01",
"min-self-delegation": "1"
}

pubkey is the whole JSON object from earthd comet show-validator, pasted in unquoted — not a string.

earthd tx staking create-validator validator.json \
--chain-id earth-1 --from <your-key> --gas auto --gas-adjustment 1.5

Your consensus key should not live on the node. Use a remote signer — see deploy/akash/REMOTE_SIGNER.md. It fails closed: with a signer configured and none answering, your node signs nothing rather than signing with a key it shouldn't have.

Double-signing gets you slashed and tombstoned permanently. Never run two nodes with the same consensus key — not during a migration, not for a few seconds.


Upgrades

Upgrades halt the chain at an agreed height. Your node stops on its own and waits.

Use cosmovisor so the new binary is staged in advance and swaps automatically, instead of you doing it by hand at whatever hour the height lands.

Each upgrade's release notes give the name, the height, and the binary.


If something goes wrong

"expected chain id earth-1" — wrong genesis. Redo step 3.

Wrong app hash at a height — your genesis differs from everyone else's, or you're on the wrong binary. Check both hashes.

"set min gas price in app.toml" — the node won't start until minimum-gas-prices is set in app.toml. See step 4.

No peers — your seeds are wrong, or port 26656 isn't reachable. Peers have to be able to dial you.

Stuck at a height with peers connected — usually an upgrade you haven't applied. Check the releases page.

State sync stuck on "Discovering snapshots" — no peer is offering one. The node you are syncing from needs a non-zero snapshot-interval, and a snapshot cannot be produced for a height already passed.