Run a validator#
Validators produce and finalize Hub blocks and check AppChain blocks. The public testnet starts from a genesis that already lists every validator, so outside validators join through a key ceremony before launch. Your private keys never leave your machine: you send only a signed entry file, and you check the finished genesis against it before you start a node.
Joining the public testnet
The number of outside validator seats, the entry deadline and the parameters below are announced before the ceremony. To take part, contact the team first.
Requirements#
| Recommended | |
|---|---|
| Server | 4+ cores, 8 GiB RAM, 100 GB SSD, Linux x86-64 with systemd |
| Network | A public IPv4 address, TCP and UDP open on the validator port (30400 below) |
| Uptime | High. Blocks need more than two thirds of all validators online |
| Clock | Synchronized with chrony or systemd-timesyncd |
Use the binaries of the announced release (tog-hubd, tog-signer), downloaded and checked against the published SHA-256 sums, or built from the same tag.
The ceremony at a glance#
- You make keys and an entry file, and send the entry to the coordinator.
- You send the entry's fingerprint over a second channel.
- The coordinator checks every fingerprint, writes the genesis and sends it to you.
- You verify the genesis against your entry and confirm the genesis hash over the second channel.
- Everyone starts their nodes at launch.
Agree on the second channel with the coordinator before you start: another person, e-mail or a chat that is not the one files come over. A file and its fingerprint never travel the same way.
1. Keys and entry#
On your validator machine:
tog-hubd ceremony keygen --dir keys
# writes validator.key, stash.key, network.key, network.pub (mode 0600)
# and prints the stash address and peer id
tog-hubd ceremony entry --network-id 2 --keys keys \
--self-bond <SELF_BOND> --commission-bps <COMMISSION_BPS> \
--out <NAME>.borsh
tog-hubd ceremony inspect --network-id 2 <NAME>.borsh
# prints the entry's fingerprint, stash and peer id
Use the self-bond and commission values announced for the network. --self-bond is in atto-TOG (1018 per test-TOG).
Back up the stash key first
stash.key controls the validator's bond. Make an offline, encrypted backup before anything else. The only working copy then lives in the signer's keys directory on the server (step 4); tog-hubd's own data directory never holds it.
Send <NAME>.borsh to the coordinator. Send the fingerprint line from inspect over the second channel.
2. Verify the genesis#
The coordinator sends you a directory with genesis.borsh, peers.txt and README.txt, and the genesis hash over the second channel. Check it against your own entry:
tog-hubd ceremony verify --genesis genesis.borsh --entry <NAME>.borsh
# genesis hash <HASH>
If the printed hash matches the one from the second channel, reply verified <HASH>. If it does not, start nothing and tell the coordinator. Any change to the entries produces a new genesis with a new hash, which every validator verifies again.
3. Lay out the node#
sudo useradd --system --home-dir /opt/tog --shell /usr/sbin/nologin tog
sudo useradd --system --gid tog --home-dir /opt/tog --shell /usr/sbin/nologin tog-signer
sudo install -d -o tog -g tog -m 750 /opt/tog/validator
sudo install -d -o tog-signer -g tog -m 750 /opt/tog/validator/signer
sudo install -d -o tog-signer -g tog -m 700 /opt/tog/validator/signer/keys
sudo install -o tog-signer -g tog -m 600 keys/validator.key keys/stash.key /opt/tog/validator/signer/keys/
sudo install -o tog -g tog -m 600 keys/network.key /opt/tog/validator/
sudo install -o tog -g tog -m 644 genesis.borsh /opt/tog/genesis.borsh
The signer runs as its own user and is the only process that can read the keys. tog-hubd runs as tog and asks the signer to sign through a socket the group may use.
4. Configure#
/opt/tog/validator/config.toml, with the values from README.txt:
data_dir = "."
genesis = "/opt/tog/genesis.borsh"
genesis_hash = "<GENESIS_HASH>"
listen = ["/ip4/0.0.0.0/tcp/30400", "/ip4/0.0.0.0/udp/30400/quic-v1"]
announce = ["/ip4/<PUBLIC_IP>/tcp/30400", "/ip4/<PUBLIC_IP>/udp/30400/quic-v1"]
peers = ["<PEER_1>", "<PEER_2>"] # the lines of peers.txt
block_time_ms = 2000
status = "127.0.0.1:30800"
signer = "signer/signer.sock"
With genesis_hash set, the node refuses any other genesis file.
The signer's unit (/etc/systemd/system/tog-validator-signer.service). Its era and session lengths must match the genesis; they are in README.txt:
[Unit]
Description=TOG validator's signer
After=network-online.target
[Service]
User=tog-signer
Group=tog
UMask=0077
WorkingDirectory=/opt/tog/validator
ExecStart=/opt/tog/bin/tog-signer --hub --socket /opt/tog/validator/signer/signer.sock --keys /opt/tog/validator/signer/keys --journal /opt/tog/validator/signer/journal --chain-id 8589934592 --uid <TOG_UID> --devnet-dkg --era-length <ERA_LENGTH> --session-length <SESSION_LENGTH>
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
--devnet-dkg belongs there only when you made the entry with --keys, as above. If your entry was signed through a running signer (ceremony entry --signer SOCK), leave it out. The node's unit (tog-validator.service) runs /opt/tog/bin/tog-hubd run --config /opt/tog/validator/config.toml as tog, after the signer.
Open the port:
sudo ufw allow 30400/tcp && sudo ufw allow 30400/udp
5. Launch and monitor#
At launch the coordinator's validators start first; start yours when they announce it:
sudo systemctl enable --now tog-validator-signer tog-validator
The node serves a status endpoint on its own 127.0.0.1:
| Endpoint | Returns |
|---|---|
GET /health |
200 ok while the newest decided block is fresh; 503 stale when the node is stalled or still syncing |
GET /status |
The node's state as JSON |
GET /metrics |
Prometheus metrics |
curl -s 127.0.0.1:30800/health
To scrape metrics from elsewhere, tunnel the port over SSH rather than opening it.
Joining later or restarting from a snapshot#
A node with an empty data directory can start from the latest era snapshot its peers serve, instead of syncing every block. Add to config.toml:
snapshot_sync = true
[trusted_checkpoint]
height = <TRUSTED_HEIGHT>
header_hash = "<TRUSTED_BLOCK_HASH>"
[trusted_checkpoint] is needed once the genesis is more than 14 days old; take the values from a published checkpoint. Set observer = true to run a full node that follows the chain without voting.
If the genesis has to change#
A genesis cannot be edited. A wrong parameter or a release that changes the stored data format means a new genesis. You keep your keys and make a new entry only when the release or your keys changed, then verify the new hash as in step 2.