Run a blockchain RPC node behind an authenticated gateway and earn by serving RPC requests through the Blockmachine network (Bittensor Subnet 19).
Supported chains:
| Chain | Tiers | Client |
|---|---|---|
tao (Bittensor subtensor) |
lite, archive |
subtensor |
eth (Ethereum mainnet) |
minimal, archive, proof |
reth (minimal, archive), erigon (proof) |
The install script picks the right stack based on your choice of chain and tier — see Getting started below.
Blockmachine is a decentralized marketplace for blockchain RPC infrastructure. Miners compete on price and quality to serve customer requests routed by the protocol gateway. You set your own price per Compute Unit (CU) and earn emissions proportional to the work you deliver.
Customer → Gateway → Your Node
↓
Logs & Verification
↓
Validators score you
↓
Emissions paid per CU served
- You run a subtensor node behind an nginx gateway that authenticates requests from the Blockmachine network
- The protocol gateway routes customer RPC requests to your node based on quality score and price
- Validators read gateway logs, verify correctness, and submit weights on-chain each epoch (~72 minutes)
- You earn emissions proportional to the CUs you served at your bid price
| Resource | Lite | Archive |
|---|---|---|
| Architecture | x86_64 | x86_64 |
| CPU | 4+ cores | 4+ cores |
| RAM | 16 GB+ | 16 GB+ |
| Disk | 100 GB SSD | 4+ TB SSD |
| Sync time | ~15 min (warp sync) | Days (or use snapshot) |
| Ports | 80, 443, 30333 | 80, 443, 30333 |
| Resource | Minimal (reth --minimal) |
Archive (reth default) | Proof (erigon) |
|---|---|---|---|
| Architecture | x86_64 | x86_64 | x86_64 |
| CPU | 4+ cores | 8+ cores recommended | 8+ cores recommended |
| RAM | 16 GB+ | 32 GB+ recommended | 32 GB+ recommended |
| Disk | ~300 GB NVMe | ~3 TB NVMe | ~500 GB NVMe |
| Sync time | ~1-2h from snapshot | ~24-48h from snapshot | ~1-2h from snapshot |
| Ports | 80, 443, 30303, 9000/tcp+udp, 9001/udp | 80, 443, 30303, 9000/tcp+udp, 9001/udp | 80, 443, 30303 |
| Serves | Recent eth_* + eth_subscribe over WS |
trace_*, debug_*, and eth_* (including eth_getLogs) at any depth |
Network policy: eth_getProof only (see note below) |
Choosing your tier:
- Minimal (reth
--minimal): general-purpose RPC, recent state only. Earns frometh_*at tip — recent-state queries and recent block/tx/receipt lookups. State pruned beyond ~33h, so this tier cannot answer historical state or proof queries. - Archive: general-purpose RPC, full archive. Earns from the bulk of customer ETH RPC:
trace_*,debug_*, andeth_*(includingeth_getLogs) against any historical block. Does not serveeth_getProof— proof traffic routes to Proof. - Proof: proof specialist. Earns from
eth_getProofonly — Erigon's responses differ from Reth's audit reference, so the network restricts this tier to proof traffic. Not recommended:eth_getProofis a small share of total ETH traffic, the disk cost is ~500 GB, and a specialist Erigon deployment is unlikely to pay back at current demand. Because external operators are not expected to find this worthwhile, Blockmachine runs an in-house Erigon as a backstop so proof availability is guaranteed regardless of miner supply. Revisit if Reth ships a wider proof window — at which point this tier may be retired entirely.
Each tier is matched to traffic based on its capabilities. Minimal earns from current-state queries; Archive earns from the bulk of customer ETH RPC including historical traces and logs; Proof serves eth_getProof only, a small slice the network covers in-house as a backstop. The gateway routes per-method based on the capabilities you advertise, so you only see traffic you can serve.
Any VPS or dedicated server with Docker will work for tao and ETH minimal. Archive tiers benefit substantially from NVMe storage. The install script handles all dependencies (Docker, git, certificates).
Run Reth. The network routes the bulk of ETH traffic — every eth_*
history method, every trace_*, every debug_* — to Reth backends, and
Reth is the audit reference, so its responses are what the gateway
validates against.
Erigon is supported only as a proof specialist (eth_getProof).
eth_getProof is a small fraction of total ETH traffic, and a
~500 GB Erigon deployment is unlikely to pay back at current demand.
Because external operators are not expected to find this worthwhile,
Blockmachine runs an in-house Erigon as a backstop to guarantee
proof availability — not to crowd anyone out. Unless you already
run Erigon for other reasons, pick Reth.
The CLI requires Python 3.10+ and can run anywhere — your laptop, a management server, or on the miner node itself. It's the control panel for registering nodes, managing secrets, and setting prices across your fleet without SSH-ing into each machine.
pip install blockmachineThe install script asks whether you're running on testnet or mainnet. For testnet, all bm CLI commands require the --testnet flag:
bm --testnet miner login
bm --testnet miner add --endpoint wss://... --alias my-node --secret '...' --price 0.01For mainnet (the default), use bm without the flag. The install script prints the correct commands for whichever network you choose.
Testnet requirement: You need a Bittensor hotkey registered as a miner on netuid 417 (the Blockmachine testnet subnet). Register your hotkey before running the install script:
btcli subnet register --netuid 417 --subtensor.network testbm miner login # mainnet
bm --testnet miner login # testnetThis uses a device authorization flow: the CLI displays a URL and code, then polls until you approve in a browser. The browser does not need to be on the same machine — you can run bm miner login on a headless server and open the URL on your phone or laptop. If a browser is available locally, it opens automatically.
SSH into your server and run the install script:
bash <(curl -sSL https://blockmachine.io/miner/install.sh)The script will:
- Install git and Docker if missing
- Clone this repository (or update it if re-running)
- Ask which chain to serve (
taooreth) - Ask whether you have a domain name or are using an IP address
- Set up TLS (auto-renewing Let's Encrypt for domains, or self-signed for IPs)
- Ask for tier (
lite/archivefor tao,minimal/archive/prooffor eth) - Offer to restore from a snapshot (tao archive only — eth archive tiers fetch from snapshots/torrents automatically)
- Generate a bearer token secret
- Start the gateway and the chain node(s)
- Print the registration commands to run from your local machine
At the end you'll see output like:
========================================
Miner is running!
========================================
Endpoint: wss://203.0.113.50
Chain: tao (lite)
Alias: tao-203-0-113-50
Secret: stored in /root/blockmachine-miner/.env
Now run these commands on your local machine:
bm miner login # or: bm --testnet miner login
bm miner add --endpoint wss://203.0.113.50 --alias tao-203-0-113-50 --secret '<secret from .env>' --price <usd-per-cu>
Using the CLI (wherever you installed it), register your node with the secret from the server's .env file (SECRET_V1):
bm miner add \
--endpoint wss://203.0.113.50 \
--alias my-node \
--secret '<SECRET_V1 from your server .env>' \
--price 0.01
# For testnet, prefix with --testnet:
bm --testnet miner add \
--endpoint wss://203.0.113.50 \
--alias my-node \
--secret '<SECRET_V1 from your server .env>' \
--price 0.01Or run bm miner add with no flags for interactive prompts.
What happens during registration:
- The CLI connects to the registry and creates your node entry
- For IP-based endpoints, the CLI fetches and pins your TLS certificate fingerprint (so the gateway can verify your identity via cert pinning)
- For domain endpoints, standard CA verification is used (no pinning needed)
- Your secret is hashed and stored — the gateway uses it to authenticate when routing requests to you
- Your price bid is recorded for the next epoch
Your subtensor node needs to sync before it can serve requests. A lite node warp-syncs in about 15 minutes. An archive node takes much longer (use a snapshot to speed this up).
Check sync progress (on your server):
docker compose logs -f nodeOnce synced, the gateway will start routing traffic to your node. Verify everything is working:
bm miner test <alias> # Test TLS, health, and authenticated RPC
bm miner show # Check status and last seen timestampbm miner test runs three checks: TLS handshake on port 443, health endpoint on port 80, and an authenticated system_health RPC call. You can also test before registering with bm miner test --endpoint <url> --secret '<secret>'.
Once traffic is flowing, check your node's performance:
bm miner metrics [alias] # Quality score, latency, success rateEach miner exposes authenticated Prometheus metrics through the same HTTPS gateway:
curl -fsS \
-H "Authorization: Bearer <SECRET_V1 from your server .env>" \
https://203.0.113.50/metricsPrometheus scrape example:
scrape_configs:
- job_name: blockmachine-miners
metrics_path: /metrics
scheme: https
authorization:
type: Bearer
credentials: <SECRET_V1 from your server .env>
static_configs:
- targets:
- 203.0.113.50Metrics are operator telemetry for visibility and alerting. They are authenticated and useful for debugging sync, peer, disk, TLS, and version rollout issues, but they are not proof that a miner is honestly serving work.
The endpoint includes Blockmachine-curated health metrics, host CPU/memory/load, node data volume usage, TLS certificate expiry, and the live deployed git SHA. It also appends the underlying client's native Prometheus output:
- tao — Subtensor metrics from the node's internal
:9615endpoint - eth minimal — Reth metrics from
:9101, plus a beacon-API sync probe against Lighthouse on:5052 - eth archive — Reth metrics from
:9101, plus a beacon-API sync probe against Lighthouse on:5052 - eth proof — Erigon metrics from
:6060/debug/metrics/prometheus
You set a price in USD per Compute Unit (CU). A CU represents the normalized computational cost of serving a specific RPC method. Different methods cost different amounts of CU (a simple balance query costs less than a transaction trace).
The gateway routes traffic based on a combination of quality score and price — cheaper miners with good quality get more traffic. You only earn on successful responses (HTTP 200 with a JSON-RPC result).
Set or update your price:
bm miner price set --price 0.01 # USD per CU, effective next epoch
bm miner price show # Current price
bm miner price history # Price historySelect "Let's Encrypt" during install. A certbot container handles issuance and auto-renewal every 12 hours. Requires a domain name with a DNS A record pointing to your server, and port 80 reachable for the ACME challenge.
Generated automatically during install. Valid for 10 years. The CLI pins the certificate fingerprint during bm miner add so the gateway can verify your identity. No renewal needed.
Place cert.pem and key.pem in the ssl/ directory before running the install script. Select "no" when prompted about Let's Encrypt.
Syncs via warp sync in ~15 minutes. Serves recent blocks and current state. Lower storage requirements (~100 GB). Suitable for most RPC methods.
Historical state queries for pruned blocks will return null — the gateway routes these to archive nodes when available.
Full block history from genesis. Requires 4+ TB disk and takes days to sync from scratch. To speed up initial sync, use a snapshot:
# On your local machine, get a snapshot URL
bm miner snapshot --type archive
# Paste the URL when prompted during install on your serverTo start an archive node:
docker compose -f docker-compose.yml -f docker-compose.archive.yml up -dReth in --minimal mode: keeps the last ~10,064 blocks (~33h) of state and history, prunes the rest. Disk footprint is around 300 GB on mainnet. The compose file bootstraps the datadir from Paradigm's official snapshot via reth download on first run, so the node reaches live tip in ~1-2h instead of days of genesis-sync over P2P. A Lighthouse beacon node runs alongside reth and drives it via the Engine API (checkpoint-synced from mainnet.checkpoint.sigp.io, ready in minutes). eth_getProof works against any block in the ~33h window; older blocks return an error.
To start a minimal-tier node manually:
docker compose -f docker-compose.eth-minimal.yml up -dReth in default (non-pruned) mode: full historical bodies, receipts, logs, headers, and state back to genesis. Disk footprint is around 2.6 TB on mainnet (allow ~3 TB headroom). The compose file bootstraps the datadir from Paradigm's official archive snapshot via reth download --archive on first run — same ~1-2h wall-clock benefit over genesis P2P sync. Same Lighthouse beacon sidecar as the Minimal tier.
This tier serves the bulk of customer ETH RPC traffic on the network: trace_*, debug_*, and eth_* (including eth_getLogs) against any historical block. The network routes eth_getProof to Proof backends — capability-based, not error-fallback — so Reth-archive operators don't generate proofs.
To start an archive node manually:
docker compose -f docker-compose.eth-archive.yml up -dErigon with --prune.mode=full --prune.distance=10064 --prune.distance.blocks=10064, plus Erigon's built-in Caplin consensus client (no separate Lighthouse service needed). Disk footprint is around 500 GB on mainnet. Serves eth_getProof and eth_getAccount at depths within the last ~10,064 blocks (~33h).
Network routing policy: Erigon's responses differ from Reth's audit reference, so the network restricts Erigon backends to eth_getProof requests. All other ETH methods route to Reth backends (Latest or Archive). Erigon operators today receive a narrow, high-value slice of traffic; the bulk of ETH volume goes to the Reth tiers. This policy will be revisited when validator-side Erigon support ships.
To start an proof node manually:
docker compose -f docker-compose.eth-proof.yml up -dIf you prefer to set things up yourself:
git clone https://github.com/taostat/blockmachine-miner.git
cd blockmachine-miner
# Generate a secret
SECRET=$(openssl rand -base64 32 | tr -d '=/+' | head -c 43)
# Create .env
cp .env.example .env
# Edit .env: set SECRET_V1=$SECRET, set DOMAIN if using Let's Encrypt
# Generate self-signed cert (skip if using Let's Encrypt or BYO cert)
IP=$(curl -s ifconfig.me)
mkdir -p ssl
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout ssl/key.pem -out ssl/cert.pem \
-subj "/CN=$IP" -addext "subjectAltName=IP:$IP"
# Start services — choose the stack for your chain and tier:
docker compose up -d # tao lite (default)
# docker compose -f docker-compose.yml \
# -f docker-compose.archive.yml up -d # tao archive
# docker compose -f docker-compose.eth-minimal.yml up -d # eth minimal (reth --minimal + lighthouse)
# docker compose -f docker-compose.eth-archive.yml up -d # eth archive (reth default + lighthouse)
# docker compose -f docker-compose.eth-proof.yml up -d # eth proof (erigon + caplin)
# For Let's Encrypt, add the TLS overlay (works for any chain):
# docker compose -f <chain-compose>.yml -f docker-compose.tls.yml up -dThen register from your local machine:
bm miner login
bm miner add --endpoint wss://$IP --alias my-node --secret "$SECRET" --price 0.01
# For testnet:
bm --testnet miner login
bm --testnet miner add --endpoint wss://$IP --alias my-node --secret "$SECRET" --price 0.01┌──────────────────────────────────────────────┐
│ Your Server │
│ │
│ ┌──────────┐ ┌───────────────────┐ │
│ │ nginx │ :443 │ subtensor node │ │
│ │ gateway │─────▶│ (lite/archive) │ │
│ │ │ :9944│ │ │
│ └──────────┘ └───────────────────┘ │
│ │ :80 (health + ACME) │ :30333 (p2p) │
│ │ │ │
│ │ /metrics │ │
│ ▼ │ │
│ ┌──────────┐ ┌────────┴─────────┐ │
│ │ metrics │─────▶│ node RPC + data │ │
│ │ │─────▶│ native :9615 │ │
│ │ │─────▶│ host /proc │ │
│ └──────────┘ └──────────────────┘ │
│ │
│ ┌──────────┐ │
│ │ certbot │ (optional) │
│ └──────────┘ │
└──────────────────────────────────────────────┘
- nginx gateway — Terminates TLS, authenticates requests via bearer token, proxies WebSocket and HTTP RPC to the chain node. Supports dual secrets for zero-downtime rotation. The eth gateway template routes by the
Upgradeheader so a single 443 endpoint serves both JSON-RPC andeth_subscribe. - chain node — Either a Bittensor subtensor (lite/archive), a reth execution client driven by Lighthouse over the Engine API (eth minimal with
--minimal, or eth archive in default mode), or an erigon archive with built-in Caplin CL (eth proof). - metrics — Exposes
/metricsinternally; nginx publishes it athttps://<endpoint>/metricswith the same bearer auth as RPC. - certbot — Optional. Auto-renews Let's Encrypt certificates. Only used with domain-based setups.
Rotate your bearer token without dropping any traffic:
-
Set the new secret as
nextin the registry:bm miner secret set --secret '<new-secret>'
-
Add the new secret to your server and restart the gateway:
# Edit .env: set SECRET_V2=<new-secret> docker compose up -d gatewayThe gateway now accepts both the old and new secret.
-
Promote the new secret to
active:bm miner secret promote
The gateway now sends requests using the new secret.
-
Remove the old secret from your server:
# Edit .env: move SECRET_V2 value to SECRET_V1, clear SECRET_V2 docker compose up -d gateway
Environment variables in .env:
| Variable | Default | Description |
|---|---|---|
SECRET_V1 |
(required) | Primary bearer token |
SECRET_V2 |
(empty) | Secondary token for zero-downtime rotation |
DOMAIN |
(empty) | Domain for Let's Encrypt auto-renewal |
CHAIN |
tao |
Chain selection: tao or eth |
METRICS_PORT |
9100 |
Internal metrics exporter port |
SSL_CERT_PATH |
/etc/nginx/ssl/cert.pem |
TLS certificate path in container |
SSL_KEY_PATH |
/etc/nginx/ssl/key.pem |
TLS key path in container |
BM_MINER_GIT_SHA |
unknown |
Deployed repository commit exposed in metrics |
BM_MINER_GIT_BRANCH |
unknown |
Deployed repository branch exposed in metrics |
Chain-specific:
| Variable | Default | Used by | Description |
|---|---|---|---|
BACKEND_PORT |
9944 |
tao | Subtensor node RPC port |
ETH_TIER |
(empty) | eth | minimal, archive, or proof |
EL_CLIENT |
(empty) | eth | reth (minimal, archive) or erigon (proof) |
BACKEND_HTTP_PORT |
8545 |
eth | EL HTTP JSON-RPC port |
BACKEND_WS_PORT |
8546 (reth) / 8545 (erigon) |
eth | EL WebSocket port |
CHECKPOINT_SYNC_URL |
https://mainnet.checkpoint.sigp.io |
eth minimal, eth archive | Lighthouse beacon checkpoint source |
bm miner show # Node status, endpoint, last seen
bm miner ls # List all your nodes
bm miner metrics [alias] # Quality score, latency, success rate
docker compose logs -f # Container logs
curl -sf http://localhost/health # Gateway health check
curl -fsS -H "Authorization: Bearer $SECRET_V1" https://<endpoint>/metricsPull the latest config and chain images, then restart. Use the same compose
file the install script started — bare docker compose defaults to
docker-compose.yml (the subtensor stack), which would replace an eth stack's
containers with subtensor ones because service names collide.
# Subtensor (lite or archive):
cd /root/blockmachine-miner && git pull && docker compose pull && docker compose up -d
# Ethereum minimal:
cd /root/blockmachine-miner && git pull \
&& docker compose -f docker-compose.eth-minimal.yml pull \
&& docker compose -f docker-compose.eth-minimal.yml up -d
# Ethereum archive:
cd /root/blockmachine-miner && git pull \
&& docker compose -f docker-compose.eth-archive.yml pull \
&& docker compose -f docker-compose.eth-archive.yml up -d
# Ethereum proof:
cd /root/blockmachine-miner && git pull \
&& docker compose -f docker-compose.eth-proof.yml pull \
&& docker compose -f docker-compose.eth-proof.yml up -dOr re-run the install script — it updates the repo automatically:
bash <(curl -sSL https://blockmachine.io/miner/install.sh)docker compose down # subtensor
docker compose -f docker-compose.eth-minimal.yml down # eth minimal
docker compose -f docker-compose.eth-archive.yml down # eth archive
docker compose -f docker-compose.eth-proof.yml down # eth proofNode data is persisted in a Docker volume (node_data). Restarting will resume from where it left off.
All commands below default to mainnet. Add --testnet after bm for testnet: bm --testnet miner ...
# Authentication
bm miner login # Authenticate with miner scopes
bm miner status # Check auth status
bm miner logout # Clear stored tokens
# Node management
bm miner add # Register a node (interactive)
bm miner use <alias> # Set active node for commands
bm miner ls # List all nodes
bm miner show [alias] # Show node details
bm miner update [alias] --endpoint ... # Change endpoint or alias
bm miner rm [alias] # Remove a node
# Secrets
bm miner secret set [alias] # Set bearer token secret
bm miner secret show [alias] # Show secret metadata
bm miner secret promote [alias] # Promote next secret to active
# Testing & metrics
bm miner test <alias> # Test TLS, health, and auth RPC
bm miner test --endpoint <url> --secret '<secret>' # Test before registering
bm miner metrics [alias] # Quality score, latency, success rate
# Pricing
bm miner price set [alias] --price ... # Set price per compute unit
bm miner price show [alias] # Show current price
bm miner price history [alias] # Show price history
# Snapshots
bm miner snapshot # Get snapshot download URLWhen [alias] is omitted, the active node (set via bm miner use) is used.
Port 80 or 443 in use: Stop the process using the port (sudo lsof -i :443) and try again.
Gateway unhealthy: The subtensor node takes time to sync. Check docker compose logs node for sync progress. A lite node should be healthy within 15-20 minutes.
Authentication errors: Run bm miner login to re-authenticate, then bm miner secret show to verify your secret is registered.
Node not receiving traffic: Check bm miner show — status should be active. If pending, the gateway hasn't connected yet (node may still be syncing). If unreachable, the gateway can't reach your endpoint — check firewall rules and that ports 443 is open.
TLS errors (self-signed): The gateway uses certificate pinning to verify your identity. If you regenerate your certificate, you need to re-register your node (bm miner rm then bm miner add) so the new fingerprint is captured.
TLS errors (Let's Encrypt): Check docker compose logs certbot. Ensure your domain's DNS A record points to your server and port 80 is reachable for the ACME challenge.
Node not syncing: Ensure port 30333 (p2p) is open for outbound connections to the Bittensor network. Check docker compose logs node for peer connection status.
Firewall setup: If using ufw, allow the required ports:
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw allow 30333/tcp