Run a node
Requirements
- Rust (stable) with the
wasm32-unknown-unknowntarget - A C++ toolchain and
clang(RocksDB is built from source) - Node.js 18+ for the gateway
- A few GB of disk for the build
Build
The node embeds two system contracts, so build them first:
cd neurochain
rustup target add wasm32-unknown-unknown
for c in nrc20 ncert; do
(cd contracts/$c && RUSTFLAGS="-C link-arg=--allow-undefined" \
cargo build --release --target wasm32-unknown-unknown)
done
cargo build -p neurochain-node --bin neurochain # → target/debug/neurochainWithout the contract step the node build fails with couldn't read …/nrc20.wasm. The --allow-undefined flag lets the contracts' host imports link with current Rust; new contracts can declare #[link(wasm_import_module = "env")] instead (see NeuroWASM).
The first build takes several minutes (RocksDB); later builds take seconds. Use --release for anything beyond local testing.
A single local node
CHAIN_ENV=testnet \
NEUROCHAIN_DATA_DIR=./data \
NEUROCHAIN_MODELS_DIR=./models \
NEUROCHAIN_P2P_PORT=30333 \
NEUROCHAIN_RPC_PORT=9933 \
NEUROCHAIN_NODE_ADDR=localhost:30333 \
NEUROCHAIN_MIN_VALIDATORS=1 \
RUST_LOG=neurochain=info \
./target/debug/neurochainCHAIN_ENV=testnetturns on the faucet and relaxes signature checks for development. Leave it unset (mainnet rules) for anything real.- Without
NEUROCHAIN_GENESIS_VALIDATORSthe node makes itself the only validator (neuro:node-1.node) and starts producing blocks at once. NEUROCHAIN_MIN_VALIDATORS=1silences the warning about having fewer than 7 validators.
Check it:
curl -s http://localhost:9933/health
# {"status":"ok","height":6,"validator_count":1,"bft_quorum":1,"bft_ready":false,…}Then start the gateway.
A multi-validator network
Every validator must start from the same genesis, which lists every validator with its consensus public key. The repository automates this for a local network:
cd neurochain
scripts/net-test/net-test.sh keys 4 # create 4 validator keys, write the shared genesis set
scripts/net-test/net-test.sh up 4 # start them (node 1 first, the rest bootstrap from it)
scripts/net-test/net-test.sh check # heights agree, blocks are produced, sponsored gas is consistent
scripts/net-test/net-test.sh downIt uses RPC ports 9941+, P2P ports 30341+ and data under .net-test/, so it does not touch a normal ./data node.
To do it by hand:
- Create each validator's key. Start each node once with its own
NEUROCHAIN_DATA_DIR,NEUROCHAIN_NODE_ADDRand ports. It writes its key to<data dir>/shard0/bft_key.binand logs:Stop it and deleteConsensus validator … — ML-DSA-65 pubkey (put in NEUROCHAIN_GENESIS_VALIDATORS): 6e80bab8…<data dir>/shard0/state_rdband<data dir>/shard0/wal. Keepbft_key.bin. - Write the genesis set — the same value for every node:Fields: validator address, its P2P address (must equal that node's
NEUROCHAIN_GENESIS_VALIDATORS='neuro:val1.node|host1:30333|1000000|0.9|<pubkey1>,neuro:val2.node|host2:30333|1000000|0.9|<pubkey2>,…'NEUROCHAIN_NODE_ADDR), stake in NRO, initial trust (reported only), ML-DSA-65 public key. An entry without a public key is left out of consensus. - Start the first node, then the others with
NEUROCHAIN_BOOTSTRAP=host1:30333. SetNEUROCHAIN_MIN_VALIDATORSto your validator count if it is below 7.
Consensus messages travel over the same P2P port; there is no separate consensus port. A node recognises which validator it is by matching NEUROCHAIN_NODE_ADDR with the P2P address in the genesis set.
The genesis set never changes
Validators that join later register a .node and stake (see Staking); they do not edit NEUROCHAIN_GENESIS_VALIDATORS. A node started with a different genesis set or balance refuses the others' blocks and logs STATE DIVERGENCE.
Docker Compose
From the repository root:
cp .env.example .env # edit as needed
docker compose up -d # node, gateway, web app
docker compose --profile monitoring up -d # plus Prometheus and GrafanaThe images build the contracts and the node in stages. Node data lives in the node1-data volume; AI model files are mounted read-only from neurochain/models.
Data directory
| Path | Content |
|---|---|
<data dir>/shard<N>/bft_key.bin | The node's consensus and signing key — back it up, never share it |
<data dir>/shard<N>/state_rdb/ | RocksDB chain state of shard N |
<data dir>/shard<N>/wal/ | Consensus write-ahead log, replayed after a restart |
Starting from a snapshot
A new node can skip replaying history by loading a signed state snapshot from a running node:
NEUROCHAIN_CHECKPOINT_URL=http://trusted-node:9933 ./target/debug/neurochainOn an empty data directory the node fetches /api/v1/state/snapshot, applies it, and catches up from that height. A snapshot reveals every balance and stake; protect the endpoint with NEUROCHAIN_SNAPSHOT_TOKEN (clients then send Authorization: Bearer <token>).
Exposure and TLS
- The RPC listens on
localhostunder mainnet rules and on0.0.0.0underCHAIN_ENV=testnet; setNEUROCHAIN_RPC_HOSTto choose. - Set
NEUROCHAIN_TLS_CERTandNEUROCHAIN_TLS_KEY(PEM paths) to serve the RPC over HTTPS. - Put the gateway, not the node, in front of users.
NEUROCHAIN_INTERNAL_TOKENprotects internal endpoints (oracle log writes, AI data export) with anX-Internal-Tokenheader.
Monitoring
GET /metrics serves Prometheus metrics:
| Metric | Meaning |
|---|---|
nc_block_height, nc_bft_round | Chain progress |
nc_bft_validators_active, nc_bft_quorum_size | Validator set |
nc_peer_count, nc_mempool_size, nc_tx_total | Network and load |
nc_total_staked, nc_base_fee, nc_fees_burned | Economics (amounts in uNRO) |
nc_deploy_rejected_total{rule_id} | Deploys refused by VMGuardian, per rule |
nc_model_active_count, nc_model_voting_count, nc_model_version{…}, nc_model_confirms_needed, nc_model_voting_deadline_remaining_blocks | AI model registry |
nc_sentinel_flagged_total{category}, nc_sentinel_held_total, nc_sentinel_released_total{how} | Sentinel |
monitoring/ holds the Prometheus scrape config and a provisioned Grafana dashboard, with alerts for a large mempool, fewer than 2 peers and a burst of rejected deploys.
Backup and restore
./scripts/backup.sh [data_dir] [backup_dir] # live copy; the node keeps running
./scripts/restore.sh <backup_dir> [data_dir] # stop the node firstBackups include bft_key.bin — store them as securely as the key itself.
Logs worth knowing
| Log line | Meaning |
|---|---|
consensus decided, committing height=… | A block was finalised |
Below minimum: n/7 active validators | Fewer validators than NEUROCHAIN_MIN_VALIDATORS |
STATE DIVERGENCE | This node's state differs from the proposer's; it refuses to vote until resolved |
genesis validator(s) missing ML-DSA pubkey | An entry in the genesis set has no public key and is excluded |
All variables: Environment.