ideas.
June 09, 2026 4 min read webcreatorcontent

Embeddable what's-new widget for indie apps

A self-hosted changelog service that injects a 'what's new' badge and panel into any web app via a single script tag, with no third-party branding or monthly fee.

The idea

A lightweight self-hosted service where you write changelog entries in a minimal admin UI, and your users see a notification badge in your app's corner whenever there is unread news. One <script> tag in your HTML is all it takes on the consumer side. Entries are plain text with an optional label — "new", "improved", or "fixed" — and a date. The embed reads a public JSON endpoint on your server, renders a small panel when the badge is clicked, and persists the user's "last read" position in localStorage so the badge clears itself.

Everything runs in a single Docker container with a SQLite database. You bring the domain; Traefik handles TLS.

Why build this

Beamer, Headway, and Canny all solve this problem well but charge $49–99 per month and stamp their own branding on the widget. For an indie developer shipping a small SaaS or an internal tool, that is a significant overhead for what is fundamentally a JSON list and a small UI component.

The technical surface is genuinely small. The public-facing side is a cacheable JSON endpoint and a few hundred lines of vanilla JS. The admin side is a handful of CRUD routes protected by a static token. What does not exist yet is a well-packaged, self-hostable version that is ready to deploy in under ten minutes and does not require managing an NPM package or a separate CDN.

The audience is the long tail of developers who ship tools with users and want to communicate changes without paying for infrastructure they do not need.

Stack sketch

  • Backend: FastAPI + SQLite via aiosqlite and SQLAlchemy Core — minimal dependencies, trivial to back up (one file), no migration tooling required for the scope of v1
  • Embed: a vanilla JS custom element (<changelog-widget>) bundled with esbuild into a single widget.js file served from your own origin; no React, no build step on the consumer's side
  • Admin UI: HTMX + Jinja2 templates; server-rendered forms for creating, editing, and publishing entries — no frontend build pipeline to maintain
  • Public API: GET /api/entries returns the last 30 published entries as JSON; versioned URL (/api/v1/entries) so you can evolve the shape without breaking existing embeds
  • Changelog page: a server-rendered /changelog route for users who want the full history outside the widget
  • RSS feed: /rss.xml generated from the same entry list; gives readers a subscribe option with no extra infrastructure
  • Auth: a single ADMIN_TOKEN environment variable; the admin routes check a Bearer token; no user accounts, no sessions

Scope for v1

  • Create, edit, publish, and unpublish changelog entries with a title, Markdown body, date, and an optional label (new / improved / fixed)
  • Embed script that shows a colored badge when unread entries exist; clicking opens a slide-in panel listing the five most recent entries with a link to /changelog for the full list
  • "Last read" timestamp stored in localStorage keyed to the widget's origin; the badge clears when the panel is opened
  • Public JSON and RSS endpoints with a 60-second Cache-Control header so the server is not hit on every page load
  • Single Docker image; ADMIN_TOKEN and BASE_URL are the only required environment variables
  • Deliberately out of scope for v1: email digests for subscribers, reader reactions or comments, per-user segmentation by plan, analytics on who opened the panel, OAuth admin login

Where it could go

The most natural follow-on is an email digest. After publishing a batch of entries, a single "send digest" button compiles recent changes into a plain-text email and delivers it through a configured SMTP relay (Amazon SES, Postmark, or a self-hosted msmtp setup). Users who subscribe by entering their address on the changelog page get the email; no external ESP account required beyond SMTP credentials. That feature alone justifies the project for teams with an established user list.

A second expansion is segmentation: pass a plan="pro" attribute in the script tag and mark entries as visible only to certain plan tiers. The embed filters client-side against the attribute value; the JSON endpoint returns all entries and the embed discards irrelevant ones locally. This requires no server-side user model and keeps the architecture flat — the complexity stays in the six-line filter function in widget.js, not in a new database schema.

Watch out for

The embed script runs inside your users' production apps. If your changelog server goes down, the badge fetch will fail — make sure widget.js catches network errors silently and degrades to "no badge" rather than throwing a console exception or blocking the page. Set the fetch with a short timeout (two seconds) and wrap it in a try/catch; a down changelog server should be invisible to your users.