Skip to content

Releases: jcrabapple/feedecho

FeedEcho v1.5.2 — Nix Flake & Module Fixes

Choose a tag to compare

@jcrabapple jcrabapple released this 09 Aug 01:38

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 hatchling from callPackage args (was undefined at top-level pkgs, would fail with "undefined variable"); now references python.pkgs.hatchling internally
  • package.nix: added passthru.python so the module can derive the correct site-packages path without hardcoding python3.12
  • flake.nix: removed deprecated nixosModule (singular) export
  • flake.nix: added devShells output (nix develop now works)
  • flake.nix: added checks output (nix build .#checks.<system>.tests runs the suite)
  • flake.nix: switched to genAttrs instead of manual builtins.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.libPrefix instead of hardcoded python3.12
  • module.nix: accepts flake-injected package with callPackage fallback for non-flake users
  • module.nix: removed redundant ReadWritePaths (StateDirectory handles it)
  • module.nix: changed dataDir type from path to str

Other fixes

  • Removed non-existent favicon.svg from pyproject artifacts (favicon is served by a route handler, not a file)
  • Fixed nix/README.md: removed non-existent nix build .#feedecho.check command
  • Rewrote non-flake example to actually wire the module and override the package
  • Added package option 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

Choose a tag to compare

@jcrabapple jcrabapple released this 09 Aug 01:34

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.nixservices.feedecho NixOS module with:
    • Dedicated feedecho system user
    • LoadCredential for auth token (never in the process listing)
    • Systemd hardening (ProtectSystem, PrivateTmp, NoNewPrivileges)
    • Configurable port, data directory, callback URL, firewall
  • 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.toml version 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

Choose a tag to compare

@jcrabapple jcrabapple released this 09 Aug 01:06

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

Choose a tag to compare

@jcrabapple jcrabapple released this 09 Aug 00:48

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 -d

See the README for full options.

FeedEcho v1.4.0 — Keyword Filters

Choose a tag to compare

@jcrabapple jcrabapple released this 09 Aug 00:39

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

Choose a tag to compare

@jcrabapple jcrabapple released this 09 Aug 00:26

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)

Choose a tag to compare

@jcrabapple jcrabapple released this 08 Aug 03:51

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=30s for 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

Choose a tag to compare

@jcrabapple jcrabapple released this 08 Aug 03:36

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

Choose a tag to compare

@jcrabapple jcrabapple released this 08 Aug 03:22

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