Infra

The whole thing runs on a deliberately small stack: a static front end and one edge function, with a database at the edge, plus an offline pipeline that builds the corpus when it is rebuilt.

Cloudflare

The site is a Cloudflare Pages project in advanced mode: a single _worker.js handles /api/* and everything else serves static assets. The worker runs at the edge, close to you, so a tap is fast. State lives in Cloudflare D1 (SQLite at the edge): the event corpus, the feedback log, the chrome-bandit impressions, and the logistic weights. There is no separate server; the worker reads and writes D1 directly. A service worker caches the app shell so it loads offline, with the cache name stamped by the deploy's git commit so a new release refreshes it.

This sits inside Cloudflare's free tier: roughly 100k worker requests/day and millions of D1 row reads/day, with no overage billing. Exceed the free limits and requests are throttled, not charged.

GitHub

Source lives in a private GitHub repository. The pipeline scripts, the worker, the schema, and the front end are all versioned; deploys are a single wrangler pages deploy.

Fireworks

Today the story text is the raw event headline from GDELT, stored as-is. A second, optional step is built but not yet switched on: an offline pass over Fireworks AI (an OpenAI-compatible inference endpoint) that filters out non-tragedies (film and book reviews leak into the disaster themes) and rewrites each kept headline into one calm factual sentence. It is designed to run offline, once when the corpus is built, never on the hot path, so a tap would still cost no model inference. Until it is enabled, none runs: the live cost of a tap is a database query, a logistic score, and an inverse-gap draw.

Why so small

Pre-generating the stories and pushing the learning into a tiny logistic model plus one exploration knob keeps the request path to a single edge function and one database. No GPU at request time, no external API on the hot path, no servers to keep alive.