Payouts are in dry-run until go-live. Draws and verify still use the real formula.

Architecture

System architecture and tech

Closing Bell is a Next.js site plus a Node/TypeScript backend (API, indexer, draw keeper, Telegram) on Postgres. Shared draw math lives in @closing-bell/fairness. Cloud backend deploy target in this repo is Amazon Lightsail in us-west-2.

Frontend

  • Next.js App Router marketing and docs site (this repo root).
  • Public pages include the ritual home, /docs, and /verify.
  • Hosting: AWS Amplify Hosting (GitHub → next build, custom domain closingbellonrh.com). Point NEXT_PUBLIC_API_BASE at the public Lightsail API URL. The site proxies that origin at /cb-api. See docs/AWS_HOSTING.md.
  • Launch knobs: NEXT_PUBLIC_TOKEN_ADDRESS, NEXT_PUBLIC_CHART_URL, NEXT_PUBLIC_DRY_RUN_PAYOUTS.

Backend

  • Node / TypeScript service under backend/.
  • Read API (examples): GET /health, /pot, /window/current, /odds, /ladder, /winners.
  • Indexer: on-chain buys/sells mint and burn tickets.
  • Draw keeper: bag lock, closing-bell-draw-v1, receipt, wipe, optional payout send.
  • Postgres for durable draws, winners, and ticket state.

Amazon Web Services

Deploy docs in the repo describe Lightsail in region us-west-2 (Oregon):

  • Lightsail Container Service named closing-bell-api (Nano scale in the guide).
  • Lightsail managed Postgres (example name closing-bell-db) wired via DATABASE_URL.
  • Public HTTP endpoint on port 8787 with health check on /health. That URL is the public API the site and Telegram bot talk to.
  • Image push via Lightsail container registry (or ECR as noted in ops docs). Env and secrets stay in the Lightsail deployment config, not in git.

Telegram bot

Same backend process can announce rings and answer commands when TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID are set. Without a token, announces log as [tg:dry].

  • Commands: /pot, /odds, /ladder, /next.
  • All commands reuse runtime / API math (no second odds formula).

Shared fairness package

packages/fairness exports closing-bell-draw-v1: odds helpers, capped weights, seed, weighted pick, snapshot hash. Backend keeper and /verify import the same package so receipt MATCH cannot drift from keeper math.

Local fixture mode vs cloud

ModeWhat runsTypical flags
Local fixtureDocker Compose API + Postgres. Fixture swaps drive tickets. Telegram optional.FIXTURE_MODE=true, DRY_RUN_PAYOUTS=true
Cloud (Lightsail)Same container image on Lightsail with managed Postgres. Public /health API.Same knobs until go-live. Set live RPC_URL / token addresses when indexing for real.
Go-live payoutsKeeper still picks via formula. GME transfer enabled only when dry-run is off and jackpot keying is configured.FIXTURE_MODE=false, DRY_RUN_PAYOUTS=false

Seed material (reference)

material = lowercase(blockhashAtSnapshot) + "|" + windowId + "|" + potBalance
seed     = keccak256(UTF-8 bytes of material)
cursor   = seed mod totalCappedWeight
# then walk entrants sorted by weight DESC, address ASC