Projects / Knowledge Tools

f.ads

Can I have a feed that ends and explains itself?

f.ads is a private, single-owner reading surface for finite, explainable editions built from ATProto and RSS sources. One person signs in, and the Worker assembles a bounded edition with a trace of why each item is there.

ForPeople who want to read from Bluesky and RSS on their own terms, on infrastructure they host themselves.
SolvesFeeds are endless and opaque, and the ranking is decided by someone else.

What is f.ads?

f.ads is a reader for one person. You host it on your own Cloudflare account, sign in with your ATProto identity, and read editions assembled from the ATProto and RSS sources you choose. An edition is finite and explainable: it has an end, and each item carries a trace of how it got there.

ELI5: a newspaper that only you receive, printed from the accounts and feeds you picked, with a note in the margin saying why each story made the cut.

How it works

The Worker holds the private boundary. It maps ATProto and RSS sources into typed content envelopes, applies the owner’s hard allowance and safety gates, then makes a bounded edition. D1 stores owner-scoped content, preferences, editions and traces. A Durable Object holds the OAuth session, and an ingestion queue plus a cron job keep sources current.

Sign-in uses ATProto OAuth with a private ES256 key generated once during setup. Only the public JWK is exposed through the client metadata flow, and only one DID, set as OWNER_DID, is allowed to enter.

Design notes

The reader is deliberately replaceable. Source adapters, the curation service and the headless API sit behind typed contracts, so another presentation could use the same owner-controlled data and decision traces. Nothing about taste, history or rankings is published back to ATProto.

Reader frames are local Svelte components registered in the repo; the reader does not load executable extensions from URLs.

Hosting it

Setup is a handful of Wrangler commands: create a D1 database and two queues, fill in an install config, generate the OAuth key and store it as a Worker secret, apply migrations, deploy. The README also covers database export before upgrades and point-in-time recovery.