A hosted changelog for a product: you write entries, it publishes a page, an RSS feed and an in-app widget so users see what shipped.
Build me a changelog service that replaces Changefeed: I write entries, it publishes a page, a feed, and a widget in my app with an unread badge. STACK - Node 20+ with Fastify, server-rendered HTML - SQLite through better-sqlite3, WAL mode - One embeddable widget under 12KB gzipped, from my own domain - Caddy in front THE DATA MODEL - projects: id, slug, name, custom_domain, accent, logo_path, is_public - entries: id, project_id, title, slug, body_md, kind, labels_json, published_at, updated_at, author, cover_path - reads: id, entry_id, user_key, read_at — user_key is the signed identifier my app passes in - subscribers: id, project_id, email, token, confirmed_at, unsubscribed_at, cadence - deliveries: id, entry_id, subscriber_id, sent_at, opened_at — one row per person per entry, so nobody is emailed twice - views: id, entry_id, path, referrer_host, ip_hash, bot, created_at - Kinds: new, improved, fixed. Three, not fifteen — a taxonomy nobody can remember gets used at random WRITING - Markdown with a live preview, images uploaded and stored locally with the width and height recorded - Draft, scheduled and published states; a scheduled entry is published by a worker that is idempotent - Every published entry has a permanent slug and its own page, because a changelog entry is the thing people link to when they say 'this shipped' - Entries are edited freely before publishing and revised after, with a revision log — a changelog that quietly rewrites history is a bad joke THE WIDGET, WHICH IS THE PRODUCT - A script and one init call, given a signed user key from my backend - An unread badge counting entries published since that user's last read - Opening it marks entries read as they scroll past, not all at once on open - Shadow DOM, my accent, dark and light, keyboard navigable, Escape closes, focus returns where it was - Loads its payload once per session with an ETag; the badge count comes from a tiny separate endpoint that can be called on every page without cost THE PAGE AND THE FEED - A public page at my own domain or a subpath, listing entries newest first, filterable by kind and label - RSS and JSON Feed, both valid, with full content - Per-entry pages with Open Graph tags and a generated share image carrying the title and the kind - Sitemap, canonical URLs, and no client-side framework: the page is HTML and it is fast EMAIL - Subscribers with double opt-in and one-click unsubscribe that needs no login - Immediate, weekly or monthly cadence per subscriber - The digest is assembled from entries not yet delivered to that address, so a change of cadence never sends the same entry twice - Plain text alongside HTML, and the HTML is tables and inline styles because that is what mail clients render MEASURING - Views per entry, widget opens, badge clicks, entries read - Email delivered, opened, unsubscribed — open rate labelled as the unreliable number it is - Bots flagged and excluded from published figures - No third-party analytics, no tracking pixel from anyone else OPERATIONS - .env: DATABASE_PATH, BASE_URL, WIDGET_JWT_SECRET, SMTP_URL, SESSION_SECRET, HASH_SALT - Migrations on boot, each once - Custom domain via on-demand TLS in Caddy, with the allowed hosts checked against the projects table - Nightly backup off the machine, restore script - Health endpoint WHAT MATTERS MOST The badge. Build the read state, the count endpoint and the mark-as-read-on-scroll first, and put the widget in a real app. Everything else here is a blog with an RSS feed; the badge is why anyone pays for it. Give me the repository, the widget script, migrations, .env.example, a seed project with twenty entries, and a README with deploy steps behind Caddy.
What you lose
- An in-app widget with an unread badge, which is what actually gets a changelog read
- A hosted page with a custom domain and no deployment step
- Email digests of new entries to people who asked for them
If you would rather not build
- Keep a Changelog, plus a static site generator
- A GitHub releases page, which is free
What it costs
as published on their pricing page
| Plan | Billed monthly | Billed yearly | Last read |
|---|---|---|---|
| — | $9/mo | — | — |
Their pricing page is where these came from. Seeing a different price? Tell us.
The escape hatch
open source · no votes, no paid placement
Astro
$0Content collections make a typed changelog with feeds a short build.
withastro/astrofree · open source
Docusaurus
$0Has a blog mode that works well as a changelog with RSS.
facebook/docusaurusfree · open source
Why this verdict
our own opinion · changed only by a person
92/100
Verdict yes at 92. Markdown files, a static build and a small widget. The unread badge is the only piece with any logic in it.
History
tracked since 10 Aug 2026 · nothing is ever overwritten
Questions about Changefeed
answered from the record above
Is Changefeed free?
No — the plan we track is $9 a month. Around $9/month billed monthly for a single product changelog.
Can you replace Changefeed by building your own?
YES. Replaceable in one session with an AI coding agent. Replacement score 92 out of 100, build time one session. Read what you lose before you decide.
How much does Changefeed cost?
$9 a month on Starter — $108 a year. Recorded 10 Aug 2026.
What do you lose by replacing Changefeed?
An in-app widget with an unread badge, which is what actually gets a changelog read; A hosted page with a custom domain and no deployment step; Email digests of new entries to people who asked for them. If any of those carry weight for you, keep paying.
Is there an open-source alternative to Changefeed?
Yes: Astro, Docusaurus. The prompt on this page is for when you want it your way instead.
Related entries
same category first, most replaced first
Every week, something stops being worth paying for.
New verdicts, prices that moved, entries added. One email a week. Unsubscribe in one click. Nothing is being sent yet — your address is kept here, and the first issue is the first thing it is used for.
free forever · no tracking pixel · stored here, never passed to anyone

