Skip to main content

Off-chain services

Three independent TypeScript services support a full deployment. They are optional for a demo (the web app runs chain-direct), but power richer history, charts, trailing APY, and keep rounds turning. Each has a /health endpoint and is Dockerized via docker/service.Dockerfile.

Keeper (keeper/)

A cron + tick worker — the only server-side signer. It can drive the round lifecycle but cannot touch user funds:

  • weekly start-round,
  • post-expiry settle-round,
  • devnet/testnet price relay into pyth-mock (since the demo uses the mock feed),
  • retries with backoff.
cd keeper
STACKS_NETWORK=testnet KEEPER_PRIVATE_KEY=<key from .testnet-deployer.json> \
PRICE_RELAY=true pnpm start

The keeper key is isolated in its own process/secret store and is rotatable via vault.set-keeper. (7 policy + loop tests.)

Indexer (indexer/)

A Chainhook webhook consumer that projects the vault's print events into Postgres:

  • idempotent by (tx_id, event_index),
  • reorg rollback — rebuilds projections from the canonical event log,
  • Stacks-API backfill for cold starts / resume.

Register the predicate indexer/chainhooks/vault-print.testnet.json (its contract_identifier is already set to the live vault) with your Chainhook node, pointed at the indexer's /chainhook/events endpoint. (5 acceptance tests: replay, duplicate no-op, rollback/re-apply, resume.)

API (api/)

A read-only Fastify REST service (zod-validated IO, rate-limited 300/min):

RouteReturns
/vaultcurrent vault state
/vault/historysnapshots over time
/roundsround list + status
/positions/:addra user's positions
/quotea live Black–Scholes quote
/pricecurrent BTC price
/tx/:txida transaction's status
/healthliveness

Set VITE_API_URL in the web app to this service's base URL to upgrade from chain-direct to API-backed data. (13 tests.)

Database (db/)

Drizzle schema + migrations + seed (events_raw, rounds, positions, snapshots, …). Run with:

pnpm --filter @bachelier/db migrate
pnpm --filter @bachelier/db seed

Running the whole stack

docker compose up postgres indexer api
# keeper drives the rounds:
pnpm --filter @bachelier/keeper dev

See docker-compose.yml and the repo README.md for the full env matrix.