Architecture
LinkBrain is one Cloudflare Worker (src/index.ts) on a Telegram webhook. There
is no long-running process: the Worker wakes per request, does its work, and
sleeps.
Request flow
Section titled “Request flow”Telegram ──webhook──▶ Cloudflare Worker (src/index.ts) │ ┌─────────────┼─────────────────┐ ▼ ▼ ▼ GitHub API HTMLRewriter Workers AI ──▶ D1 (SQLite + FTS5) (repos) (OG/title scrape) (summary + tags)The Worker validates the request, enriches the link, summarizes it with a Cloudflare-hosted model, stores it in D1, and replies over the Telegram Bot API.
The spine
Section titled “The spine”The file is divided into banner-comment sections, and the section order is the dependency direction — a lower section never calls a higher one.
| Section | Job |
|---------|-----|
| ENV / TYPES | The typed Env (bindings linkbrain (D1), AI, plus secrets) and minimal Telegram/row interfaces. |
| CONFIG | The model constant and secrets read from env. |
| DATABASE | All D1 access (saveLink, searchLinks, getRecent, countLinks). The only section that runs SQL. |
| ENRICHMENT | The outside world: GitHub API fetch, OG/HTML scraping via HTMLRewriter, and env.AI.run summaries. Catches its own failures and degrades — never throws into a handler. |
| HELPERS | Pure transforms: URL/note extraction, reply formatting, Markdown escaping, tag normalization. |
| HANDLERS | Orchestration: parse → enrich → store → reply. No SQL, no direct network. |
| ENTRY — fetch() | The webhook. Validates the secret header, acks immediately, processes in ctx.waitUntil(...), and routes commands vs. plain messages. |
Why a webhook (not polling)
Section titled “Why a webhook (not polling)”The original LinkBrain was a local Python bot that long-polled Telegram, so it
only worked while a process was running on a machine that was awake and online.
On Cloudflare there’s no process to babysit — Telegram delivers each update to
the Worker URL, which is always reachable. The Worker acks the webhook with
200 immediately and does the enrichment + save in the background
(ctx.waitUntil) so Telegram never retries or duplicates an update.
Enrichment degrades, never crashes
Section titled “Enrichment degrades, never crashes”Every enrichment function returns a safe fallback on failure: a failed GitHub
fetch returns null, a failed scrape returns the URL’s hostname, and a failed
or non-JSON AI response yields an empty summary. A link always saves, even if
the network or the model is having a bad day. The model used for summaries is
@cf/google/gemma-4-26b-a4b-it (Workers AI, JSON mode) — the same Gemma family
the original local build ran via Ollama.