30. Node Operator Runbook: Complete Setup & Deployment Guide
This section is the authoritative technical reference for deploying a RandProof keeper node. It covers hardware requirements, software installation, B
This section is the authoritative technical reference for deploying a RandProof keeper node. It covers hardware requirements, software installation, BLS key generation, on-chain staking, configuration, multi-chain setup, monitoring, and maintenance. Follow every step in order. Estimated setup time: 45–90 minutes for a single-chain EVM node; 3–4 hours for full multi-chain deployment.
30.0 Platform Compatibility Guide: Mac, Windows & Desktop PCs
RandProof keeper nodes run on any modern consumer hardware, no server or VPS required. Mac Mini, MacBook, iMac, Windows PC, and Linux desktop are all fully supported. This section covers every platform, generation, and OS-specific setup path.
30.0.1 Apple Mac Mini: All Models (Recommended Home Node Hardware)
The Mac Mini is ideal for a RandProof home node: always-on, silent, power-efficient (6–22W idle), compact, and far more powerful than minimum requirements. Every Mac Mini from M1 (2020) onward is fully supported.
-
Mac Mini M4 (Late 2024), BEST VALUE: 10-core CPU, 10-core GPU | 16–24 GB unified RAM | 256 GB–2 TB SSD | From $599: ✅ Native ARM64. Runs 5+ chains simultaneously with headroom to spare. Lowest power draw (~6W idle). Docker Desktop 4.x runs natively. Recommended starting point for any new node operator.
-
Mac Mini M4 Pro (Late 2024), POWER USER: 12 or 14-core CPU | 24–64 GB unified RAM | 512 GB–8 TB SSD | From $1,399: ✅ Native ARM64. Thunderbolt 5. Can run 10+ chains plus a full local monitoring stack or RandProofCompute workloads simultaneously. Ideal for high-uptime production nodes or running multiple instances.
-
Mac Mini M2 (2023): 8-core CPU | 8 or 16 GB RAM | 256 GB–2 TB SSD | Used ~$350+, ✅ Native ARM64. The 8 GB config is tight for multi-chain, use the 16 GB config. Capable for 3–5 chains. Excellent value on the used market.
-
Mac Mini M2 Pro (2023): 10 or 12-core CPU | 16 or 32 GB RAM | 512 GB–8 TB SSD | Used ~$600+, ✅ Native ARM64. Excellent multi-chain node. Optional 10GbE useful for high-throughput operations.
-
Mac Mini M1 (2020): 8-core CPU | 8 or 16 GB RAM | 256 GB–2 TB SSD | Used ~$250+, ✅ Native ARM64. The 16 GB config is required for multi-chain. Docker Desktop natively supported since 2021. Good for 2–3 chains. Great value entry point.
-
Mac Mini Intel (2018): Intel Core i3/i5/i7 | 8–64 GB DDR4 | 256 GB–2 TB SSD | Used ~$150+, ⚠ x86_64. Supported but not recommended, higher power draw, EOL from Apple. Fully functional if you already own one. Minimum 16 GB RAM required for keeper node.
-
Mac Mini Intel (2014–2017): Intel Core i5/i7 | 4–16 GB DDR3 | Used ~$80+, ⛔ NOT RECOMMENDED. Maximum 16 GB RAM, older hardware, HDD configurations too slow. Upgrade RAM to 16 GB and install SSD before attempting. Testnet use only.
⚠ NOTE: All Apple Silicon Mac Mini models (M1, M2, M4) run Docker Desktop natively on ARM64: no emulation or Rosetta required. The RandProof Docker image ships as a multi-arch image (linux/arm64 + linux/amd64) and pulls natively on all Apple Silicon hardware.
30.0.2 Mac Setup: macOS 13 Ventura or Later (All Mac Models)
macOS does not include apt, UFW, or systemctl. Follow this path instead of the Linux steps in Section 30.1.3:
# Step 1: Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Apple Silicon: add Homebrew to PATH after install
echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
# Step 2: Install Docker Desktop for Mac
brew install --cask docker
# Or download from: https://www.docker.com/products/docker-desktop/
# Select: "Mac with Apple Chip" (M1/M2/M4) OR "Mac with Intel Chip"
# Open Docker Desktop from Applications. In Settings > Resources set:
# CPUs: 4+ | Memory: 4 GB minimum (6 GB for multi-chain) | Disk: 50 GB
# Step 3: Install Node.js 20 and RandProof CLI
brew install node@20
echo 'export PATH="/opt/homebrew/opt/node@20/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
npm install -g @randproof/cli
# Step 4: Verify (Apple Silicon - confirm native ARM64)
docker pull --platform linux/arm64 randproof/randproofkeeper:latest
# No "platform mismatch" warning = native ARM64 confirmed30.0.3 Mac Always-On Configuration (Critical for Node Uptime)
# Prevent Mac Mini from sleeping (run in Terminal)
sudo pmset -a sleep 0 # disable sleep
sudo pmset -a disksleep 0 # disable disk sleep
sudo pmset -a displaysleep 10 # display can still sleep
sudo pmset -a powernap 1 # allow background tasks
pmset -g # verify settings
# Enable Docker Desktop auto-start:
# Docker Desktop > Settings > General > Start Docker Desktop when you log in = ON
# Container auto-restart (use --restart unless-stopped in docker run command):
# Already included in Section 30.6.1 docker run command.
# macOS Firewall: allow Docker incoming connections
# System Settings > Privacy & Security > Firewall > Firewall Options
# Add Docker.app and allow incoming connections.
# If running behind home router: forward port 4001 (P2P gossip) to your Mac.Note: Ports 3000 (health) and 9090 (metrics) are localhost-only, never expose to public internet (AV-35). Port 4001 (P2P gossip) must be public but rate-limited (AV-36).
⚡ POWER TIP: A Mac Mini M4 running a RandProof keeper node uses approximately 6–12W at idle and 15–22W during fulfillment. At $0.15/kWh that is roughly $1–2/month in electricity, the most energy-efficient node platform available.
30.0.4 Windows 10 / 11 Desktop & Laptop: Setup via WSL2
Windows is fully supported via WSL2 (Windows Subsystem for Linux 2) + Docker Desktop for Windows. This provides a native Linux environment with near-native performance. Supported on any Windows 10 version 2004+ or Windows 11 machine.
Windows minimum requirements:
-
CPU: Any 64-bit Intel or AMD processor (2015 or newer), 4+ cores recommended
-
RAM: 8 GB minimum, 16 GB recommended
-
Storage: 50 GB free SSD space
-
OS: Windows 10 version 2004 (build 19041+) or Windows 11
-
BIOS: Virtualization enabled (VT-x or AMD-V), verify in Task Manager > Performance > CPU > Virtualization: Enabled
# Step 1: Install WSL2 (PowerShell as Administrator)
wsl --install
# Installs WSL2 + Ubuntu 22.04. Reboot when prompted.
# If WSL is already installed:
wsl --set-default-version 2
wsl --update
# Step 2: Install Docker Desktop for Windows
# Download: https://www.docker.com/products/docker-desktop/
# During setup: check "Use WSL2 instead of Hyper-V"
# After install: Settings > Resources > WSL Integration > Enable Ubuntu-22.04
# Step 3: Open Ubuntu terminal (Start > Ubuntu or WSL)
# All Linux commands in this runbook run identically inside WSL2.
docker --version # confirm Docker works in WSL2
docker run hello-world
# Step 4: Install Node.js and RandProof CLI inside WSL2
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
npm install -g @randproof/cli
# Step 5: Windows Firewall rules (PowerShell as Administrator)
New-NetFirewallRule -DisplayName "RandProof P2P" -Direction Inbound -Protocol TCP -LocalPort 4001 -Action Allow # AV-36: Consider rate-limiting via third-party firewall for production
New-NetFirewallRule -DisplayName "RandProof Health" -Direction Inbound -Protocol TCP -LocalPort 3000 -Action Allow
# Step 6: Prevent Windows from sleeping
# Settings > System > Power > Sleep > "Never" (while plugged in)
# Step 7: Auto-start on boot (PowerShell as Administrator)
$action = New-ScheduledTaskAction -Execute "wsl.exe" -Argument "-d Ubuntu -e docker start randproof-keeper"
$trigger = New-ScheduledTaskTrigger -AtStartup
Register-ScheduledTask -TaskName "RandProof Keeper" -Action $action -Trigger $trigger -RunLevel Highest
⚠ **WSL2 NOTE:** Store your .env file, keys directory, and data directory inside the WSL2 filesystem (~/ path) rather than the Windows filesystem (/mnt/c/). WSL2 filesystem I/O is significantly faster and avoids permission issues.30.0.5 Other Apple Hardware: MacBook, iMac, Mac Studio, Mac Pro
All modern Apple hardware on macOS 13+ is supported. Use the same Mac setup path (Section 30.0.2). Device-specific notes:
-
MacBook Pro / MacBook Air (M1, M2, M3, M4): ✅ Fully supported. Keep plugged in. Disable sleep: System Settings > Battery > Power Adapter > Prevent automatic sleeping when display is off. M3/M4 Pro with 16–36 GB RAM is excellent for multi-chain.
-
iMac M1 / M3 / M4 (24-inch): ✅ Fully supported. Always-on desktop. 16 GB+ config recommended for multi-chain.
-
Mac Studio (M2 Max, M4 Max, M4 Ultra): ✅ Excellent high-uptime production node. Can run 10+ chains plus RandProofCompute simultaneously.
-
Mac Pro (M2 Ultra, M4 Ultra): ✅ Server-grade. Suitable for running multiple node instances or genesis keeper node with maximum uptime.
-
MacBook (Intel, 2017–2020): ⚠ Supported (x86_64) but not recommended as a primary node due to battery and thermal constraints. Suitable for testnet or secondary node use.
30.0.6 Linux Desktop & Home Server
Linux desktops use the same setup as a VPS (Section 30.1.3). No additional steps. Supported distributions:
-
Ubuntu 22.04 LTS / 24.04 LTS: ✅ Recommended. All runbook commands written for Ubuntu.
-
Debian 12 (Bookworm): ✅ Fully supported. Identical apt commands.
-
Pop!_OS 22.04: ✅ Ubuntu-based. Identical setup path.
-
Fedora 39 / 40: ✅ Supported. Use dnf install docker-ce docker-ce-cli instead of apt.
-
Raspberry Pi 5 (8 GB, 64-bit OS): ✅ ARM64. Good single-chain or light multi-chain node. Pi 4 (4 GB) is testnet-only.
-
Arch Linux: ⚠ Supported but advanced. Use pacman/yay. Community-maintained setup guide.
30.0.7 Platform Quick-Reference
-
Mac Mini M4 (2024): ARM64, macOS 15: ✅ Best home node, lowest power, 5+ chains
-
Mac Mini M2 / M2 Pro (2023): ARM64, macOS 13+, ✅ Excellent, strong used-market value
-
Mac Mini M1 (2020): ARM64, macOS 13+, ✅ Good, 16 GB config required for multi-chain
-
Mac Mini Intel (2018): x86_64, macOS 12+, ⚠ Works, not recommended, higher power draw
-
MacBook Pro / Air M1–M4: ARM64, macOS 13+, ✅ Good, keep plugged in, disable sleep
-
iMac M1 / M3 / M4: ARM64, macOS 13+, ✅ Good, 16 GB+ RAM config
-
Mac Studio / Mac Pro (M2+): ARM64, macOS 14+, ✅ Excellent production / multi-instance node
-
Windows 11 Desktop or Laptop: x86_64, WSL2 + Docker Desktop, ✅ Fully supported
-
Windows 10 Desktop or Laptop: x86_64, WSL2 (v2004+), ✅ Supported
-
Ubuntu / Debian / Pop!_OS Desktop: x86_64 or ARM64: ✅ Reference Linux platform
-
Raspberry Pi 5 (8 GB): ARM64, Raspberry Pi OS 64-bit, ✅ Light single-chain node
-
VPS / Cloud Server: x86_64 or ARM64, Ubuntu 22.04: ✅ Production standard, highest uptime
-
RandProofBox (Phase 4): ARM64, RandProof OS, ✅ Plug-and-play, self-configuring
30.1 Prerequisites
30.1.1 Hardware Requirements
Minimum (single-chain EVM node):
-
CPU: 2 vCPU / 2 cores (x86_64 or ARM64)
-
RAM: 4 GB
-
Storage: 20 GB SSD
-
Bandwidth: 10 Mbps sustained / 100 GB/month
-
OS: Ubuntu 22.04 LTS (recommended) | Debian 12 | macOS 13+ | Windows 11 with WSL2
Recommended (multi-chain, 5+ chains):
-
CPU: 4 vCPU / 4 cores
-
RAM: 8 GB
-
Storage: 50 GB SSD
-
Bandwidth: 50 Mbps / 500 GB/month
-
Uptime SLA: 95%+ (80% minimum enforced by slashing policy)
RandProofBox (purpose-built hardware, Phase 4):
-
NVIDIA Jetson Orin 8GB / Raspberry Pi 5 8GB
-
Pre-flashed with RandProof OS image
-
Plug-and-play: power on, enter keys, stake automatically
30.1.2 Required Open Ports (Firewall Rules)
Open the following inbound ports on your VPS or firewall:
# UFW (Ubuntu)
sudo ufw allow 22/tcp # SSH
sudo ufw allow from 127.0.0.1 to any port 3000 proto tcp # RandProof Keeper API (localhost only, AV-35)
sudo ufw limit 4001/tcp # P2P gossip network (rate-limited, AV-36)
sudo ufw allow 9090/tcp # Prometheus metrics (restrict to monitoring server)
sudo ufw enable
# Verify
sudo ufw status verbose30.1.3 Software Dependencies
# 1. Update system
sudo apt update && sudo apt upgrade -y
# 2. Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
docker --version # confirm: Docker 24.x+
# 3. Install Docker Compose
sudo apt install -y docker-compose-plugin
docker compose version # confirm: v2.x+
# 4. Install Node.js 20 LTS (for CLI tools)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node --version # confirm: v20.x
# 5. Install RandProof CLI
npm install -g @randproof/cli
randproof --version # confirm: 1.0.x
# 6. Install Git
sudo apt install -y git curl jq wget30.2 Repository & Initial Setup
# Clone the RandProof Keeper repository
git clone https://github.com/randproof-network/randproofkeeper.git
cd randproofkeeper
# Verify repository integrity
git verify-commit HEAD # GPG-signed releases only
# Pull latest stable release
git checkout $(git describe --tags --abbrev=0)
# Pull Docker image (alternative to source build)
docker pull randproof/randproofkeeper:latest\
# AV-43: Verify image signature: cosign verify --key cosign.pub randproof/randproofkeeper:latest
docker pull randproof/randproofkeeper:v1.0.0 # pin to specific version30.3 BLS Key Generation (CRITICAL: Do Not Skip)
Each keeper node requires two keypairs: an EVM wallet keypair (for on-chain staking and fee receipt) and a BLS keypair (for threshold signature participation). The BLS key share must never be exposed or shared.
⛔ **WARNING:** Never reuse a BLS key across multiple nodes. Never store the BLS private key in plaintext. Never commit the .env file to version control. Loss of the BLS key means loss of your stake.30.3.1 Generate EVM Wallet
# Option A: Using RandProof CLI (recommended)
randproof wallet generate
# Output:
# Address: 0xYourKeeperAddress
# Private Key: 0xYourPrivateKey <- store in password manager
# Mnemonic: word1 word2 ... <- store offline, never digitally
# Option B: Using Cast (Foundry)
cast wallet new
# Fund the wallet with ETH for gas (Base mainnet: 0.005 ETH minimum)
# Send stake asset PLUS gas funds to this address before proceeding30.3.2 Generate BLS Key Share
# Generate BLS keypair for threshold signing (BN254 curve for EVM chains)
randproof bls generate --curve bn254 --output ./keys/bls-keystore.json
# You will be prompted for an encryption password:
# Enter BLS keystore password: ************
# Confirm password: ************
# Output:
# BLS Public Key: 0x1a2b3c... (register this on-chain)
# Keystore saved: ./keys/bls-keystore.json (encrypted)
# For non-EVM chains (BLS12-381 for Polkadot/Quicknet verification)
randproof bls generate --curve bls12-381 --output ./keys/bls-381-keystore.json
# Verify key integrity
randproof bls verify ./keys/bls-keystore.json
# Back up immediately (encrypted backup to secure location)
cp ./keys/bls-keystore.json ~/secure-backup/bls-keystore-$(date +%Y%m%d).json
⚠ ***NOTE:** Store your BLS keystore password in a separate location from the keystore file. A password manager (1Password, Bitwarden) is strongly recommended. The keystore file alone cannot be used without the password.*30.4 Complete .env Configuration Reference
Copy the template and fill in all required values:
cp .env.example .env
nano .env # or: vim .envFull .env reference (all variables):
# ═══════════════════════════════════════════════════════
# RANDPROOF KEEPER NODE — COMPLETE .env REFERENCE v1.0
# ═══════════════════════════════════════════════════════
# ── IDENTITY ─────────────────────────────────────────
KEEPER_PRIVATE_KEY=0x<your_evm_wallet_private_key>
KEEPER_ADDRESS=0x<your_evm_wallet_address>
BLS_KEYSTORE_PATH=./keys/bls-keystore.json
BLS_KEYSTORE_PASSWORD=<your_bls_keystore_password>
NODE_NAME=randproof-keeper-01 # human-readable label
NODE_REGION=us-east-1 # geographic region (for decentralization tracking)
# ── CHAIN SELECTION ──────────────────────────────────
# Comma-separated list. Start with 1-2 chains, add more after testing.
CHAINS=base,optimism,arbitrum
# Full list: base,optimism,arbitrum,linea,ethereum,polygon,worldchain,
# bnb,avalanche,moonbeam,solana,ton,cardano,sui,polkadot,bittensor,hyperevm
# ── EVM RPC ENDPOINTS (WebSocket required for event listening) ──
BASE_RPC=wss://base-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
BASE_RPC_HTTP=https://base-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
OPTIMISM_RPC=wss://opt-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
OPTIMISM_RPC_HTTP=https://opt-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
ARBITRUM_RPC=wss://arb-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
ARBITRUM_RPC_HTTP=https://arb-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
LINEA_RPC=wss://linea-mainnet.infura.io/ws/v3/<YOUR_INFURA_KEY>
LINEA_RPC_HTTP=https://linea-mainnet.infura.io/v3/<YOUR_INFURA_KEY>
ETHEREUM_RPC=wss://eth-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
ETHEREUM_RPC_HTTP=https://eth-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
POLYGON_RPC=wss://polygon-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
BNB_RPC=wss://bsc-ws-node.nariox.org:443
AVALANCHE_RPC=wss://api.avax.network/ext/bc/C/ws
MOONBEAM_RPC=wss://wss.api.moonbeam.network
HYPEREVM_RPC=wss://rpc.hyperliquid.xyz/evm
WORLDCHAIN_RPC=wss://worldchain-mainnet.g.alchemy.com/v2/<YOUR_ALCHEMY_KEY>
# ── NON-EVM RPC ENDPOINTS ───────────────────────────
SOLANA_RPC=wss://api.mainnet-beta.solana.com
SOLANA_RPC_HTTP=https://mainnet.helius-rpc.com/?api-key=<YOUR_HELIUS_KEY>
SOLANA_KEEPER_KEYPAIR=./keys/solana-keypair.json
TON_RPC=https://toncenter.com/api/v2/
TON_API_KEY=<your_toncenter_api_key>
TON_KEEPER_MNEMONIC=word1 word2 word3 ... word24
CARDANO_NODE_HOST=cardano-mainnet.blockfrost.io
CARDANO_BLOCKFROST_KEY=<your_blockfrost_key>
CARDANO_PAYMENT_SIGNING_KEY=./keys/cardano.skey
SUI_RPC=https://fullnode.mainnet.sui.io:443
SUI_KEEPER_KEYPAIR=./keys/sui-keypair.json
POLKADOT_RPC=wss://rpc.polkadot.io
POLKADOT_KEEPER_MNEMONIC=<polkadot_sr25519_mnemonic>
BITTENSOR_RPC=wss://entrypoint-finney.opentensor.ai:443
# ── ENTROPY SOURCE API KEYS ─────────────────────────
# DRAND (no key required - public)
DRAND_HTTP_ENDPOINT=https://api.drand.sh
DRAND_CHAIN_HASH=8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce
DRAND_QUICKNET_CHAIN_HASH=52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971
# NIST Randomness Beacon (no key required - public)
NIST_BEACON_ENDPOINT=https://beacon.nist.gov/beacon/2.0/pulse/last
NIST_MAX_STALENESS_SECONDS=65
# ANU QRNG (sole quantum entropy source)
ANU_QRNG_ENDPOINT=https://qrng.anu.edu.au/API/jsonI.php
ANU_MAX_STALENESS_SECONDS=30
# AV-54 / v0.57: ANU QRNG has no acceptable fallback provider. If ANU QRNG is stale or unavailable, its weight is redistributed automatically. Do not configure a substitute source.
# (HotBits variables removed in v0.57 -- the retired HotBits service is not an acceptable entropy source; see Section 3.3, Source 9.)
# Tor Consensus (no key required - public)
TOR_CONSENSUS_ENDPOINT=https://torproject.org/tor/status-all.json
TOR_MAX_STALENESS_SECONDS=3900
# Bitcoin Merkle Root (via public RPC or block explorer API)
BITCOIN_RPC_ENDPOINT=https://blockstream.info/api
BITCOIN_CONFIRMATIONS_REQUIRED=2
# Multi-Chain Block Hashes (list of non-EVM chains to include)
MULTICHAIN_HASH_SOURCES=solana,cardano,polkadot
# ── STAKING CONFIGURATION ───────────────────────────
MIN_STAKE_ETH=0.005 # Base/OP/ARB/Linea/Polygon/WorldChain
MIN_STAKE_SOL=0.5 # Solana
MIN_STAKE_SUI=5 # Sui
MIN_STAKE_TON=10 # TON
MIN_STAKE_ADA=50 # Cardano
MIN_STAKE_BNB=0.1 # BNB Chain
MIN_STAKE_AVAX=1 # Avalanche
MIN_STAKE_GLMR=0.5 # Moonbeam
MIN_STAKE_DOT=5 # Polkadot (5 DOT minimum)
MIN_STAKE_ETH_MAINNET=0.01 # Ethereum mainnet
# ── KEEPER BEHAVIOR ────────────────────────────────
MIN_UPTIME_PERCENT=80 # below this = slashing risk
MAX_FULFILLMENT_LATENCY_MS=5000 # flag events taking longer than 5s
GOSSIP_PEERS=11 # genesis: n-1 (full mesh); Phase 2+: 5+ (deterministic from registry)
BLS_THRESHOLD=3 # minimum signatures to aggregate (t)
BLS_COMMITTEE_SIZE=12 # total keepers in signing committee (n)
FULFILL_RETRY_ATTEMPTS=3 # retry failed fulfillments
FULFILL_RETRY_DELAY_MS=2000 # wait between retries
# ── FEE ASSET PREFERENCE ───────────────────────────
FEE_ASSET=usdc # options: usdc | native
# If usdc: fees received in USDC via CCTP V2 (EVM chains) or native USDC
# If native: fees received in chain native asset
# ── MONITORING & ALERTING ──────────────────────────
METRICS_PORT=9090 # Prometheus scrape port
HEALTH_PORT=3000 # HTTP health check port
LOG_LEVEL=info # debug | info | warn | error
LOG_FORMAT=json # json | pretty
ALERT_EMAIL=you@yourdomain.com # email for critical alerts
ALERT_WEBHOOK=https://hooks.slack.com/... # Slack/Discord webhook
PAGERDUTY_INTEGRATION_KEY=<key> # optional: PagerDuty for 24/7 on-call
# ── DATABASE (for local event history) ─────────────
DB_TYPE=sqlite # sqlite | postgres
DB_PATH=./data/keeper.db # sqlite path
# DB_HOST=localhost # postgres only
# DB_PORT=5432 # postgres only
# DB_NAME=randproof # postgres only
# DB_USER=keeper # postgres only
# DB_PASSWORD=<password> # postgres only30.5 On-Chain Registration & Staking
Every keeper must register its address and BLS public key on-chain before it can participate in fulfillment. Registration happens per chain.
30.5.1 EVM Chains (Base, OP, ARB, Linea, Ethereum, Polygon, BNB, AVAX, Moonbeam, WorldChain, HyperEVM)
# ── Step 1: Get your BLS public key ────────────────────────────
randproof bls pubkey ./keys/bls-keystore.json
# Output: 0x1a2b3c4d... (save this)
# ── Step 2: Register on Base (example) ─────────────────────────
randproof keeper register \
--chain base \
--keeper-address 0xYourKeeperAddress \
--bls-pubkey 0xYourBLSPublicKey \
--private-key 0xYourPrivateKey
# Output:
# Transaction: 0xabc123... (KeeperCoordinator.registerKeeper())
# Gas used: ~85,000
# Block: 12345678
# Status: ✓ Registered
# ── Step 3: Stake minimum ETH on Base ──────────────────────────
randproof keeper stake \
--chain base \
--amount 0.005 \
--asset eth \
--private-key 0xYourPrivateKey
# Output:
# Staking 0.005 ETH on Base...
# Transaction: 0xdef456...
# Stake confirmed. Node active.
# ── Step 4: Verify registration ─────────────────────────────────
randproof keeper status --chain base --address 0xYourKeeperAddress
# Output:
# Chain: Base
# Status: ACTIVE
# Stake: 0.005 ETH (~$20)
# BLS Key: 0x1a2b3c...
# Events fulfilled: 0
# Uptime: 100%
# Earnings: 0.00 USDC
# ── Repeat for each chain you are joining ───────────────────────
randproof keeper register --chain optimism --bls-pubkey 0x... --keystore ./keys/evm-keystore.json --password-file ./keys/.password
randproof keeper stake --chain optimism --amount 0.005 --asset eth --keystore ./keys/evm-keystore.json --password-file ./keys/.password
randproof keeper register --chain arbitrum --bls-pubkey 0x... --keystore ./keys/evm-keystore.json --password-file ./keys/.password
randproof keeper stake --chain arbitrum --amount 0.005 --asset eth --keystore ./keys/evm-keystore.json --password-file ./keys/.password30.5.2 Solana
# Install Solana CLI if not present
sh -c "$(curl -sSfL https://release.solana.com/v1.18.0/install)"
export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
# Generate Solana keeper keypair
solana-keygen new --outfile ./keys/solana-keypair.json
solana-keygen pubkey ./keys/solana-keypair.json
# Fund with SOL (send 0.6 SOL: 0.5 stake + 0.1 gas buffer)
solana balance ./keys/solana-keypair.json --url mainnet-beta
# Register and stake via RandProof CLI
randproof keeper register --chain solana --keypair ./keys/solana-keypair.json
randproof keeper stake --chain solana --amount 0.5 --asset sol \
--keypair ./keys/solana-keypair.json30.5.3 Polkadot
# Install Polkadot.js CLI
npm install -g @polkadot/cli
# Generate sr25519 keypair for BABE VRF participation
subkey generate --scheme sr25519
# Save: Secret seed, Public key (SS58), Account ID
# Register via RandProof CLI (requires 5 DOT minimum)
randproof keeper register --chain polkadot \
--mnemonic "word1 word2 ... word12" \
--bls-pubkey 0xYourBLSPublicKey
randproof keeper stake --chain polkadot --amount 5 --asset dot \
--mnemonic "word1 word2 ... word12"30.5.4 TON
# TON registration uses mnemonic-based wallet
randproof keeper register --chain ton \
--mnemonic "word1 word2 ... word24" \
--bls-pubkey 0xYourBLSPublicKey
randproof keeper stake --chain ton --amount 10 --asset ton \
--mnemonic "word1 word2 ... word24"30.5.5 Cardano
# Generate Cardano payment keys
cardano-cli address key-gen \
--normal-key \
--signing-key-file ./keys/cardano.skey \
--verification-key-file ./keys/cardano.vkey
cardano-cli address build \
--payment-verification-key-file ./keys/cardano.vkey \
--mainnet
# Register and stake (50 ADA minimum)
randproof keeper register --chain cardano \
--signing-key ./keys/cardano.skey \
--bls-pubkey 0xYourBLSPublicKey
randproof keeper stake --chain cardano --amount 50 --asset ada \
--signing-key ./keys/cardano.skey30.6 Running the Keeper Node
30.6.1 Single Node (Docker: Recommended)
# Start the keeper node
docker run -d \
--name randproof-keeper \
--restart unless-stopped \
--env-file .env \
-v $(pwd)/keys:/app/keys \
-v $(pwd)/data:/app/data \
-p 127.0.0.1:3000:3000 \
-p 4001:4001 \
-p 9090:9090 \
randproof/randproofkeeper:latest
# Verify it started correctly
docker logs -f randproof-keeper
# Expected output:
# [RandProof Keeper v1.0.0] Starting...
# [Chain: Base] ✓ Connected (wss://base-mainnet...)
# [Chain: Optimism] ✓ Connected
# [Chain: Arbitrum] ✓ Connected
# [DRAND] ✓ Round 1234567 verified
# [NIST Beacon] ✓ Pulse 1234 verified
# [BLS] ✓ Key loaded: 0x1a2b3c...
# [Keeper] ✓ Registered on 3 chains
# [Keeper] ACTIVE — watching for PoFR events30.6.2 Multi-Chain Docker Compose (Recommended for 5+ Chains)
Create docker-compose.yml in your project directory:
version: "3.8"
services:
keeper:
image: randproof/randproofkeeper:latest
container_name: randproof-keeper
restart: unless-stopped
env_file: .env
volumes:
- ./keys:/app/keys:ro
- ./data:/app/data
ports:
- "127.0.0.1:3000:3000" # health API (localhost only, AV-35)
- "4001:4001" # P2P gossip
- "9090:9090" # Prometheus
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
# Optional: Local Prometheus + Grafana monitoring stack
prometheus:
image: prom/prometheus:latest
container_name: randproof-prometheus
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9091:9090"
restart: unless-stopped
grafana:
image: grafana/grafana:latest
container_name: randproof-grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=YOUR_GENERATED_PASSWORD
volumes:
- grafana-data:/var/lib/grafana
restart: unless-stopped
volumes:
grafana-data:
# Start full stack
docker compose up -d
# Check all containers are healthy
docker compose ps
# Follow keeper logs
docker compose logs -f keeper
# Stop gracefully
docker compose down30.6.3 Running from Source (Advanced)
cd randproofkeeper
npm install
npm run build
# Run in production mode
NODE_ENV=production npm start
# Or with PM2 for process management
npm install -g pm2
pm2 start dist/index.js --name randproof-keeper
pm2 save
pm2 startup # auto-restart on reboot30.7 Verifying Node Operation
# ── Health check ──────────────────────────────────────────────
curl http://localhost:3000/health
# Expected:
# {
# "status": "healthy",
# "version": "1.0.0",
# "uptime_seconds": 3600,
# "chains": {
# "base": { "connected": true, "blockNumber": 18234567, "pendingEvents": 0 },
# "optimism": { "connected": true, "blockNumber": 45678901, "pendingEvents": 0 },
# "arbitrum": { "connected": true, "blockNumber": 234567890, "pendingEvents": 0 }
# },
# "entropy": {
# "drand": { "lastRound": 1234567, "latency_ms": 45, "status": "ok" },
# "nist": { "lastPulse": 1234, "age_seconds": 30, "status": "ok" },
# "anu": { "latency_ms": 120, "status": "ok" },
# "tor": { "age_seconds": 1800, "status": "ok" }
# },
# "bls": { "keyLoaded": true, "publicKey": "0x1a2b3c..." },
# "stake": {
# "base": { "amount": "0.005 ETH", "status": "active" },
# "optimism": { "amount": "0.005 ETH", "status": "active" }
# }
# }
# ── Live event log ────────────────────────────────────────────
docker logs -f randproof-keeper 2>&1 | grep -E "FULFILL|ERROR|WARN|ENTROPY"
# ── CLI node status ───────────────────────────────────────────
randproof keeper status --address 0xYourKeeperAddress --all-chains
# ── Check earnings ────────────────────────────────────────────
randproof keeper earnings --address 0xYourKeeperAddress --period 30d
# Output:
# Period: Last 30 days
# Events fulfilled: 1,247
# Total earned: $18.42 USDC
# Avg per event: $0.0148
# Uptime: 97.3%30.8 Withdrawing Earnings & Managing Stake
# ── Withdraw accumulated earnings ────────────────────────────
randproof keeper withdraw-earnings \
--chain base \
--private-key 0xYourPrivateKey
# Transfers pending USDC balance to your keeper address
# ── Top up stake (increase to earn more) ─────────────────────
randproof keeper stake \
--chain base \
--amount 0.01 \
--asset eth \
--private-key 0xYourPrivateKey
# ── Unstake (initiates unbonding period) ─────────────────────
randproof keeper unstake \
--chain base \
--amount 0.005 \
--private-key 0xYourPrivateKey
# Note: unbonding period is 7 days. Node remains active during unbonding.
# Unstaking below minimum moves node to INACTIVE status.
# ── Claim unstaked funds (after unbonding period) ─────────────
randproof keeper claim \
--chain base \
--private-key 0xYourPrivateKey
# ── Rotate BLS key (recommended every 90 days) ───────────────
randproof bls generate --curve bn254 --output ./keys/bls-keystore-new.json
randproof keeper rotate-bls \
--chain base \
--new-keystore ./keys/bls-keystore-new.json \
--private-key 0xYourPrivateKey
# Old key remains active for 24h to complete in-flight signatures30.9 Monitoring & Alerting Setup
30.9.1 Prometheus Configuration
Create monitoring/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "randproof-keeper"
static_configs:
- targets: ["keeper:9090"]
metrics_path: /metrics
# Key metrics exposed by RandProof Keeper:
# randproof_events_fulfilled_total - counter: total events fulfilled
# randproof_events_pending - gauge: events awaiting fulfillment
# randproof_fulfillment_latency_ms - histogram: time to fulfill
# randproof_entropy_fetch_latency_ms - histogram: per-source fetch time
# randproof_entropy_staleness_seconds - gauge: age of each entropy source
# randproof_bls_signatures_produced_total - counter: BLS partial sigs produced
# randproof_stake_amount - gauge: current stake per chain
# randproof_earnings_usdc_total - counter: cumulative earnings
# randproof_uptime_percent - gauge: rolling 24h uptime
# randproof_rpc_connected - gauge: 1=connected 0=disconnected
# randproof_chain_block_number - gauge: latest block per chain30.9.2 Grafana Dashboard Import
# Import the official RandProof Grafana dashboard
# Navigate to: http://localhost:3001 (admin / YOUR_GENERATED_PASSWORD)
# Go to: Dashboards > Import > Upload JSON
# Dashboard JSON: https://github.com/randproof-network/grafana-dashboard
# Or import by ID:
# Grafana Dashboard ID: 19847 (RandProof Keeper Node Overview)
# Key panels in the dashboard:
# - Events Fulfilled (24h / 7d / 30d)
# - Fulfillment Latency P50 / P95 / P99
# - Entropy Source Health (all 15 sources)
# - Chain Connection Status (all active chains)
# - Stake Balance per Chain
# - Earnings Accumulation
# - Uptime % (with slashing threshold line at 80%)
# - Peer Count (gossip network)30.9.3 Alert Rules (Prometheus Alertmanager)
Create monitoring/alerts.yml:
groups:
- name: randproof-keeper-alerts
rules:
# CRITICAL: Node offline
- alert: KeeperNodeDown
expr: up{job="randproof-keeper"} == 0
for: 2m
labels: { severity: critical }
annotations:
summary: "RandProof keeper node is DOWN"
description: "Node has been unreachable for 2+ minutes. Slashing risk after 24h."
# CRITICAL: Chain RPC disconnected
- alert: ChainRPCDisconnected
expr: randproof_rpc_connected == 0
for: 5m
labels: { severity: critical }
annotations:
summary: "Chain RPC connection lost: {{ $labels.chain }}"
# HIGH: Entropy source stale
- alert: EntropySourceStale
expr: randproof_entropy_staleness_seconds{source="nist"} > 65
for: 1m
labels: { severity: high }
annotations:
summary: "NIST beacon data is stale (>65s)"
# HIGH: Uptime below threshold
- alert: UptimeLow
expr: randproof_uptime_percent < 85
for: 30m
labels: { severity: high }
annotations:
summary: "Keeper uptime {{ $value }}% — approaching slashing threshold (80%)"
# MEDIUM: High fulfillment latency
- alert: HighFulfillmentLatency
expr: histogram_quantile(0.95, randproof_fulfillment_latency_ms) > 8000
for: 10m
labels: { severity: medium }
annotations:
summary: "P95 fulfillment latency above 8 seconds"
# LOW: Stake balance below comfortable buffer
- alert: LowStakeBalance
expr: randproof_stake_amount < 0.006
labels: { severity: low }
annotations:
summary: "Stake on {{ $labels.chain }} near minimum threshold"30.10 Updates, Maintenance & Troubleshooting
30.10.1 Updating the Keeper Node
# Check current version
docker exec randproof-keeper randproof --version
# Pull latest image
docker pull randproof/randproofkeeper:latest
# Rolling restart (zero-downtime update)
docker compose pull keeper
docker compose up -d --no-deps keeper
# Verify update
docker logs randproof-keeper | head -20
curl http://localhost:3000/health | jq .version30.10.2 Common Issues & Fixes
Issue: RPC WebSocket disconnects frequently
Fix: Switch to a dedicated RPC provider (Alchemy/Infura). Free tier RPCs have connection limits. Set BASE_RPC to a paid endpoint. Restart the container.
Issue: NIST beacon staleness alert firing
Fix: Check internet connectivity from the container: docker exec randproof-keeper curl https://beacon.nist.gov/beacon/2.0/pulse/last. If blocked, add a DNS override or use a NIST mirror.
Issue: BLS signing fails with "key not found"
Fix: Verify BLS_KEYSTORE_PATH is correctly mounted: docker exec randproof-keeper ls /app/keys. Ensure the keys volume mount is correct in docker-compose.yml.
Issue: Keeper shows INACTIVE on-chain
Fix: Run: randproof keeper status --chain base --address 0xYOUR. Check if stake fell below minimum (price change). Top up stake: randproof keeper stake --chain base --amount 0.005.
Issue: High fulfillment latency (>5s)
Fix: Check entropy source latency in the health endpoint. ANU QRNG can be slow; if it is stale or unavailable, its weight is redistributed automatically (AV-54) -- do not configure a substitute endpoint. Check Bitcoin RPC confirmations setting (reduce to 1 for faster response).
Issue: Docker container keeps restarting
Fix: Check logs: docker logs randproof-keeper --tail 50. Most common cause: invalid KEEPER_PRIVATE_KEY format (must start with 0x) or missing BLS keystore password.
30.10.3 Emergency: Node Slashed
# If slashed, the node is temporarily banned. Steps to recover:
# 1. Check slash reason
randproof keeper slash-history --address 0xYourKeeperAddress
# 2. Fix the underlying issue (double-sign = disable duplicate processes,
# offline = fix RPC, invalid sig = check BLS key integrity)
# 3. Re-stake to minimum (previous stake was partially/fully slashed)
randproof keeper stake --chain base --amount 0.005 --asset eth --keystore ./keys/evm-keystore.json --password-file ./keys/.password
# 4. Request reinstatement (after temporary ban expires)
randproof keeper reinstate --chain base --keystore ./keys/evm-keystore.json --password-file ./keys/.password
# 5. Monitor closely for 24h after reinstatement
docker compose logs -f keeper | grep -E "SLASH|WARN|ERROR"30.11 Client Contract Integration (For Developers Using RandProof)
If you are building a protocol that consumes RandProof (e.g., a raffle, NFT mint, or DAO tool), integrate the following interface:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IKeeperCoordinator {
// Request a PoFR event. Returns eventId.
// fee must be sent with the call (msg.value or ERC20 approve first)
// Request a PoFR event. Returns eventId.\
// fee must be >= MIN_FEE (spam protection, AV-27 fix)\
// Rate limited: 1 request per 10 blocks per address\
function requestRandomness(bytes32 userSeed) external payable returns (bytes32 eventId);
// Fulfill with verified entropy inputs (called by keepers)
function fulfill(bytes32 eventId, bytes32[15] memory entropyInputs, uint256 entropyBlock, bytes memory aggregatedSig, address[] memory signers) external;
// Submit entropy attestation (Layer 2: multi-keeper attestation)
function submitEntropy(bytes32 eventId, uint8 sourceId, bytes32 value, bytes memory proof) external;
// Submit fraud proof during challenge window (Layer 3)
function submitFraudProof(bytes32 eventId, uint8 sourceId, bytes memory proof) external;
// Get verified entropy inputs for a fulfilled event
function getEntropyInputs(bytes32 eventId) external view returns (bytes32[15] memory);
// Check if an event has been fulfilled
function isFulfilled(bytes32 eventId) external view returns (bool);
// Get the RandProof artifact for a fulfilled event
function getRandProof(bytes32 eventId) external view returns (
bytes32 seed,
bytes aggregatedBLSSig,
address[] memory signers,
uint256 fulfillBlock,
uint256 fulfillTimestamp
);
}
contract MyRaffle is IRandProofConsumer {
IKeeperCoordinator public coordinator;
mapping(bytes32 => uint256) public pendingDraws; // eventId => totalEntrants
constructor(address _coordinator) {
coordinator = IKeeperCoordinator(_coordinator);
}
// Step 1: Request randomness when draw is ready
function requestDraw(uint256 totalEntrants) external payable {
bytes32 userSeed = keccak256(abi.encode(block.number, msg.sender, totalEntrants));
bytes32 eventId = coordinator.requestRandomness{value: msg.value}(userSeed);
pendingDraws[eventId] = totalEntrants;
emit DrawRequested(eventId, totalEntrants);
}
// Step 2: Receive the fulfilled randomness (called by KeeperCoordinator)
function fulfillRandomness(bytes32 eventId, bytes32 seed) external override {
require(msg.sender == address(coordinator), "Only coordinator");
uint256 totalEntrants = pendingDraws[eventId];
require(totalEntrants > 0, "Unknown event");
uint256 winnerIndex = uint256(seed) % totalEntrants;
emit WinnerSelected(eventId, winnerIndex);
delete pendingDraws[eventId];
}
event DrawRequested(bytes32 indexed eventId, uint256 totalEntrants);
event WinnerSelected(bytes32 indexed eventId, uint256 winnerIndex);
}KeeperCoordinator contract addresses (current deployments):
-
Base Mainnet: 0xRPKC...Base (deploy address TBD, Phase 1 Q3 2026)
-
OP Mainnet: 0xRPKC...OP
-
Arbitrum One: 0xRPKC...ARB
-
Linea: 0xRPKC...Linea
-
Ethereum Mainnet: 0xRPKC...ETH
-
Polygon: 0xRPKC...MATIC
-
BNB Chain: 0xRPKC...BNB
-
Avalanche C-Chain: 0xRPKC...AVAX
-
Moonbeam: 0xRPKC...GLMR
-
World Chain: 0xRPKC...WLD
-
HyperEVM: 0xRPKC...HYPE
⚠ NOTE: Contract addresses will be published at randproof.network/contracts upon Phase 1 mainnet deployment (Q3 2026). Always verify addresses against the official repository before use.
30.12 Support & Resources
-
Documentation: https://docs.randproof.network
-
GitHub: https://github.com/randproof-network/randproofkeeper
-
Node Operator Discord: https://discord.gg/randproof (#node-operators channel)
-
Status Page: https://status.randproof.network
-
Contract Addresses: https://randproof.network/contracts
-
Grafana Dashboard: https://github.com/randproof-network/grafana-dashboard
-
Bug Reports: https://github.com/randproof-network/randproofkeeper/issues
-
Enterprise Integration: info@randproof.network
-
Node Operator Inquiries: nodes@randproof.network
29. Technical Audit Report
Audit Date: June 18, 2026 | Scope: Whitepaper v0.44 | Auditor: Internal Technical Review
31. RandProof Mobile Application: iOS & Android
The RandProof Mobile App is the primary interface for node operators, stakers, mobile light node participants, integrators, and end users. It serves f