# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The long form, with what was wrong before and how it was found, is in [docs/history.md](docs/history.md). ## [Unreleased] ### Added - A Popular button at the top of the feed list opens the popular list without going through Add feed. ### Changed - The popular list counts everyone, you included. Your own feeds stay on it, marked Subscribed, and clicking one opens it. ## [0.3.0] - 2026-09-11 ### Added - Add feed lists what other people on this server subscribe to, most subscribers first, and subscribes you by id (`GET /api/popular`, `POST /api/popular/{id}`). Feeds from an OPML, and feeds with a login or a key in their URL, are never listed. - Upload an OPML file to import, beside the paste box. The page checks it looks like OPML before sending it and clears the picker afterwards. - Settings → Manage users: add and remove accounts, and choose who is an admin (`GET`/`POST /api/users`, `PATCH`/`DELETE /api/users/{id}`). - Per-user subscriptions, and per-user read, starred and playback state. The existing library is adopted by the admin on first start. - Subscribing to a feed someone else already has costs no second fetch and no second copy. Scanning merges every subscriber's wants. - Delete on a shared feed reads **Delete for everyone**, and the server answers `409` while anyone else has starred the item or not played it (`?force=true` overrides). - A shared feed's header says how many other people read it. - `docs/` for configuration, the CLI, users, SSO and architecture, and `CLAUDE.md` for anyone working on the code. - Browser tests for OPML import and export by every route, user admin, unread ordering, and `ipx import`/`ipx export`. ### Changed - Feeds inside an OPML subscription list the ones with unread items first. - OPML import subscribes you to every feed in the file. `ipx import` subscribes the first admin. - OPML export lists only your own subscriptions. - Production runs as a Docker image pushed to `192.168.1.130:5000` and recreated with `docker compose`. - This changelog follows Keep a Changelog. The long-form entries moved to `docs/history.md`. ### Fixed - Importing another account's OPML export subscribed nobody and reported "Imported 0 feed(s)". Feeds it added had no subscriber, so they were never scanned. - Importing something that is not OPML answered `500`. It is now `400` "that is not an OPML file", refused before anything changes. - Starring stopped protecting a file from the quota and age sweeps once read state became per-user. ### Security - The log is admin-only (`GET /api/logs` answers `403`, and the Log button is hidden). It names every account, every feed and every failed sign-in. - OPML export no longer hands anyone signed in the whole catalogue, including other people's private feed URLs. ## [0.2.0] - 2026-09-10 ### Added - Web UI served by the daemon: plain HTML and JS compiled into the binary, with feeds, items, filters, search, sanitised show notes and live progress over SSE. - Player bar with resume, speed, keyboard shortcuts and lock-screen controls. - Three-pane layout: feeds beside, items above, and the selected item's text and files below. - Phone layout. - Accounts and sign-in: Argon2id passwords, session cookies, `ipx user add|list|passwd|rm`, and a trusted proxy header for Cloudflare Zero Trust or Authentik (`docs/sso.md`). - Subscribing to an OPML: it is re-read every scan and its feeds show as a folder. A feed dropped from it is removed unless it has downloads. - Scheduling: a global interval with per-feed overrides (`every 30m`, `4h`, `1d`, `2w`). - `[general] media_types`, default audio and video, and `max_new_per_check`, default 3. - Every enclosure of an item, a View link for files that are not audio or video, and per-item artwork. - In-app log view with Daemon I/O, Scans and HTTP tabs. - Mark all read on an OPML subscription. - Editable feed URL with a copy button. - Docker image whose healthcheck goes through the control socket. - The iPodderX name, icon, and a colour scheme taken from the icon. - `tests/page-smoke.js` and a Playwright browser suite. ### Changed - Global settings and scan schedules are admin-only. The per-feed schedule picker is gone. - Feeds from an OPML live in the database, not `config.toml`. - Opening an item marks it read. Playing it marks it read only at the end or past 90%. - "Episodes" became "items", since half the library is text. - A burst of scan events causes one refresh, not one per feed. - All is the default filter. OPML import and export, Settings and Log moved out of the header. - Torrents run detached, two at a time. ### Fixed - Download fetched the next queued episodes instead of the one clicked. - Pressing play made an item vanish from the Unread list. - Clearing a folder, schedule or cap from the UI did nothing. - The daemon ignored SIGTERM until the current download finished. - A missing function stopped the page script and left the whole UI dead. - One download painted progress on every pending row. - A torrent could freeze scanning for up to an hour. - Downloading from an OPML feed failed with "belongs to unsubscribed feed". - Image enclosures were downloaded, counted as episodes and given a play button. - A feed whose entries had been deleted stayed empty, because the server kept answering `304`. - An item with several enclosures kept only the last. - OPML folders rendered open by default. - Mark read in the text pane recursed until the stack overflowed. - The Unread, Downloaded and Flagged filters answered `500` without a search term. - An OPML subscription always showed 0 unread. - Folder names kept doubled spaces where separators were stripped. - Sidebar rows had four different left edges. ### Security - The web UI needs a token or a sign-in. The token is compared in constant time, and an empty token refuses to serve. - Show notes are sanitised with `ammonia`. - A proxy's user header is honoured only from an address in `trusted_proxies`. - A feed URL must be http(s), so `file:///etc/passwd` is refused. - Download folders are sanitised per path segment, so `../../etc/Show` cannot climb out. ## [0.1.0] - 2026-09-09 ### Added - `ipx`, a Rust rewrite of the iPodderX engine: TOML config, SQLite state, and `ipx list`, `add`, `rm` and `fetch`. - RSS and Atom parsing with conditional GET, `` and basic auth. - Streaming downloads with explicit, keyword and per-scan filters, deduplicated by enclosure URL. - Quota and age retention that never touches a starred file, and `ipx reap [--dry-run]`. - `ipx daemon` with a JSON-lines Unix socket. CLI commands proxy to a running daemon. - Torrent enclosures through librqbit, seeding to a ratio or a time, with a stall timeout. - `ipx import` and `ipx export` for OPML, and systemd units in `contrib/`. [unreleased]: https://git.sdf1.net/rays/ipodderx-rs/compare/v0.3.0...main [0.3.0]: https://git.sdf1.net/rays/ipodderx-rs/compare/v0.2.0...v0.3.0 [0.2.0]: https://git.sdf1.net/rays/ipodderx-rs/compare/v0.1.0...v0.2.0 [0.1.0]: https://git.sdf1.net/rays/ipodderx-rs/releases/tag/v0.1.0