Registered humans can run an Aequitas validator node โ no stake, no application required, but NODE_OPERATOR_WALLET must be a registered Aequitas human (this is a protocol security requirement). Nodes participate in block production, validate the human registry, and synchronize the BlockDAG. Node operators earn a share of protocol fees via the Validators Pool (40% of all protocol fees, distributed daily). The more nodes that run, the more decentralized and resilient the network becomes.
This inline guide is in English. A translated PDF is available in your language using the button above.
AEQUITAS NODE OPERATOR GUIDE
v1.0 ยท June 2026
Complete step-by-step guide · No prior blockchain experience required · Estimated time: 20–30 min
What is an Aequitas Node?
An Aequitas node is a program that runs in the cloud and participates in the Aequitas network. It keeps a copy of the entire blockchain, validates who is a registered human, and produces new blocks (like new pages in the global ledger). The more nodes exist, the more decentralized and resilient the network becomes. As a reward for running a node, you receive a daily share of all protocol fees — automatically, with no further action required on your part.
Before You Start — What You Need
1.An Aequitas account: You must first be registered as a human on Aequitas. Install the Android app, complete biometric registration, and note your wallet address. Without this, you cannot receive validator rewards.
2.A GitHub account (free): Go to github.com and create a free account. You need this to copy (fork) the Aequitas code so Railway can deploy it.
3.A Railway account (free): Go to railway.app and sign in with GitHub. Railway is a hosting platform that runs your node in the cloud — no server or command line required.
4.Node signing key (RELAYER_PRIVATE_KEY): Your node needs a dedicated Ethereum wallet to sign on-chain registrations. This can be any MetaMask wallet. Export its private key: MetaMask → Account Details → Show Private Key → enter password → copy. Keep strictly private. IMPORTANT: To receive validator rewards you also need NODE_OPERATOR_WALLET set to your registered Aequitas human wallet (the one verified with the Aequitas app). Only verified humans can earn validator rewards.
5.10–30 minutes of your time. Railway does most of the work automatically.
Step 1 — Fork the Aequitas Repository on GitHub
What is a fork? A fork is your own personal copy of the Aequitas code on GitHub. Railway deploys directly from your fork, so you need one first.
a) Open github.com/hanoi96international-gif/Aequitas in your browser
b) Click the Fork button in the top-right corner of the page
c) Click Create fork — GitHub creates a copy under your own account (e.g. github.com/YOUR-NAME/Aequitas)
d) Done — you now have your own copy of the Aequitas node code
Step 2 — Create a PostgreSQL Database
What is a database? Your node needs permanent storage for all block data and human registrations. Without it, your node loses all data on every restart. PostgreSQL is the storage system Aequitas uses. Each node must have its own dedicated database โ never share one database between two nodes.
Option A — Railway (easiest, recommended for beginners)
a) Go to railway.app and sign in with your GitHub account
b) Click New Project
c) Inside your new project, click + New → Database → Add PostgreSQL
d) Railway creates and manages your database automatically. DATABASE_URL is injected into your node service for free — no manual configuration needed.
Option B — VPS / own server (Contabo, Hetzner, DigitalOcean)
Important: Install PostgreSQL directly on your VPS โ do not connect to Railway's PostgreSQL from a different VPS. Both nodes would share one database โ state conflicts and your data could be overwritten.
# 1. Install PostgreSQL (Ubuntu / Debian)
sudo apt update && sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresql
# 2. Create database and user (run as root or with sudo)
sudo -u postgres psql -c "CREATE USER aequitas WITH PASSWORD 'CHOOSE_A_STRONG_PASSWORD';"
sudo -u postgres psql -c "CREATE DATABASE aequitas OWNER aequitas;"
# 3. Allow connections from Docker containers (Docker bridge: 172.17.0.0/16)
PG_CONF=$(sudo -u postgres psql -t -c "SHOW hba_file;" | tr -d ' ')
echo "host aequitas aequitas 172.17.0.0/16 md5" | sudo tee -a $PG_CONF
sudo -u postgres psql -c "ALTER SYSTEM SET listen_addresses = '*';"
sudo systemctl restart postgresql
# 4. Test (should print one row)
psql "postgres://aequitas:CHOOSE_A_STRONG_PASSWORD@localhost:5432/aequitas" -c "SELECT 1;"
# Your DATABASE_URL for the docker run command below:
# postgres://aequitas:CHOOSE_A_STRONG_PASSWORD@172.17.0.1:5432/aequitas
# (172.17.0.1 = Docker bridge gateway, how containers reach the VPS host)
Firewall: PostgreSQL only needs to be reachable from within the VPS itself. Do not open port 5432 to the internet. ufw deny 5432 (or leave it closed โ most VPS firewalls block it by default).
Step 3 — Understand the Environment Variables
Environment variables are configuration settings you pass to your node before it starts. Think of them like a settings file. Collect these values before deploying — you will enter them in Step 4.
Security Warning: Your RELAYER_PRIVATE_KEY is like a master password. Anyone who has it controls your node wallet. Never share it publicly, never paste it in chat or email. Use a separate MetaMask wallet for RELAYER_PRIVATE_KEY (signing). NODE_OPERATOR_WALLET (for rewards) must be your registered Aequitas human wallet.
| Variable |
Required? |
What to enter and where to find it |
| DATABASE_URL |
YES |
Your PostgreSQL connection string. On Railway: auto-injected when PostgreSQL is in the same project. Format: postgres://user:pass@host:5432/dbname |
| RELAYER_PRIVATE_KEY |
YES |
The private key (starts with 0x, 66 characters total) of your dedicated node wallet. In MetaMask: click account icon → Account Details → Show Private Key → enter your MetaMask password → copy the key |
| RELAYER_ADDRESS |
Recommended |
The wallet address (starts with 0x, 42 characters) matching RELAYER_PRIVATE_KEY. This is the public address — safe to share. Copy it from MetaMask. A fallback exists in the node code, but setting this explicitly prevents startup errors. |
| NODE_OPERATOR_WALLET |
For rewards |
Your Aequitas human wallet address — the one you registered with via the Android app. This wallet receives your daily validator rewards (40% of all protocol fees). Must be a registered human on Aequitas. Find it in the app under your profile. |
| PEER_SECRET |
No |
No longer required. Validator authorization is now identity-based: a verified NODE_OPERATOR_WALLET plus the binding signature below is enough โ there is nothing to obtain from the network operator for this step. |
| NODE_OPERATOR_BINDING_SIGNATURE |
For multi-node |
Proves you own NODE_OPERATOR_WALLET โ without it, anyone could claim your wallet as their own node's operator and permanently lock you out of it. Generate it at /node-binding: connect the SAME wallet you registered with, it signs a short message naming your node's signing address, and shows you this value to copy here. To move your validator to a new machine later, just generate a new signature there for the new signing address โ no need to contact anyone. |
| SELF_URL |
YES |
Your node's own public URL (e.g. https://my-node.up.railway.app or http://YOUR-IP:8080 on VPS). Without it the node starts in isolated mode โ it cannot register with peers, cannot propagate blocks, and other nodes cannot reach it. On Railway: Settings → Networking → Public Networking. |
| PRIMARY_NODE_URL |
For multi-node |
Set to: https://aequitas.digital — the primary node your node registers with for automatic peer discovery. On startup your node posts its URL + signing address to the primary, gets the full peer list back, and joins the network automatically. No manual PEER_NODES list needed. |
| PORT |
No |
Leave unset on Railway — Railway sets this automatically. Default is 8080. |
| NODE_KEY |
No |
Base64-encoded libp2p private key for stable P2P identity. If not set: auto-generated on first start and printed to stderr. Copy the base64 string from SAVE THIS AS NODE_KEY ENVIRONMENT VAR: <base64> and paste it here to keep a stable peer ID across restarts. |
| IS_PRIMARY_NODE |
No |
Removed โ does nothing. Leave unset. |
| DISTRIBUTION_ENABLED |
No |
Leave unset for normal operation โ all nodes are eligible to trigger the daily pool distribution by default. Three-layer dedup (cross-node last_ubi_at check + intra-node CAS lock + replay pre-pass) prevents double-credit even when multiple nodes fire at the same time. Set to false only if you explicitly want to opt this node out of running distributions (e.g. a resource-constrained replica). |
| BOOTSTRAP_SNAPSHOT_URL |
Multi-node |
Set to https://aequitas.digital/api/snapshot on a fresh node. If the local DB has 0 humans at startup, the node automatically downloads and imports the full state from this URL โ fixing StateRoot mismatches immediately. Also set BOOTSTRAP_SIGNER. SNAPSHOT_TOKEN is optional โ only needed if you want the full export instead of the public bootstrap tier. |
| BOOTSTRAP_SIGNER |
Multi-node |
Ethereum signing address of the primary node. Required when BOOTSTRAP_SNAPSHOT_URL is set. Get it from https://aequitas.digital/api/status โ signing_address field. Prevents importing a tampered snapshot. |
| SNAPSHOT_TOKEN |
No |
Optional — no longer required to bootstrap a new node. Without a token, BOOTSTRAP_SNAPSHOT_URL still downloads everything a node needs to run correctly (accounts, balances, pool, config). A token only unlocks the FULL export (nullifier→wallet linkage + bio_registrations), used for authoritative resync/recovery of an already-diverged node — get it from the network operator only if you actually need that. |
| RESYNC_FROM_SNAPSHOT |
No |
Recovery tool for a node KNOWN to have diverged โ unlike BOOTSTRAP_SNAPSHOT_URL (which only merges into an empty DB), this REPLACES local accounts, pool, nullifiers, and chain_config with exactly what the snapshot contains, discarding anything local that doesn't match. Set to true together with BOOTSTRAP_SNAPSHOT_URL and BOOTSTRAP_SIGNER (mandatory here, no unsigned fallback), restart once, then remove it. Combine with RESET_DB_STATE=true for the cleanest result. |
| AUTO_HEAL_ON_DIVERGENCE |
Strongly recommended |
Important for network security and speed: if your node's chain ever diverges from the network (e.g. after downtime or a bad restart), set this to true together with PRIMARY_NODE_URL, BOOTSTRAP_SNAPSHOT_URL, and BOOTSTRAP_SIGNER โ your node then compares itself against PRIMARY_NODE_URL every few minutes and resyncs itself automatically, no manual RESYNC_FROM_SNAPSHOT needed. A node that stays diverged and keeps broadcasting its own blocks can slow down or destabilize the network for every other operator (confirmed live: this is what caused a network-wide slowdown on 2026-07-02). |
| SNAPSHOT_RESTRICT_TO_PRIVATE_NETWORK |
No |
Opt-in: when set to true, /api/snapshot only answers requests from private/loopback addresses instead of any caller with the right token. Off by default because this project's bootstrap/resync mechanism relies on cross-cloud-provider access (Railway โ VPS โ Railway); only enable this if every node you run shares a private network. |
| RESET_STATE |
No |
DANGEROUS: Setting this to true wipes your entire database on every restart. Development use only. Never in production. |
| RESET_DB_STATE |
No |
DANGEROUS, one-time use: truncates bootstrap-related tables (including evm_upgrade_relationship_slots) so a node can re-sync clean from genesis. Only takes effect within the first few minutes after process start to avoid repeated wipes on a crash-restart loop, and also requires ALLOW_DESTRUCTIVE_MAINTENANCE (see below). On success the process exits immediately instead of continuing to start — remove this variable (and ALLOW_DESTRUCTIVE_MAINTENANCE) and redeploy to bring the node back up. |
| CLEAR_REGISTRATIONS |
No |
DANGEROUS, one-time use: wipes all human registration data (chain_accounts' human flags, nullifiers, bio_hashes, liquidity_pool, evm_upgrade_relationship_slots) so everyone can re-register. Same 5–minute-from-startup guard as RESET_DB_STATE, plus also requires CLEAR_REGISTRATIONS_CONFIRM (see below) and ALLOW_DESTRUCTIVE_MAINTENANCE. On success the process exits immediately instead of continuing to start — remove all three variables and redeploy to bring the node back up. |
| CLEAR_REGISTRATIONS_CONFIRM |
No |
Required alongside CLEAR_REGISTRATIONS=true on both this service and the proof-server — must be set to the exact literal string I_UNDERSTAND_THIS_DELETES_ALL_REGISTRATIONS. Without it, CLEAR_REGISTRATIONS=true alone is refused. Exists so a single boolean set by accident (copy-pasted env file, typo elsewhere) can't wipe every human's registration on the next restart. |
| ALLOW_DESTRUCTIVE_MAINTENANCE |
No |
Required alongside RESET_DB_STATE=true or CLEAR_REGISTRATIONS=true — must be set to true. Without it, both are refused even with their other confirmation values correct. One more explicit gate so a wrong Railway/Render service selection or a stray copy-pasted env file can't trigger either destructive path alone. Visible at /api/health/combined (destructive_flags_set) whenever any destructive flag is currently set, even if it was refused. |
| BOOTSTRAP_P2P_ADDR |
No |
Overrides the built-in libp2p bootstrap multiaddress (see Step 7 below). Only needed if the network operator's Railway TCP-proxy address changes again in the future — set this instead of waiting for a code deploy. |
Step 4 — Deploy on Railway (Recommended)
Railway is the easiest way to run your node — no server setup, no command line required. The free tier covers all requirements. Total time: about 10–15 minutes.
1In your Railway project (from Step 2), click + New → GitHub Repo
2Select your Aequitas fork (from Step 1) — Railway detects the Dockerfile automatically
3Click Deploy Now — a first build starts (may fail without env vars, that is normal)
4Click your Aequitas service → Variables → add each variable:
# Railway auto-sets DATABASE_URL if PostgreSQL is in the same project
RELAYER_PRIVATE_KEY = 0xYOUR_PRIVATE_KEY
RELAYER_ADDRESS = 0xYOUR_NODE_WALLET_ADDRESS
NODE_OPERATOR_WALLET = 0xYOUR_HUMAN_WALLET
NODE_OPERATOR_BINDING_SIGNATURE = generate-at-/node-binding
SELF_URL = https://YOUR-RAILWAY-DOMAIN.up.railway.app
PRIMARY_NODE_URL = https://aequitas.digital
AUTO_HEAL_ON_DIVERGENCE = true # strongly recommended: auto-resyncs if your chain ever diverges
5Click Deploy (or save variables to trigger auto-redeploy). Build takes ~3 minutes while Go compiles the node binary.
6Watch Deploy Logs. Success looks like: Aequitas Node Running V and [NODE] Registered node operator wallet: 0x...
7Go to Settings → Networking → Generate Domain to get your public URL
8Open https://YOUR-URL/api/status in your browser — you should see JSON with height climbing steadily (check block_time in the same response for the current cadence)
Step 4b — Alternative: Deploy on a VPS with Docker
For your own server (Contabo, Hetzner, DigitalOcean). Requires Docker installed on the VPS and a local PostgreSQL database set up via Step 2 Option B above. Do not connect to Railway's PostgreSQL from a VPS โ each node must have its own separate database. NODE_OPERATOR_WALLET must be a registered Aequitas human wallet.
# 1. Install Docker (if not already installed)
curl -fsSL https://get.docker.com | sh
# 2. Clone and build the node (~3 min Go compile)
git clone https://github.com/hanoi96international-gif/Aequitas && cd Aequitas
docker build -t aequitas-node .
# 3. First start (NODE_KEY will be printed in logs โ see step 4)
# DATABASE_URL uses 172.17.0.1 = Docker bridge gateway โ host PostgreSQL
# (set up PostgreSQL first via Step 2 Option B above)
docker run -d --name aequitas-node --restart unless-stopped \
-e DATABASE_URL="postgres://aequitas:YOUR_DB_PASSWORD@172.17.0.1:5432/aequitas" \
-e RELAYER_PRIVATE_KEY="0xYOUR_PRIVATE_KEY" \
-e RELAYER_ADDRESS="0xYOUR_NODE_SIGNING_ADDRESS" \
-e NODE_OPERATOR_WALLET="0xYOUR_REGISTERED_HUMAN_WALLET" \
-e NODE_OPERATOR_BINDING_SIGNATURE="generate-at-/node-binding" \
-e SELF_URL="http://YOUR-SERVER-IP:8080" \
-e PRIMARY_NODE_URL="https://aequitas.digital" \
-e BOOTSTRAP_SNAPSHOT_URL="https://aequitas.digital/api/snapshot" \
-e BOOTSTRAP_SIGNER="0x92cbedec9d348b4762cb9af99500ee6139c5b671" \
-e AUTO_HEAL_ON_DIVERGENCE="true" # strongly recommended \
-p 8080:8080 -p 4001:4001 aequitas-node
# 4. Copy NODE_KEY from logs (do this once โ gives your node a stable P2P identity)
docker logs aequitas-node 2>&1 | grep "SAVE THIS AS NODE_KEY"
# 5. Stop, add NODE_KEY, restart permanently:
docker stop aequitas-node && docker rm aequitas-node
docker run -d --name aequitas-node --restart unless-stopped \
-e DATABASE_URL="postgres://aequitas:YOUR_DB_PASSWORD@172.17.0.1:5432/aequitas" \
-e RELAYER_PRIVATE_KEY="0xYOUR_PRIVATE_KEY" \
-e RELAYER_ADDRESS="0xYOUR_NODE_SIGNING_ADDRESS" \
-e NODE_OPERATOR_WALLET="0xYOUR_REGISTERED_HUMAN_WALLET" \
-e NODE_OPERATOR_BINDING_SIGNATURE="generate-at-/node-binding" \
-e NODE_KEY="base64-from-step-4" \
-e SELF_URL="http://YOUR-SERVER-IP:8080" \
-e PRIMARY_NODE_URL="https://aequitas.digital" \
-e BOOTSTRAP_SNAPSHOT_URL="https://aequitas.digital/api/snapshot" \
-e BOOTSTRAP_SIGNER="0x92cbedec9d348b4762cb9af99500ee6139c5b671" \
-e AUTO_HEAL_ON_DIVERGENCE="true" # strongly recommended \
-p 8080:8080 -p 4001:4001 aequitas-node
Tip: Save all vars in /root/.aequitas.env (chmod 600) and use --env-file /root/.aequitas.env instead of listing each -e โ keeps secrets out of shell history and simplifies updates.
Port requirements: TCP 8080 must be open inbound (API + RPC). TCP 4001 is optional (P2P โ enables direct node-to-node connections). If P2P is firewalled, HTTP sync still works. On Linux: ufw allow 8080/tcp
Step 5 — Verify Your Node is Running
Open these URLs in your browser. Replace YOUR-NODE-URL with your actual Railway domain or server address.
https://YOUR-NODE-URL/api/status
→ Expected: {"height": 1234, "total_humans": N, "aequitas_index": N}
https://YOUR-NODE-URL/rpc
→ Expected: {"jsonrpc":"2.0","error":"method not specified"} — this confirms RPC is alive
The block height should match the primary node within 1–2 blocks within seconds of startup. If it stays at 0, check that PRIMARY_NODE_URL=https://aequitas.digital is set and reachable.
Step 5b — Link Wallet for Rewards
โ Usually automatic โ most users skip this step
When your node starts, it
automatically connects to the network and registers for block production. If you set
NODE_OPERATOR_WALLET in your environment variables (Step 4), your wallet is already linked and you will receive validator rewards automatically.
You only need Step 5b if:
- Your node logs show
[NODE] validator key not authorized
- You want to change your reward wallet without restarting the node
- You are running a Docker/VPS node and auto-registration failed
Check if already registered: Look in your node logs for [PEERS] Registered with primary node. If you see it โ you're done, no manual step needed.
Step 6 — Connect MetaMask to Your Node (Optional)
You can use your own node as a custom RPC in MetaMask so your wallet connects through your node instead of the shared public node. In MetaMask: click the network dropdown at the top → Add network → Add a network manually, then enter:
| Network Name | Aequitas Chain |
| RPC URL | https://YOUR-NODE-URL/rpc |
| Chain ID | 1926 |
| Currency Symbol | AEQ |
| Decimals | 18 |
| Block Explorer | https://aequitas.digital |
Step 7 — Earning Validator Rewards
The Validators Pool collects 40% of all protocol fees (swap fees, demurrage, wealth cap overflow). Every day at 20:00 Berlin time (CEST/CET, handles DST automatically) the primary node distributes the pool balance to all registered node operator wallets proportionally. The more consistently your node runs, the larger your share.
1.Make sure you are registered as a human on Aequitas. If not: install the Android app and complete biometric registration first. You will receive a wallet address and 1,000 AEQ.
2.Set NODE_OPERATOR_WALLET = your Aequitas human wallet address in your Railway Variables
3.Save — Railway redeploys automatically. On Docker: docker restart aequitas-node
4.In your node logs, confirm: [NODE] Registered node operator wallet: 0x...
5.Rewards are distributed automatically every day at 20:00 Berlin time (CEST/CET). Just keep your node running — no further action needed.
Troubleshooting
| Symptom |
Likely cause |
Solution |
| Block height stays at 0 |
PRIMARY_NODE_URL not set or wrong |
Set PRIMARY_NODE_URL=https://aequitas.digital and redeploy. Also set SELF_URL to your own node's public URL. |
| DATABASE_URL error on startup |
Wrong connection string or PostgreSQL unreachable |
Check format: postgres://user:pass@host:5432/dbname — make sure PostgreSQL is running and accessible |
| "no code at address" in logs |
V7 contract not yet deployed in this EVM |
Normal on first start when RELAYER_ADDRESS is set — node auto-deploys V7. Wait a few seconds and check again. |
| "NODE_OPERATOR_WALLET not set" in logs |
Missing environment variable |
Add NODE_OPERATOR_WALLET=0xYOUR_HUMAN_WALLET to your variables. Node runs fine without it but you won't receive rewards. |
| Railway shows "Application error" |
Build or startup failure |
Check Deploy Logs in Railway for the error message. Most common cause: DATABASE_URL missing or RELAYER_PRIVATE_KEY in wrong format (must start with 0x). |
| Port 8080 not reachable (Docker) |
Firewall or cloud provider config |
Open TCP port 8080 inbound in your firewall or cloud security group settings. |
| Docker build fails with module error |
No internet access during build |
Docker build needs outbound internet to download Go modules. Railway handles this automatically. |
| โ P2P bootstrap unreachable (HTTP sync still works) |
libp2p port 4001 firewalled (very common) |
Not critical โ HTTP block sync is the primary mechanism and runs automatically. Add -p 4001:4001 and ufw allow 4001/tcp to enable P2P as well. |
| Bootstrap snapshot failed / StateRoot mismatch |
SNAPSHOT_TOKEN not set on primary, or BOOTSTRAP_SIGNER wrong |
Set BOOTSTRAP_SNAPSHOT_URL=https://aequitas.digital/api/snapshot, BOOTSTRAP_SIGNER=0x92cbedec9d348b4762cb9af99500ee6139c5b671, and SNAPSHOT_TOKEN (from network operator). Restart โ node imports state automatically if DB is empty. |
| Node not in block explorer / no MERGE blocks |
Port 8080 not reachable from outside OR Step 5b not done |
1) Open port 8080 inbound (ufw allow 8080/tcp). 2) Set SELF_URL=http://YOUR-IP:8080. 3) Complete Step 5b to register your signing key. Then the primary node syncs your blocks and MERGE events appear. |
| MetaMask shows 0 AEQ or wrong balance after registration |
Stale network config in MetaMask (cached old RPC data) |
MetaMask โ Settings โ Networks โ delete all "Aequitas Chain" entries โ re-add via the "+ ADD AEQUITAS NETWORK" button on this website. Balance will update immediately. |
| NODE_KEY generating new key on every restart |
NODE_KEY env var not set |
On first start, look for SAVE THIS AS NODE_KEY ENVIRONMENT VAR: <base64> in logs. Copy that value and add it as NODE_KEY environment variable. Restart once โ P2P identity is now stable across all future restarts. |
Questions / Feedback
Open an issue on
GitHub or reach the Aequitas team via the repository. Feedback on node setup, performance, and documentation gaps is especially welcome. Download this guide as a PDF in your selected language using the button above.