The route service
The trust-minimized convenience layer — indexer, store, relayer, and API handlers — and exactly what it can and cannot do.
@hestia/route is the off-chain convenience layer: an indexer that rebuilds pool state
from chain events, a store that caches it, a relayer that submits proofs and pays gas,
and framework-agnostic handlers that expose it all over a small API. It is designed to be
trust-minimized — and to be something you can self-host or skip.
import { ROUTE_API_VERSION } from "@hestia/route"; // "v1"The store
The store is an in-memory index of pool state, reconstructed from events. The chain is the source of truth; the store is a cache that can be rebuilt by re-scanning.
class HestiaStore {
static create(depth?: number): Promise<HestiaStore>;
applyCommitment(commitment, leafIndex, encryptedNote, blockNumber): void;
applyNullifier(nullifier): void;
applyAspRoot(root, uri): void;
revokeAspRoot(root): void;
get root(): bigint;
get leafCount(): number;
proof(leafIndex): MerkleProof;
isNullified(nullifier): boolean;
isKnownAspRoot(root): boolean;
notesSince(fromBlock): IndexedNote[];
}The in-memory store is the v0.1 implementation. Production persistence is a Prisma/Postgres schema with the same shape, so the interface doesn't change when you move from a single process to a durable database.
The indexer
The indexer reads HestiaPool and AssociationSetRegistry events with viem and applies them
to a store — reconstructing the commitment tree, the nullifier set, and the approved ASP roots.
import { Indexer, HestiaStore } from "@hestia/route";
const store = await HestiaStore.create();
const indexer = new Indexer(publicClient, poolAddress, registryAddress, store);
await indexer.sync(); // fetch + apply events from the last indexed block to headCalling sync() repeatedly keeps the store current. Because the chain is authoritative, a
fresh process can rebuild the entire state from block zero — nothing is lost if the cache is
discarded.
The relayer
The relayer submits a client-built proof so a withdrawal's destination address has no
gas-funding history tying it to the depositor. It is reimbursed in-token via the
proof-bound feeAmount.
import { relayTransact1x2, relayTransact2x2 } from "@hestia/route";
const txHash = await relayTransact1x2(walletClient, poolAddress, {
proof: { a, b, c },
nullifiers: [nf],
outCommitments: [c0, c1],
data, // { root, associationRoot, withdrawAmount, token, recipient, feeAmount, relayer }
encryptedNotes: [blob0, blob1],
});The crucial property: the relayer cannot alter recipient, withdrawAmount, or
feeAmount — they are bound into the proof's public signals, so a dishonest relayer can only
submit your transaction faithfully or fail to submit it. It can never redirect funds or skim
more than the fee you authorized.
The handlers
Pure functions over a store, framework-agnostic — a thin Next.js app wraps them as the API routes:
poolState(store); // { root, leafCount, treeDepth, lastBlock }
treeProof(store, leafIndex); // { leaf, pathElements, pathIndices, root }
notesSince → notesScan(store, fromBlock); // encrypted blobs to trial-decrypt
associationStatus(store, root); // { root, valid, uri }
nullifierStatus(store, nullifier);// { nullifier, spent }
health(store); // { status, lastBlock, leafCount }There is also a standalone startServer (a node:http server) for running the route outside
any framework, and loadChainConfig / makePublicClient for resolving chain config from the
environment.
The trust model
The route is trust-minimized: it indexes public chain state and relays transactions. It cannot:
- steal — it never holds your keys or your notes' plaintext;
- forge — it cannot produce a valid proof; only you can; or
- deanonymize beyond what is already public — it sees commitments, nullifiers, and ciphertexts it cannot read.
The worst a malicious route can do is withhold service (refuse to relay, or serve stale data). And because every component is open source and the chain is authoritative, agents may self-host the indexer and relayer, or run the SDK against an RPC with no route at all. See self-hosting.
