Validator Guide
Register on-chain, run a paraloom validator on devnet, and survive reputation + slashing.
Validator guide
A paraloom validator verifies Groth16 withdrawal proofs and votes in the BFT cohort. It does not generate proofs — that's client-side. The hardware bar is intentionally low: a recent laptop, a Raspberry Pi 5, or a small cloud VM all work.
Registration is permissionless on devnet. One binary does everything — paraloom, with a validator subcommand for each verb (register, start, status, list, unregister).
This is an early devnet program — run it to test and to be part of the network, not for yield. Three things to know before you start:
- Rewards don't pay out yet. Settlement fees are designed to flow to the round's leader on-chain, but fee distribution isn't wired into the node yet — so
total_earningsstays 0 on devnet. The reward + claim path exists and is tested on-chain; it goes live before mainnet. - Use a dedicated RPC. The public
api.devnet.solana.comrate-limits (429s) and will stall your node's deposit listener. Get a free dedicated devnet endpoint (Helius, Alchemy, QuickNode) and use it everywhere below. - Expect coordinated upgrades. Consensus is still evolving. When we ship a protocol change we'll announce an upgrade window in Discord — you pull, rebuild, and restart together with the rest of the set.
What you're signing up for
| Role | Verify-only — proof verification, voting, optional compute |
| Stake | 1 SOL (devnet), held in a per-validator on-chain PDA |
| Reward | Settlement fees, credited to the round leader on-chain — payout not live on devnet yet (see callout above) |
| Risk | Slashing on equivocation or persistent unavailability |
| Discovery | You dial one anchor; the Kademlia DHT fans the rest of the set out automatically |
1. Hardware
| Required | Recommended | |
|---|---|---|
| CPU | 2 cores x86_64 / arm64 | 4+ cores |
| RAM | 8 GB | 16 GB |
| Disk | 60 GB SSD | 120 GB NVMe |
| Network | 50 Mbit/s symmetric | 100+ Mbit/s |
No GPU required. RocksDB does fsync on hot writes so prefer SSD over HDD.
Should I run this on my desktop?
Short answer: the hardware is never the problem — uptime is.
A validator is verify-only — no GPU, and 2 cores / 8 GB comfortably covers any modern desktop or laptop. But the slashing model penalises persistent unavailability, and a desktop that sleeps overnight, gets shut down, or reboots for updates is exactly the machine that trips it. "Leave it running in the background" only works if the machine is genuinely on and reachable 24/7 — which on a home desktop also means:
- Port-forward TCP 9300 through your router (home NAT) so peers can dial you.
- Disable sleep/suspend — a sleeping machine misses heartbeats and votes.
- Keep NTP synced — consensus uses slot timestamps; clock drift breaks heartbeats.
So a daily-driver desktop is the setup most likely to earn an unavailability slash, not because it's weak but because it isn't always on.
What we'd actually recommend on devnet: don't buy dedicated hardware yet. Rewards are 0 here, so there's nothing to amortise.
- Most practical — a small always-on cloud VM (~$5/mo) plus a free Helius devnet RPC. No home-NAT to fight, port 9300 is trivial to open, and it never sleeps.
- Local option — a Raspberry Pi 5 (~$80, one-time) is plenty for a verify-only role and, because it doesn't sleep or reboot like your desktop, it's safer than a daily-driver.
- Dedicated hardware only makes sense once mainnet rewards are live and you're running long enough that cloud cost outweighs a one-time box.
If you do use a desktop, treat it like a server: sleep off, on a UPS if you can, and unregister before any planned downtime.
2. Install
Install the build dependencies, then build the paraloom CLI:
# system deps (Debian/Ubuntu; see release.yml for the full list)
sudo apt-get install -y build-essential pkg-config libssl-dev \
protobuf-compiler clang libclang-dev cmake \
libc++-dev libc++abi-dev libstdc++-12-dev
git clone https://github.com/paraloom-labs/paraloom-core.git
cd paraloom-core
cargo build --release --bin paraloomThis produces ./target/release/paraloom — one binary for every validator verb.
3. Solana wallet & devnet SOL
Registration stakes 1 SOL and pays tx fees, so fund the wallet with at least 2 SOL. The devnet faucet gives 2 SOL per 8 hours.
# Create the wallet that owns the validator stake
solana-keygen new --no-bip39-passphrase \
-o ~/.config/solana/paraloom-validator.json
# Top up on devnet
solana airdrop 2 \
$(solana-keygen pubkey ~/.config/solana/paraloom-validator.json) \
--url https://api.devnet.solana.comKeep this keypair secure — it controls the staked SOL and signs settlement transactions.
4. Register on-chain
This is the moment you become a validator. register stakes 1 SOL, creates a ValidatorAccount PDA seeded by your pubkey, and initialises reputation_score = 1000. Devnet RPC and the canonical program ID are the defaults, so the keypair is all you need to pass:
./target/release/paraloom validator register \
--keypair ~/.config/solana/paraloom-validator.jsonThe deployment it targets by default is program ID 8gPsRSm1CAw38mfzc1bcLMUXyFN7LnS8k6CV5hPUTWrP (override with --program-id, and the RPC with --rpc-url). Under the hood this calls register_validator in programs/paraloom/src/lib.rs:
pub fn register_validator(ctx: Context<RegisterValidator>, stake_amount: u64) -> Result<()> {
require!(stake_amount >= MIN_VALIDATOR_STAKE, BridgeError::InsufficientStake);
// transfer stake from wallet into the per-validator PDA, then:
validator_account.stake_amount = stake_amount;
validator_account.reputation_score = 1000;
validator_account.is_active = true;
// …
registry.total_validators += 1;
registry.active_validators += 1;
Ok(())
}MIN_VALIDATOR_STAKE is 1_000_000_000 lamports (1 SOL).
5. Verify registration
Read your registration straight from chain — no log-grepping:
# your own account (stake, reputation, active)
./target/release/paraloom validator status \
--keypair ~/.config/solana/paraloom-validator.json
# the whole live set
./target/release/paraloom validator listYou should see your pubkey with stake 1 SOL, reputation 1000, active: true. The same data is on the public Paraloom Explorer validator list and on Solana Explorer (open your validator PDA).
6. Configure
Copy the template that ships with the repo and edit the marked paths. It already wires the bootstrap anchor and the correct program ID:
mkdir -p ~/.paraloom
cp scripts/devnet/validator.toml.example ~/.paraloom/validator.tomlThe template (scripts/devnet/validator.toml.example):
[network]
# Public listen address. Open this TCP port on your firewall.
listen_address = "/ip4/0.0.0.0/tcp/9300"
# Devnet bootstrap anchor maintained by paraloom-labs. New nodes dial this
# to enter the Kademlia DHT; once you have any peer, the DHT fans out to
# the rest of the validator set automatically.
bootstrap_nodes = [
"/ip4/67.205.142.8/tcp/9300/p2p/12D3KooWFf8xfNz77E9Ve4HnpyZkAHKAcUdw4LmagpFCYQD6R7WK",
]
# mDNS is only useful on a LAN; leave off on a public-internet validator.
enable_mdns = false
# Persist this node's libp2p identity. Without it every restart rotates
# your PeerId. The node generates this file (mode 0600) on first start.
identity_path = "/var/lib/paraloom/node.key"
[node]
node_type = "ResourceProvider"
max_cpu_usage = 80
max_memory_usage = 70
max_storage_usage = 10240
[storage]
data_dir = "/var/lib/paraloom/data"
[bridge]
# Use a DEDICATED devnet RPC here — the public api.devnet.solana.com
# rate-limits (429) and will stall the deposit listener. Helius / Alchemy
# / QuickNode all have a free devnet tier.
solana_rpc_url = "https://devnet.helius-rpc.com/?api-key=YOUR_KEY"
program_id = "8gPsRSm1CAw38mfzc1bcLMUXyFN7LnS8k6CV5hPUTWrP"
poll_interval_secs = 8
enabled = true
# The Solana keypair you registered with in step 4 — same wallet that
# holds the 1-SOL stake; signs settlement txs.
authority_keypair_path = "/home/USER/.config/solana/paraloom-validator.json"
event_lag_warn_threshold_slots = 1500
withdrawal_expiration_window_slots = 150
# Local-only HTTP endpoint for wallet/CLI Merkle queries. Stays on
# loopback — do not open this port on the public firewall.
merkle_path_query_address = "127.0.0.1:9090"
# Empty = disabled. Set to a loopback address only on a node that accepts
# wallet-submitted withdrawal / transfer requests.
withdrawal_ingress_address = ""
transfer_ingress_address = ""Edit for your machine: identity_path, [storage] data_dir, [bridge] authority_keypair_path (point it at the wallet from step 3), and solana_rpc_url (your dedicated endpoint). Leave program_id and bootstrap_nodes as-is.
Don't leave solana_rpc_url on the public devnet endpoint. Under any real traffic it returns 429s, the deposit listener freezes, and your node silently stops tracking the bridge. A free dedicated RPC fixes it.
Discovery is automatic — the node dials the anchor, joins the Kademlia DHT, and the set fans peers out from there. Manual seeds beyond the anchor are only needed for an isolated localnet. See Networking.
7. Start
./target/release/paraloom validator start --config ~/.paraloom/validator.tomlOn first start the node generates and persists its libp2p identity at identity_path (mode 0600), dials the anchor, and begins listening on TCP/9300. Watch the logs for the bootstrap handshake and a rising peer count.
# more verbose startup
RUST_LOG=info ./target/release/paraloom validator start --config ~/.paraloom/validator.tomlRun as a systemd service
# /etc/systemd/system/paraloom-validator.service
[Unit]
Description=Paraloom validator
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=paraloom
Environment=RUST_LOG=info
ExecStart=/usr/local/bin/paraloom validator start --config /home/paraloom/.paraloom/validator.toml
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetCopy the built binary to a stable path first (sudo cp target/release/paraloom /usr/local/bin/), then:
sudo systemctl daemon-reload
sudo systemctl enable --now paraloom-validator
sudo journalctl -u paraloom-validator -fThis mirrors how the public devnet anchor itself runs.
Leaving cleanly (unregister)
Planning maintenance or shutting down for a while? Unregister first — don't just go silent, or you risk a persistent-unavailability slash. Unregistering returns your staked SOL to your wallet and marks the account inactive:
./target/release/paraloom validator unregister \
--keypair ~/.config/solana/paraloom-validator.jsonWhen you're ready to come back, register again (step 4) — it's permissionless.
Reputation & slashing
Each ValidatorAccount carries an on-chain reputation_score (initialised to 1000) and a times_slashed counter. Reputation moves with observed behaviour; the exact deltas are tunable per network. Validators below a gate threshold can be excluded from voting — see Consensus.
Two slashable events, settled on-chain via the slash_validator instruction (submitted by the program authority against cohort-signed evidence):
| Event | Detection | Result |
|---|---|---|
| Equivocation | Two valid signatures on conflicting messages at the same height | Stake slashed, times_slashed += 1, reputation tanked |
| Persistent unavailability | Missed heartbeats / votes over a window without unregistering | Cohort signs evidence; authority submits the slashing tx |
The clean way to avoid an unavailability slash is to unregister before extended downtime.
Monitoring
Today, monitor a validator through its process logs:
sudo journalctl -u paraloom-validator -f
# or, foreground:
RUST_LOG=info ./target/release/paraloom validator start --config ~/.paraloom/validator.tomlAn operational HTTP server with /health, /ready, and /metrics (Prometheus text) lives in the codebase (src/health/) exposing paraloom_peer_count, paraloom_ready, paraloom_uptime_seconds, and paraloom_build_info. It is not yet wired into the node — there is no config knob for it pre-mainnet. Until it lands, logs and the on-chain account are the source of truth. Track Monitoring for when the endpoint ships.
Earnings
total_earnings and pending_rewards live on your on-chain ValidatorAccount PDA. Settlement fees are designed to credit the round's leader, but fee distribution isn't wired into the node yet, so these stay 0 on devnet — see the callout at the top. The on-chain claim_rewards path exists and is tested; claiming goes live before mainnet. Read the fields any time from Solana Explorer (open your validator PDA) or paraloom validator status.
Security checklist
-
~/.config/solana/paraloom-validator.jsonischmod 600and backed up offline -
identity_path(node.key) is written0600by the node — don't loosen it -
solana_rpc_urlpoints at a dedicated RPC, not the public devnet endpoint - Firewall allows TCP/9300 inbound from anywhere (libp2p)
-
merkle_path_query_address(9090) stays on loopback — never opened publicly - Time is NTP-synced (consensus uses slot timestamps; large drift breaks heartbeats)
- You have a plan to
unregisterbefore extended maintenance windows
Troubleshooting
Next
- Consensus — vote weighting, slashing evidence, leader rotation
- Coordinator HA — what failover looks like when the primary dies
- Networking — DHT bootstrap and discovery