Releases: jcrabapple/feedecho
Release list
FeedEcho v1.5.2 — Nix Flake & Module Fixes
FeedEcho v1.5.2 — Nix Flake & Module Fixes
Fixes 12 issues found in a review of the v1.5.1 Nix support.
Critical fixes
- package.nix: removed
hatchlingfromcallPackageargs (was undefined at top-levelpkgs, would fail with "undefined variable"); now referencespython.pkgs.hatchlinginternally - package.nix: added
passthru.pythonso the module can derive the correctsite-packagespath without hardcodingpython3.12 - flake.nix: removed deprecated
nixosModule(singular) export - flake.nix: added
devShellsoutput (nix developnow works) - flake.nix: added
checksoutput (nix build .#checks.<system>.testsruns the suite) - flake.nix: switched to
genAttrsinstead of manualbuiltins.listToAttrs - flake.nix: flake-built package is now injected into the module via
pkgs.feedecho-flake-pkg, so the module and flake package are no longer disconnected - module.nix: site-packages path derived from
cfg.package.python.libPrefixinstead of hardcodedpython3.12 - module.nix: accepts flake-injected package with
callPackagefallback for non-flake users - module.nix: removed redundant
ReadWritePaths(StateDirectory handles it) - module.nix: changed
dataDirtype frompathtostr
Other fixes
- Removed non-existent
favicon.svgfrom pyprojectartifacts(favicon is served by a route handler, not a file) - Fixed
nix/README.md: removed non-existentnix build .#feedecho.checkcommand - Rewrote non-flake example to actually wire the module and override the package
- Added
packageoption to config table, hash prefetch instructions, and StateDirectory/dataDir notes
Note
Still not nix build-tested locally (no Nix on this machine). The fetchFromGitHub hash in nix/package.nix is lib.fakeHash — replace after first build.
FeedEcho v1.5.1 — Nix Flake + NixOS Module
FeedEcho v1.5.1 — Nix Flake + NixOS Module
Adds a Nix flake and NixOS module for declarative deployments. No application changes since v1.5.0.
What's included
- flake.nix — builds FeedEcho as a Python application, exposes packages per system and a NixOS module
- nix/module.nix —
services.feedechoNixOS module with:- Dedicated
feedechosystem user LoadCredentialfor auth token (never in the process listing)- Systemd hardening (
ProtectSystem,PrivateTmp,NoNewPrivileges) - Configurable port, data directory, callback URL, firewall
- Dedicated
- nix/package.nix — standalone derivation for non-flake NixOS users
- nix/README.md — full install and configuration docs
Usage
{
inputs.feedecho.url = "github:jcrabapple/feedecho";
outputs = { self, nixpkgs, feedecho, ... }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
modules = [
feedecho.nixosModules.default
{
services.feedecho = {
enable = true;
authTokenFile = "/run/secrets/feedecho-token";
callbackUrl = "https://feedecho.example.com/oauth/callback";
};
}
];
};
};
}Other changes
pyproject.tomlversion bumped to 1.5.0 (was 0.1.0)- Wheel build now includes templates/static/favicon (self-contained install)
- LICENSE file added (MIT)
Note
The Nix files use standard patterns but were not nix build-tested locally (no Nix on the development machine). The person who requested this should run nix build and report any issues — the fetchFromGitHub hash in nix/package.nix will need to be filled in after the first build.
FeedEcho v1.5.0 — Pause, Retries & Failure Notifications
FeedEcho v1.5.0 — Pause, Retries & Failure Notifications
Reliability release: feeds can be paused, failed deliveries retry intelligently, and FeedEcho tells you when something is broken.
Per-feed pause
Pause button on each feed (Feeds page). Paused feeds are skipped entirely — no fetching, no posting — while echoes and cursor state are preserved for when you resume. Useful for feeds that go temporarily spammy (liveblogs, event coverage).
Bounded retries with backoff
- Failed deliveries retry automatically with exponential backoff (default: 5 min, doubling per attempt, capped at 1 day)
- Max attempts setting (default 5, 0 = retry forever). Exhausted items are marked Gave up — terminal — so one broken item can no longer stall the whole feed
- A retry sweep after each feed check picks up due retries even behind the cursor; items that have aged out of the feed give up cleanly instead of looping
- History shows attempt counts and next retry times, with per-row Retry (reset + requeue) and Give up actions
Failure notifications
- After N consecutive failed delivery attempts (default 3), FeedEcho emails you once via the existing SMTP settings — with the likely causes (expired Mastodon token, changed SMTP credentials, unreachable host)
- One recovery email when delivery starts working again
- Alert state is persisted, so restarts don't re-send alerts
- Notification address defaults to your first email account; overridable in Settings
All knobs live in Settings → Retries & Failure Notifications.
Quality
- 12 new tests: pause behavior, backoff gating, give-up at cap, retry sweep, notification state machine (97 total passing)
- Schema migrates automatically on startup
Upgrade
Docker: `docker compose pull && docker compose up -d`. From source: pull master and restart. No config changes required.
FeedEcho v1.4.1 — Docker Packaging
FeedEcho v1.4.1 — Docker Packaging
No application changes since v1.4.0 — this release adds official Docker packaging.
What's included
- Dockerfile + docker-compose.yml — one-command install, SQLite persisted in a volume
- Pre-built multi-arch images (amd64 + arm64) published to GHCR: `ghcr.io/jcrabapple/feedecho:latest`
- Install docs — pre-built image path, local build path, env var reference, reverse-proxy notes
Fastest install
mkdir feedecho && cd feedecho
printf 'FEEDCHO_AUTH_TOKEN=change-me\nFEEDCHO_CALLBACK_URL=http://localhost:8453/oauth/callback\n' > .env
curl -O https://raw.githubusercontent.com/jcrabapple/feedecho/master/docker-compose.yml
# edit docker-compose.yml: comment out `build: .`, uncomment the `image:` line
docker compose up -dSee the README for full options.
FeedEcho v1.4.0 — Keyword Filters
FeedEcho v1.4.0 — Keyword Filters
Echoes can now filter feed items by keyword before anything gets posted.
Usage
Each echo has a Keyword filter field (comma-separated, case-insensitive) matched against item title and summary, plus a Filter mode:
- Exclude matching items (default) — anything containing a keyword is skipped. Example: `spoiler, giveaway, nsfw`
- Only include matching items — only items containing at least one keyword are echoed
Empty filter = everything passes, same as before.
Behavior
- Filtered items appear in History with a gold "Filtered" badge, so you can audit what got dropped
- Filters are checked before delivery is claimed — filtered items never enter the retry machinery and never block the feed cursor
- Filtered rows are terminal: removing a filter later will not replay old items as a backlog dump to your destinations
- Active filters show as a badge on each echo row (`not: spoiler, nsfw` / `only: python`)
Quality
- 14 new tests covering matching semantics, skip behavior, history recording, and no-replay guarantee (85 total passing)
- Existing echoes unaffected (migration adds columns with empty defaults)
Upgrade
Pull `master` and restart `feedecho.service`. Schema migrates automatically on startup.
FeedEcho v1.3.0 — UI Redesign: Color Palette + Light/Dark Themes
FeedEcho v1.3.0 — UI Redesign
Full visual redesign replacing the monochrome blue palette with a dynamic, token-based color system and light/dark theme support.
Highlights
- New OKLCH color palette — warm coral primary, teal secondary, and semantic green/gold/red accents. Brand colors hang off 5 hue variables at the top of `style.css`, so reskinning is a one-line change.
- Light + dark themes — toggle button in the navbar (persists to `localStorage`), falls back to system preference, follows live OS theme changes. Theme is applied pre-paint via an inline script — no flash of the wrong theme on load.
- Colorful dashboard — stat cards now rotate coral → teal → green → gold accents.
- Adaptive browser chrome — `theme-color` meta and `color-scheme` set per theme so mobile status bars, form controls, and scrollbars match.
Quality
- Both themes pass WCAG AA contrast on primary buttons (7.9:1 light, 5.0:1 dark)
- Hardcoded badge/alert hex colors replaced with semantic, theme-aware tokens
- Added proper `.alert-warning` class (was an inline-style hack)
- `prefers-reduced-motion` respected
- All 71 tests passing
Upgrade
Pull the latest `master` and restart `feedecho.service`. No database or config changes required.
FeedEcho v1.2.0 — Security Hardening (Critical + High)
Addresses all Critical and High-severity findings from the GPT-5.6 Terra Pro code review.
Critical Fixes
C-1: SSRF bypass via redirects
Replaced follow_redirects=True with a manual redirect loop that validates every Location header via validate_outbound_url(). A public feed that redirects to 169.254.169.254 or 127.0.0.1 is now blocked. Capped at 5 redirects.
C-2: Unauthenticated /oauth/connect SSRF
Removed /oauth/connect from auth-exempt paths. Only /oauth/callback remains exempt (needed for Mastodon redirect). Unauthenticated users can no longer trigger outbound requests to arbitrary instance URLs.
High Fixes
H-1: Session-bound, expiring, single-use OAuth state
New oauth_states table stores nonce, instance, hashed session binding, expiry, and consumed timestamp. State tokens are:
- Session-bound: OAuth session cookie ties callback to initiating browser
- Expiring: 10-minute TTL
- Single-use: Atomically consumed via conditional UPDATE
- Full HMAC: SHA-256 (was truncated to 64 bits)
H-2: Cursor stops at first failed item
break on first failure instead of continuing past it. Failed items are retried on next poll, never permanently skipped. Cursor only advances across a contiguous successful prefix.
H-3: Atomic pending-row claim
INSERT ... ON CONFLICT DO UPDATE with conditional WHERE clause. claim_token ensures only the owning worker can finalize. Stale pending rows (10+ min) are reclaimable. attempt_count tracks retries.
H-4: Per-feed lease
Atomic lease acquisition prevents concurrent processing. Lease renewed per-item, released in finally block. Cursor updates require lease ownership. Scheduler uses max_instances=1, coalesce=True.
H-5: SSRF validation for all outbound URLs
Renamed validate_feed_url to validate_outbound_url and applied to: feed URLs, Mastodon API calls, OAuth registration/token exchange. Blocks private IPs, embedded credentials, non-http schemes.
Also Fixed (Medium severity)
- M-1:
get_db()now properly closes connections via@contextmanager - M-1:
busy_timeout=30sfor SQLite write contention - M-1: WAL mode set once in
init_db, not per-connection - M-2: Generic error messages to clients; details logged server-side with
logger.exception()
Tests
71 tests passing. New tests cover: single-use state consumption, session binding, expired state rejection, full-length signature, redirect-to-private blocked, instance URL SSRF, embedded credentials.
Full changelog: v1.1.0...v1.2.0
FeedEcho v1.1.0 — Security Hardening
Security-focused release addressing SSRF, CSRF, auth, and secret exposure.
New Features
SSRF Protection
Feed URLs are validated before fetching. Blocks private/internal IP ranges (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x, ::1, fc00::, fe80::), non-http schemes (file://, gopher://), and hostnames that resolve to private IPs. Prevents attacks on cloud metadata endpoints, localhost, and internal services.
Web UI Authentication
Optional shared-secret auth via FEEDCHO_AUTH_TOKEN environment variable. When set, all requests must include the token as either a cookie (set by the login page at /login) or an X-Auth-Token header. When unset, behavior is unchanged. OAuth callback endpoints are exempt.
HMAC-Signed OAuth State
OAuth state tokens are now HMAC-signed (nonce|instance|signature) with hmac.compare_digest verification. Prevents CSRF and instance field tampering. Replaces the previous unsigned state parameter.
SMTP Password Masking
SMTP passwords are masked (********) when sent to the browser. The save endpoint skips password updates when the mask placeholder is submitted, preserving the existing password.
Configurable OAuth Callback URL
FEEDCHO_CALLBACK_URL environment variable replaces the hardcoded callback URL. Self-hosters no longer need to edit source code.
Configuration
| Environment variable | Purpose | Default |
|---|---|---|
FEEDCHO_AUTH_TOKEN |
Shared-secret auth token (enables login page + API auth, also signs OAuth state) | Unset (auth disabled) |
FEEDCHO_CALLBACK_URL |
Public URL for OAuth callback | https://feedecho.snakepit.us/oauth/callback |
FEEDCHO_DB_PATH |
Path to SQLite database | ./feedecho.db |
Tests
17 new tests (test_security.py) covering SSRF IP/scheme blocking, hostname resolution mocking, and OAuth state sign/verify/tamper rejection. 62 tests total, all passing.
Full changelog: v1.0.0...v1.1.0
FeedEcho v1.0.0
First stable release.
Self-hosted RSS feed cross-poster — built as a replacement for Echofeed (shut down August 2026).
Features
- RSS/Atom/JSON feed support via feedparser
- Mastodon OAuth — connect accounts with one click, no manual token creation
- Template engine —
{{ title }},{{ link }},{{ summary }},{{ content }},{{ author }},{{ date }},{{ date:iso }},{{ date:short }},{{ hashtags }} - Multiple accounts — post to multiple Mastodon instances
- Per-feed poll intervals — each feed checked on its own schedule
- Post history with success/failure tracking and error messages
- Idempotent posting — pending-row pattern prevents duplicates; failed posts retried on next poll
- Visibility settings — public, unlisted, private, direct
- Mobile-responsive — tables convert to cards, 44px touch targets
- Inline echo editing with XSS-safe cancel
- SMTP email destination — echo to email in addition to Mastodon
- Cloudflare Tunnel deployment ready
Tech Stack
Python · FastAPI · SQLite (WAL) · Jinja2 · APScheduler · feedparser · httpx
Stats
- 45 tests passing
- 12 commits since initial implementation
Full changelog: https://github.com/jcrabapple/feedecho/commits/v1.0.0