Skip to content

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.

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 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. |

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.

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.