Running a node locally
Start the chain, relayer, indexer, app, and explorer against a local devnet.
Everything runs natively against a local chain. The services and their default ports:
| Service | Port | What |
|---|---|---|
| Chain node (sequencer) | 8899 | Solana-compatible JSON-RPC |
| Relayer | 8911 | gasless unshield |
| App | 3010 | shield / transfer / unshield UI |
| Indexer | 8921 | event → Postgres + read API |
| Explorer | 3020 | read-only public view |
| Docs | 3030 | this site |
Chain node
# release binary (built for the local target)
sequencer --db ./chain-data/ledger --rpc-port 8899The node serves standard Solana RPC plus the pool/bridge extensions
(RPC reference). getHealth should return ok.
Relayer
relayer --keypair ./relayer-keypair.json --min-fee 2000000 \
--chain-rpc-port 8899 --port 8911Generates and persists a keypair on first run (gitignored — it holds a secret).
GET /info returns the relayer address and minimum fee; the app binds these into
the proof.
Indexer
cd indexer && npm install && npm start # reads node on 8899, serves 8921With no DATABASE_URL, the indexer uses an embedded Postgres locally. Set
DATABASE_URL (a Neon connection string) for production — the SQL is identical.
App & explorer
cd apps/app && npm install && npm run dev # http://localhost:3010
cd apps/explorer && npm install && npm run dev # http://localhost:3020To point the app's SDK at the indexer instead of the node directly, set
NEXT_PUBLIC_USE_INDEXER=1 (node-direct is the default).
Circuit artifacts (joinsplit.wasm, joinsplit_final.zkey) are served statically
by the app and cached in the browser on first proof.