Brief
A reader for everything I follow: RSS, Atom and JSON feeds in one calm, newest-first queue, with the full article a tap away. No algorithm decides what I see. I built it, directed Codex to write it, and read with it every day.
- Made
- Written by
- Codex, directed by me
- Built with
- Cloudflare Workers, D1, Queues, Cron, Browser Run, TypeScript
- Status
- Live, in daily use
The question
Could one quiet reader replace the dozen tabs and apps I skim every morning? The constraint was that nothing re-ranks, recommends or hides anything: every source I choose, in the order it was published.
How it works
- One queue. RSS, Atom, JSON Feed and a few discovery sources merge into a single list, strictly newest first. Each item is labelled with its source.
- Reading rhythms. Every source belongs to one of four rhythms: Morning, During the day, Daily depth or Weekly. A rhythm filters the queue; it never re-sorts it.
- The full article. If a feed carries the whole text, Brief cleans and stores it straight away. Otherwise it fetches the linked page in the background, three at a time, keeps only the editorial body, and builds an "In this article" outline from its headings. A few pages only render their text with JavaScript; for those it falls back to a headless browser.
- One story, once. Articles are matched on their canonical URL, with fragments and tracking parameters such as
utm_*,fbclidandgclidremoved. A story that appears in several feeds is stored once, with every source kept. - Sync. A cron job runs every 15 minutes, and the app also checks when it opens and on demand. Each source syncs independently, so one broken feed never blocks the rest. Background work runs on a queue with retries and a dead-letter queue.
- Reading. Card, list, headline and grid views, unread and saved filters,
/to search,source:TechCrunch fundingto search within one source, andEscto go back. It installs as a phone app; the service worker caches only the reading shell, never the API.
Safety
A server that fetches any URL a feed hands it is an open door unless it's careful. Brief accepts only public HTTP(S) addresses. It blocks localhost, private networks and credential-bearing URLs, re-checks every redirect hop, caps response time and size, and sanitises all HTML before it's stored or shown.
How it evolved
It started in August as a local app with no dependencies: Python, SQLite and a browser. I ran it privately for a few weeks and realised it was a personal tool, not a product. In September it moved to Cloudflare Workers, with D1 for storage, Queues for background fetching, Cron Triggers for sync and Browser Run for the pages that need rendering, and went public at its own address. The Python version still runs as an offline fallback.
Evidence
- 20 sources across the four rhythms, and 1,791 stored articles at launch.
- 24 Vitest tests for the Worker and 11 pytest tests for the offline runtime, plus a TypeScript check, all passing.
- About 2,300 lines of TypeScript in the Worker.
What didn't work, and the trade-offs
- Some publishers turn away simple HTTP clients, and some feeds only carry a summary. Full text isn't guaranteed; when a fetch fails, the headline and summary still come through.
- A few discovery sources aren't documented APIs, so they can change without notice. The feed card is kept as a fallback.
- There's no sign-in, so read and saved state is shared. That's fine for one reader, and it's why Brief is a personal tool rather than a service.