Skip to content

Running a Node

This guide covers running Ashen nodes in various configurations: development, production validators, followers, and RPC gateways.

ModeCommandUse Case
Single-nodenode runLocal development, testing
Validatornode run --validatorProduction consensus participation
Followernode followerArchive/RPC nodes via P2P sync
RPC Gatewaynode rpcOffload RPC from validators

The fastest way to run a local development node:

Terminal window
# 1. Generate a keypair
just ed25519-keygen > ./target/dev.key.json
export ASHEN_PRIVATE_KEY=@./target/dev.key.json
ADDR=$(jq -r .address ./target/dev.key.json)
# 2. Initialize with funded account
just devnet-node init --data-dir ./node-data --alloc "$ADDR=1000000000000"
# 3. Run the node
just devnet-node run --data-dir ./node-data
# In another terminal, verify it's working:
just rpc-status
just rpc-account $ADDR

Or use the all-in-one just node recipe:

Terminal window
just node DATA_DIR=./node-data

Single-node mode runs a complete chain with one validator. This is ideal for:

  • Local development and testing
  • Contract development and debugging
  • Integration testing
Terminal window
# Initialize data directory
node init \
--data-dir ./node-data \
--alloc "0xYOUR_ADDRESS=1000000000000"
# Run the node
node run \
--data-dir ./node-data \
--listen 127.0.0.1:3030 \
--block-time-ms 1000
OptionDefaultDescription
--data-dir./node-dataDirectory for chain data
--listen127.0.0.1:3030RPC server address
--block-time-ms1000Target block time
--produce-empty-blockstrueProduce blocks even when mempool is empty
--no-rate-limitfalseDisable RPC rate limiting (dev only)

Pre-fund accounts at genesis using --alloc:

Terminal window
# Single allocation
node init --data-dir ./node-data --alloc "0xADDR=1000000000000"
# Multiple allocations
node init --data-dir ./node-data \
--alloc "0xADDR1=1000000000000" \
--alloc "0xADDR2=500000000000"
# Seeded accounts (deterministic for testing)
node init --data-dir ./node-data \
--seed 42 \
--seed-count 10 \
--seed-balance 1000000000

For rapid iteration, reduce block time and disable rate limiting:

Terminal window
node run \
--data-dir ./node-data \
--block-time-ms 500 \
--no-rate-limit \
--produce-empty-blocks

Validator mode participates in consensus with other validators. This is required for production networks.

  • Ed25519 network key for P2P identity
  • Peers configuration with all validator addresses
  • BLS keys for threshold signatures (from DKG or testnet seed)

Ashen validators validate the ELF output produced by the canonical compilers. If you build with a different Rust/LLVM/Zig toolchain, the ELF will not validate and your artifacts will be rejected. This is intentional: it protects developers from shipping binaries that the network cannot accept.

Do not go rogue on toolchains. Use the exact toolchain pinned by devenv (or the Docker image built from devenv) for all contract builds and node runs in live environments. Validators run the node inside that same Docker image in production.

Terminal window
# Initialize keystore
ashen keystore init
# Generate network key
ashen keystore add --label my-validator
# Export address for peers.yaml
ashen keystore export --label my-validator

Create peers.yaml with all validator public keys and addresses:

addresses:
"0x1234...abcd": "192.168.1.10:4040"
"0x5678...efgh": "192.168.1.11:4040"
"0x9abc...ijkl": "192.168.1.12:4040"

Testnet mode (deterministic BLS keys):

Terminal window
node run \
--data-dir /var/lib/ashen \
--validator \
--validator-network-key "keystore:my-validator" \
--peers ./peers.yaml \
--p2p-port 4040 \
--bls-seed 42 \
--validator-index 0 \
--bootstrapper "0x1234...abcd"

Production mode (DKG-generated BLS keys):

Terminal window
node run \
--data-dir /var/lib/ashen \
--validator \
--validator-network-key "keystore:my-validator" \
--peers ./peers.yaml \
--p2p-port 4040 \
--bls-share "$BLS_SHARE" \
--bls-polynomial "$BLS_POLY" \
--bootstrapper "0x1234...abcd"
OptionDescription
--validatorEnable validator mode
--validator-network-keyEd25519 key reference (keystore:<label>)
--peersPath to peers.yaml
--p2p-portP2P listen port (separate from RPC)
--bootstrapperBootstrapper public key (repeatable)
--bls-shareBLS threshold share (hex)
--bls-polynomialBLS commitment polynomial (hex)
--bls-seedDeterministic BLS seed (testnet)
--validator-indexYour index in validator set (testnet)
--enable-rpcEnable full RPC (validators default to health/metrics only)
--skip-preflightSkip startup checks (not recommended)

Validators run preflight checks at startup:

  1. BLS Key — Validates share and polynomial format
  2. Disk Space — Requires at least 1GB free
  3. Clock Sync — Warns if system time appears off
  4. Data Directory — Verifies write access

To skip (development only):

Terminal window
node run --validator --skip-preflight ...

Follower nodes sync via P2P without participating in consensus. Use for:

  • Archive nodes in different datacenters
  • RPC nodes that don’t share storage with validators
  • Read-only indexing nodes
Terminal window
node follower \
--data-dir ./follower-data \
--peers peers.yaml \
--bootstrapper "0x1234...abcd" \
--listen 127.0.0.1:3030 \
--p2p-port 4040
OptionDefaultDescription
--sync-batch-size32Headers per sync request
--sync-poll-ms1000Poll interval when caught up
--tx-forward-urlForward transactions to this URL
--network-keyOptional: specify P2P identity

Followers can forward transactions to a leader node:

Terminal window
node follower \
--tx-forward-url http://leader:3030 \
...

RPC gateway mode runs a read-only RPC server that shares the data directory with a running node. Use to offload RPC traffic from validators.

Terminal window
# On the same machine as the validator
node rpc \
--data-dir /var/lib/ashen \
--listen 0.0.0.0:3030 \
--refresh-interval-s 1
OptionDefaultDescription
--refresh-interval-s1How often to reload from disk
--rate-limit-per-s1000Requests per second
--rate-limit-burst2000Burst allowance
--auth-tokenOptional authentication token
┌─────────────────┐
Internet ────►│ RPC Gateway │──┐
│ (node rpc) │ │
└─────────────────┘ │ shared
├─ data-dir
┌─────────────────┐ │
Validators ◄──►│ Validator │──┘
│ (node run) │
└─────────────────┘

Preserve all historical state (no pruning):

Terminal window
node run --archive-mode --data-dir ./archive-data ...

Archive mode is required for:

  • Historical queries at any block height
  • State sync for new nodes
  • Debugging and forensics

Non-archive nodes prune old state to save disk space:

Terminal window
# Keep 10 epochs of state (default)
node run --prune-keep-epochs 10 ...
# Keep more history
node run --prune-keep-epochs 100 ...

Checkpoints enable fast sync by providing state snapshots:

Terminal window
# Checkpoint every 1000 blocks
node run --checkpoint-interval 1000 ...
# Default: checkpoint at epoch boundaries
node run ...

Terminal window
curl http://localhost:3030/health
# Returns: {"status":"healthy"}
Terminal window
curl http://localhost:3030/metrics
# Returns: Prometheus-format metrics
MetricDescription
ashen_tip_heightCurrent chain tip height
ashen_finalized_heightLast finalized height
ashen_mempool_sizePending transaction count
ashen_block_time_secondsRecent block times
ashen_rpc_requests_totalRPC request counter

Generate a diagnostic bundle:

Terminal window
just agent-report LISTEN=127.0.0.1:3030 OUT_DIR=./diagnostics

This collects:

  • Health status
  • Metrics snapshot
  • Recent log lines
  • Chain status

Enable detailed execution traces:

Terminal window
# Trace to stdout
ASHEN_TRACE_OUTPUT=1 node run --data-dir ./node-data
# Trace to files
ASHEN_TRACE_OUTPUT_DIR=./traces node run --data-dir ./node-data
Terminal window
just trace-diff trace_a.json trace_b.json
Terminal window
# Trace transaction execution
ashen debug trace --tx-hash 0x...
# Replay with breakpoints
ashen debug replay --tx-hash 0x... --breakpoint pc:0x1000

Terminal window
node run \
--log-file /var/log/ashen/ \
--log-rotation daily \
--log-max-files 30 \
--log-prefix node \
...

With rotation enabled, logs are named:

  • node.2026-01-22.log
  • node.2026-01-21.log
  • etc.
VariableDescription
NODE_LOG_FILELog file path
NODE_LOG_ROTATIONdaily, hourly, or never
NODE_LOG_MAX_FILESMax rotated files to keep
NODE_LOG_PREFIXLog filename prefix
RUST_LOGLog level filter

Default rate limits protect against DoS:

Terminal window
# Customize limits
node run \
--rate-limit-per-s 5000 \
--rate-limit-burst 10000 \
...
# Separate limits for utility endpoints
node run \
--utility-rate-limit-per-s 100 \
--utility-rate-limit-burst 200 \
...

Protect RPC with bearer tokens:

Terminal window
# Server side
node run --auth-token "secret-token-here" ...
# Client side
export NODE_AUTH_TOKEN="secret-token-here"
ashen status

For production, bind RPC to localhost and use a reverse proxy:

Terminal window
# Node binds to localhost
node run --listen 127.0.0.1:3030 ...
# Nginx or Caddy handles TLS and public exposure

Terminal window
# Check prerequisites
node run --validator --skip-preflight=false --data-dir ./check-only 2>&1 | head -20
# Start with systemd
sudo systemctl start ashen-node
# Or directly
node run --data-dir /var/lib/ashen --validator ...

Nodes handle SIGTERM gracefully:

Terminal window
# Graceful shutdown
kill -TERM $(pgrep -f "node run")
# Or with systemd
sudo systemctl stop ashen-node
Terminal window
# Remove all state (DESTRUCTIVE)
node reset --data-dir ./node-data
# Or manually
rm -rf ./node-data/{chain,state,logs}
Terminal window
# Export snapshot
ashen backup export --data-dir ./node-data --output ./snapshot.tar
# Import snapshot
ashen backup import --data-dir ./new-node --input ./snapshot.tar

“BLS key not found”

  • Ensure --bls-share and --bls-polynomial are both provided
  • Or use --bls-seed + --validator-index for testnet

“peer not found in config”

  • Your public key must be in peers.yaml
  • Check with ashen keystore export --label my-validator

“disk space check failed”

  • Ensure at least 1GB free in data directory
  • Use --skip-preflight temporarily (not recommended)
  1. Check peer connectivity:

    Terminal window
    curl http://localhost:3030/health
  2. Check logs for timeout messages:

    Terminal window
    grep -i "timeout\|stall" /var/log/ashen/*.log
  3. Verify clock sync:

    Terminal window
    timedatectl status
  • Reduce --p2p-mailbox-size and --p2p-message-backlog
  • Enable pruning (disable --archive-mode)
  • Reduce --prune-keep-epochs

“rate limited”

  • Increase --rate-limit-per-s and --rate-limit-burst
  • Or use --no-rate-limit for development

“body too large”

  • Increase --max-body-bytes