Skip to content
home/guides/operations

Run a relayer for nothing.

The conventional version of this stack — a synced zebrad, a lightwalletd, a dedicated Nitro RPC and a GPU prover — costs $150–300 a month. None of it is necessary. Here is the architecture that replaces every line item with zero.

What the money usually goes on

Zcash state sync
Hosted zebrad + lightwalletd → public gRPC endpoints
Robinhood Chain RPC
Dedicated node → the public Nitro RPC, with local rate limiting
Relayer compute
Always-on droplet → a laptop or a free-tier ARM instance
Proof hardware
GPU instance → CPU rapidsnark on a low-constraint circuit
API gateway
Managed cluster → no backend; the SDK calls public endpoints
Frontend hosting
Paid tier → Vercel / Cloudflare Pages / GitHub Pages free tier

1 — Read from public infrastructure

A relayer needs to observe two chains. Neither requires running a node. Zcash has mature public lightwalletd endpoints; Robinhood Chain publishes a Nitro RPC at https://rpc.robinhoodchain.com. Configure a fallback for each and apply local rate limiting so a public endpoint's bad day is a degradation rather than an outage.

relayer.toml
1# relayer.toml — no private infrastructure anywhere in this file.2 3[evm]4rpc          = "https://rpc.robinhoodchain.com"   # public Nitro RPC5chain_id     = 46636escrow       = "0x3F8a9Db62e109d93Eb69145Ac4E0f5D409941aB2"7poll_ms      = 250                                # ~2.5 blocks8 9[zcash]10lightwalletd = "https://zec.rocks:443"            # public community endpoint11fallback     = "https://mainnet.lightwalletd.com:9067"12pool         = "orchard"13 14[frost]15participant  = 316threshold    = 717total        = 1118key_package  = "/secrets/frost.key"               # generated during DKG19 20[prover]21mode         = "cpu"                              # rapidsnark, no GPU22threads      = 4
Public endpoints see your queries
A lightwalletd operator can observe which block ranges you request. For a relayer this leaks little — it is reading public settlement events anyway. For an end user building an attestation it matters more, which is why the SDK batches range requests and why running your own lightwalletd is still the strongest option if you can.

2 — Prove on the CPU

Recursive Groth16 proving is only expensive if the circuit is large. The attestation circuit is compiled with low-constraint templates against BN254, which brings proving inside the reach of an ordinary multi-core CPU via rapidsnark — a few seconds, no GPU, no instance.

For user-facing attestations, the work moves off the relayer entirely. The circuit is compiled to WASM and the proof is generated in the user's own browser, which means the marginal cost of an attestation to the operator is exactly zero. See viewing keys.

3 — Run it somewhere free

The relayer is a single Rust binary with a roughly 900MB working set. That fits comfortably on an always-free ARM instance (4 vCPU / 24GB is a common allocation) or on a development machine you already leave running.

terminal
# Build once (~4 min on 4 ARM vCPUs).
cargo build --release -p zrelay-relayer

# Run. Memory ceiling is ~900MB; it fits an always-free instance.
./target/release/zrelay-relayer --config relayer.toml

# Health check.
curl -s localhost:9944/health | jq
# { "evm_head": 18442901, "zcash_head": 2654917, "ring": "7/11", "lag_ms": 310 }

Docker

compose.yaml
services:
  relayer:
    image: zrelay/relayer:latest
    restart: unless-stopped
    volumes:
      - ./relayer.toml:/etc/zrelay/relayer.toml:ro
      - ./secrets:/secrets:ro
    ports:
      - "9944:9944"
    deploy:
      resources:
        limits:
          memory: 1g

4 — Fund gas from user surcharges

The one genuine cost is on-chain gas, and the escrow already covers it. Every shieldAsset call forwards a flat native-ETH surcharge straight to the relayer address, sized to cover the EVM submission and the Zcash miner fee. A relayer that processes traffic is funded by that traffic.

Keep a float
Surcharges arrive after the work, not before. Hold enough ETH to cover a few hours of submissions so a burst does not strand you mid-batch — and monitor the balance, because a relayer that runs dry silently stops earning while still appearing healthy.

Joining the ring

Reading events costs nothing and requires no permission. Signing does: ring membership means participating in distributed key generation and bonding $ZRL that can be slashed. The parameters:

Ring size
11 at mainnet launch
Threshold
7 of 11
Bond
Denominated in $ZRL, set by governance
Slashing
False attestation, double-sign, prolonged absence
Rewards
Gas surcharges + emissions tied to shielded volume
Key refresh
Per epoch; the group key is unchanged

What to monitor

  • Lag. lag_ms above a few seconds means your RPC is struggling — fail over before you miss a signing round.
  • Ring participation. If ring sits below the threshold, shields are stalling and users are heading for refunds.
  • Gas balance. The single most common operational failure, and the easiest to alert on.
  • Endpoint health. Track both lightwalletd endpoints independently; a fallback you have never exercised is not a fallback.

Hosting the frontend

This site is a static Next.js build with no server component doing anything privileged — it deploys to any free static host. The SDK talks directly to public RPCs from the user's browser, so there is no proxy to operate and no request log to protect.

deploy
npm run build          # static, no server runtime required
npx vercel deploy      # or: wrangler pages deploy ./out