HESTIAdocs

Self-hosting

Run your own console + backend — environment variables, the API routes, proving artifacts, the relayer, and deployment notes.


The console is a Next.js app you can run yourself. Its frontend is the SDK in the browser; its backend (/api/v1/*) wraps the open-source route service. This page is what you need to stand it up.

Environment

The app reads two sets of variables — public ones the browser uses, and server ones the backend uses. Copy .env.example to .env.local and fill them in.

bash
# ── server (backend API routes; consume @hestia/route) ──
HESTIA_CHAIN=baseSepolia                 # base | baseSepolia | anvil
HESTIA_RPC_URL=https://sepolia.base.org
HESTIA_POOL_ADDRESS=0x…
HESTIA_REGISTRY_ADDRESS=0x…
HESTIA_RELAYER_KEY=0x…                   # server-side relayer wallet — NEVER commit a real key

# ── public (browser): deployed addresses + chain ──
NEXT_PUBLIC_HESTIA_CHAIN=baseSepolia
NEXT_PUBLIC_HESTIA_RPC_URL=/api/rpc       # same-origin proxy — keeps the provider key server-side
NEXT_PUBLIC_HESTIA_POOL_ADDRESS=0x…
NEXT_PUBLIC_HESTIA_REGISTRY_ADDRESS=0x…
NEXT_PUBLIC_HESTIA_USDC_ADDRESS=0x036CbD53842c5426634e7929541eC2318f3dCF7e

Defaults, if unset, target a local chain (anvil at http://127.0.0.1:8545) with zero addresses — useful for wiring up against a local node before you have a deployment.

Keep the provider key off the client. NEXT_PUBLIC_* values are inlined into the browser bundle, so never put a keyed RPC URL there. The app ships a same-origin read proxy at app/api/rpc — set NEXT_PUBLIC_HESTIA_RPC_URL=/api/rpc and the browser's reads are forwarded to the server-side HESTIA_RPC_URL, which never leaves the server. The proxy allows only read methods; the wallet still broadcasts writes through the user's own provider.

Run it

bash
npm install
cp .env.example .env.local   # fill in addresses + relayer key
npm run dev                  # → http://localhost:3000/app

For production: npm run build then npm run start.

The backend routes

The /api/v1/* routes are thin wrappers over @hestia/route handlers, running on the Node runtime. They expose only public, indexed state plus the relayer:

RoutePurpose
GET /api/v1/healthindexer health
GET /api/v1/pool/stateroot, leaf count, tree depth
GET /api/v1/tree/proof?leaf=Merkle proof for a leaf
GET /api/v1/notes/scan?since=encrypted note blobs to trial-decrypt
GET /api/v1/association/status?root=is an association root approved
POST /api/v1/relaysubmit a client-built proof (relayer pays gas)

See the API routes reference for shapes. The backend keeps a single in-memory indexer instance reused across requests; the chain is the source of truth, so a restart simply re-syncs.

Proving artifacts

The browser fetches circuit wasm/zkey from /circuits. These files are large (a few MB

  • tens of MB per arity) and are not committed to the repo by default. For any deployment you must make them available, by one of:
  • committing them (Git LFS recommended) so they ship with the app,
  • fetching them at build time into public/circuits/, or
  • serving them from a CDN and pointing the artifact URLs there.

If they're missing, client-side proving 404s and send/unshield fail. (Reads, balance, and shield still work.)

The relayer

POST /api/v1/relay submits a proof with the server's HESTIA_RELAYER_KEY, so a withdrawal's destination has no gas history. Keep that key:

  • funded with gas on the target chain, and
  • secret — only in the environment, never in the repo.

The relayer can't alter your transaction (recipient, amount, and fee are bound into the proof), but it does pay gas, so treat it as an operational wallet.

Enabling spends — the association registry

The pool accepts a spend only if its associationRoot is approved on-chain (registry.isValidRoot(root)). Deposits never touch this gate, so shield works immediately — but send / unshield revert with InvalidAssociationRoot until the matching root is published. Two owner/ASP steps bring the registry online:

  1. Authorize an ASP — the registry owner calls setASP(asp, true).
  2. Publish the root — that ASP calls publishRoot(root, uri) for the current association set.

The Console screens its own deposits (labels 0..leafCount-1) as the set, so the root advances with every deposit. The repo ships an operator script that reproduces that exact set and publishes its root from the ASP key:

bash
node --env-file=.env.local scripts/publish-association-root.mjs

Re-run it after deposits to keep the current root approved. On the live Base-mainnet deployment the deployer is already authorized as the ASP, so operating the gate is just re-running the script (or wiring a small ASP service to do it automatically).

Deployment notes

  • Don't hardcode the port. next start honors the PORT the platform injects — leave it to the environment.
  • Persistence. v0.1 uses the in-memory store, which re-syncs on boot. If you wire the Prisma/Postgres store, run migrations at start time, not build time (build environments often can't reach the database).
  • Self-contained build. The app consumes the @hestia/* packages; once they're resolved (from the registry or vendored locally), the build needs no access to the protocol repo.

With your own console and backend, you control the indexer, the relayer, and the RPC — the trust-minimized story from the route service becomes no third party at all.