A Gleam/Erlang RSS and Atom feed reader. Tracks feeds, fetches entries on a schedule, and serves a server-rendered HTML UI with HTMX for partial updates.
- Subscribe to RSS/Atom feeds (manual add or OPML import)
- Unread, starred, and history views
- Ever-opened tracking for entry content and comments links (first open only)
- Background scheduler fetches new entries every 3 minutes
- SQLite storage via sqlight + Parrot codegen
- Server-rendered HTML with Lustre + lustre_pipes + HTMX
- No authentication (handle at the reverse proxy / network layer)
mise install # install gleam, erlang, rebar3
gleam deps download
gleam run # serves on http://localhost:3000The database defaults to feedreader.db in the working directory. Override
with the DATABASE_PATH environment variable.
Content and comments links have independent first-open timestamps
(content_opened_at and comments_opened_at). JavaScript records keyboard,
regular-click, Cmd/Ctrl-click, and middle-click activations in the background;
later opens do not change the timestamps. Opening a link does not mark the entry
as read, and changing read status does not erase these timestamps. Existing
entries start with no recorded opens when the database is upgraded.
Tracking is best-effort if the background request fails. Context-menu “open link” actions are not tracked.
gleam check # type check
gleam test # run the test suite
gleam format # format code
mise run pre-commit # format + check + lint + test
mise run gen # regenerate Parrot SQL codegen from schema + queries
mise run assets # install + build TailwindCSSRun the browser event-handler tests separately with Node.js:
mise exec node@24.20.0 -- node --test test/feedreader/web/entry-opens.test.jsThe CI builds a multi-arch image (amd64 + arm64) and pushes it to GHCR on every
push to main and on version tags (v1.0.0).
docker pull ghcr.io/kasuboski/feedreader:main
docker run -d \
-p 3000:3000 \
-v feedreader-data:/data \
-e DATABASE_PATH=/data/feedreader.db \
ghcr.io/kasuboski/feedreader:mainOr with docker-compose (see docker-compose.yaml):
docker compose up -dThe SQLite database persists in a Docker volume mounted at /data. To migrate
from the old Elixir/Phoenix deployment, copy the existing feedreader.db into
the volume — the schema is compatible.
| Layer | Technology |
|---|---|
| Language | Gleam → Erlang/BEAM |
| Web server | Wisp + Mist |
| HTML rendering | Lustre SSR + lustre_pipes |
| Interactivity | HTMX via hx |
| Database | SQLite via sqlight + Parrot |
| Background workers | Gleam/OTP actors (scheduler + fetcher) under a supervisor |
See SCOUTING_REPORT.md for the full Elixir → Gleam migration analysis.