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.
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 = 42 — 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.
# 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
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: 1g4 — 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.
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_msabove a few seconds means your RPC is struggling — fail over before you miss a signing round. - Ring participation. If
ringsits 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.
npm run build # static, no server runtime required
npx vercel deploy # or: wrangler pages deploy ./out