https://github.com/outten/tech-feed-reader
Multi-user, passkey-authenticated RSS/Atom reader with AI triage, summaries, relevance ranking, sports, stocks, podcasts, YouTube, and more. Ruby/Sinatra/PostgreSQL. AGPL-3.0.
https://github.com/outten/tech-feed-reader
agpl feed-reader postgresql rss rss-reader ruby self-hosted sinatra webauthn
Last synced: 15 days ago
JSON representation
Multi-user, passkey-authenticated RSS/Atom reader with AI triage, summaries, relevance ranking, sports, stocks, podcasts, YouTube, and more. Ruby/Sinatra/PostgreSQL. AGPL-3.0.
- Host: GitHub
- URL: https://github.com/outten/tech-feed-reader
- Owner: outten
- License: agpl-3.0
- Created: 2026-05-02T15:08:44.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-13T15:15:54.000Z (18 days ago)
- Last Synced: 2026-06-13T16:23:20.025Z (18 days ago)
- Topics: agpl, feed-reader, postgresql, rss, rss-reader, ruby, self-hosted, sinatra, webauthn
- Language: Ruby
- Size: 8.34 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# Tech Feed Reader
[](https://github.com/outten/tech-feed-reader/actions/workflows/ci.yml)
[](https://github.com/outten/tech-feed-reader/releases)
[](LICENSE)
A multi-user, passkey-authenticated web application that aggregates public, free RSS / Atom feeds for technology articles, sports news + scores, nature/YouTube channels, podcasts, and webcomics. Reading, tagging, search, summarization, AI-assisted triage, and personalised relevance ranking. Conventions inherited from [t-money-terminal](https://github.com/outten/t-money-terminal) โ Ruby / Sinatra / ERB / RSpec, cache-only render contract, scheduled background refresh โ but storage is PostgreSQL (managed DO cluster, `tsvector` + `ts_rank` for search) instead of `t-money`'s file-per-store JSON.
> **๐ v1.0.0 โ the MVP phase is complete.** Feeder is feature-complete for its original goals and now enters a contributor-driven, open-source phase under [AGPL-3.0](LICENSE). This release is the handoff from "users" to "contributors": if you're here to build, start with [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
> **Status: multi-user behind a passkey auth wall; covers tech + sports + nature/YouTube + podcasts + webcomics + internet radio + finance + world news + science + gaming + daily games + stock ticker; ranked + triaged + summarized; popular-with-other-readers discovery on /feeds.**
## What it does
- **Multi-user, passkey-only auth** (Phase A1) โ WebAuthn sign-up + sign-in with 10 single-use recovery codes; an auth-wall middleware on every protected route; `/account` for display name + passkeys + recovery-code regeneration + account deletion. No passwords, no email.
- **Per-user data, shared catalog** (Phase A2) โ every signed-in user has their own `read_state` / bookmarks / tags / mute rules / sports follows / digests / triages; the `feeds` catalog stays shared via a `user_feed_subscriptions` bridge so one fetch keeps every subscriber up to date.
- **Discovery on `/feeds`** โ 227 curated catalog entries with a client-side filter toolbar (search + topic chips); recommended-for-you + ๐ฅ popular-with-other-readers top charts; AI feed recommender (Claude) from a free-text prompt; Apple Podcasts URLs auto-resolve to the underlying RSS.
- **Home & onboarding** โ `/` shows a marketing pitch to first-time visitors, the `/welcome` topic-chip onboarding (Tech / Sports / Nature / Podcasts / Humor) to newly signed-in users with zero subscriptions, and **What's On Today** (today's sports fixtures, top reads ranked by For You, podcast episodes, YouTube videos) to everyone else.
- **Unified reading list** (`/articles`) โ mixes ๐ articles, ๐ง podcasts, ๐บ videos with day-group dividers, left-anchored thumbnails (per-article โ YouTube โ feed-cover fallback), source-cluster ribbons, and a ๐ reading-time pill.
- **AI triage** (`/triage`) โ Claude Sonnet 4.6 classifies your unread queue into must-read / optional / skip with rationale; daily cron emits one triage per topic.
- **Sports** (`/sports`) โ followed-team score tiles, iCal calendar export, per-team detail pages, league standings, tennis rankings, and "articles mentioning Sinner"-style entity surfaces.
- **Podcasts** (`/podcasts`) โ subscribed-show grid with a persistent mini-player at the bottom of every page that survives Turbo navigation; resume-where-you-left-off; ๐ bus-mode chip for sub-15-min commute episodes.
- **YouTube** (`/youtube`) โ subscribed-channel grid with a bulk-add textarea that resolves `@PBSNewsHour`-style handles โ canonical channel-feed URLs via channel-page scrape; background fetch populates new channels within ~30s.
- **Comics** (`/comics`) โ subscribed webcomic series tiles (latest panel as cover) + recent panels list; comic-aware article hero (no crop) + click-to-zoom image lightbox.
- **Stocks** (`/stocks`) โ search for stock symbols via Finnhub API, view company profiles + real-time quotes, follow symbols. A scrolling ticker bar (followed symbols + the major indices) rides along the top of **every signed-in page**. Each symbol/index page carries a **Recent news** section sourced from that symbol's Yahoo Finance RSS feed (no key required), and following a symbol routes its news into `/articles` and the home page. 10 major world indices (S&P 500, DOW, NASDAQ, Russell 2000, FTSE, DAX, Nikkei, Hang Seng, CAC 40, Euro Stoxx 50) tracked via ETF proxies with hourly refresh. User-followed symbols sync every 15 min via Sidekiq cron.
- **Games** (`/games`) โ daily puzzles and quizzes: **Sudoku** (backtracking-generated 9ร9 with pencil notes, live timer, autosave) and **News Trivia** (5 Claude-generated questions from the last 24h of articles, progressive answer reveal with explanations). Both shared daily and per-user progress tracked.
- **Topics & search** โ `/topics` clusters across the recent corpus using weighted scoring (publisher categories 2ร body keywords, proper-noun phrase detection like "Jannik Sinner", ubiquity ceiling, URL/site-boilerplate stopword sweep); `/search` is PG full-text with `tsvector` + `ts_headline` snippet highlighting and pre-search suggestion chips.
- **Observability** โ `/health` (liveness JSON), `/metrics` (Prometheus), `/admin/dashboard` (article counts + 7-day Activity chart), `/admin/traces` (OpenTelemetry ring buffer).
- **CLI** โ `make refresh-feed FEED=`, `make refresh-feeds`, `make scheduler`, `make digest`, `make triage`, `make sync-sports`, `make run-all` / `make stop-all`.
## Getting started
```bash
make install
make seed-feeds # optional: insert the 5 starter feeds (browse 222 more on /feeds)
make run # dev server with rerun auto-reload โ http://localhost:4567
make test # RSpec โ smoke suite passes out of the box
```
Runs on Ruby 3.4.1 (`.ruby-version` pinned). No API keys required to boot โ Anthropic API key (for Tier 2 LLM summaries) is the only one wired and is optional.
## Pages
| Page | URL | What it shows |
|---|---|---|
| Home | `/` | Marketing pitch for first-time visitors; **What's On Today** (sports / read / listen / watch) for returning users. Continue-progress tile when any podcast or YouTube video has a saved position |
| About | `/about` | Philosophy, anti-swivel-chair argument, how-it-works, tech stack |
| Articles | `/articles` | Unified reading list (๐ articles + ๐ง podcasts + ๐บ videos). Day-group dividers, left-anchored thumbnails, source-cluster ribbons, ๐ reading-time pill, For You sort, topic chips, skim mode |
| Bookmarks | `/bookmarks` | Every article you've saved with the โ button, newest first; reuses /articles list affordances |
| Article | `/article/:uid` | Single article + cached summary + Read-next. Podcasts get a "Play episode" affordance; YouTube articles embed the player with watch-progress + resume |
| Topics | `/topics` | Trending term clusters with sample articles; 7/14/30-day window selector |
| Topic detail | `/topics/:term` | Synthesized "highlights" + every article in the cluster, summaries inline |
| Podcasts | `/podcasts` | Subscribed shows grouped freshest-first + recent episodes |
| YouTube | `/youtube` | Subscribed YouTube channels grid + "+ Add channels" bulk-textarea (resolves @handles via channel-page scrape) |
| YouTube channel | `/youtube/:feed_id` | 10 most recent videos for one subscribed channel as 16:9 tiles |
| Comics | `/comics` | Subscribed webcomic series tiles (latest panel as cover) + recent panels linear list |
| Comic series | `/comics/:feed_id` | 30 most recent panels for one subscribed series; click any to read in /article/:uid with comic-aware hero + lightbox |
| Games | `/games` | Daily games index โ progress tiles for Sudoku and News Trivia |
| Sudoku | `/games/sudoku` | Daily 9ร9 puzzle (shared, one per day); pencil notes, live timer, AJAX autosave, leaderboard |
| News Trivia | `/games/trivia` | 5 Claude-generated multiple-choice questions from today's articles; progressive reveal + explanations |
| Sports | `/sports` | Followed-team score tiles + per-sport landings (NFL / NBA / soccer / rugby / tennis). Calendar + standings + per-team detail + tennis player follows nested below |
| Sports calendar | `/sports/calendar` | Upcoming fixtures across followed teams + iCal export |
| Radio | `/radio` | Browse + follow 32 curated commercial-free internet radio stations (SomaFM, KCRW, KEXP, WFMU, FIP, Swiss Radio, NTS, Radio Paradise, โฆ). Live-stream mode in the global player (LIVE badge, no scrubber) |
| Stocks | `/stocks` | Stock symbol search (Finnhub), major indices grid with intraday sparkline charts + day-range bars (Yahoo Finance, no key required), follow buttons. A scrolling ticker (followed symbols + indices) shows on every signed-in page |
| Stock detail | `/stocks/:symbol` | Company profile, real-time quote (price, change, day range, market cap), follow/unfollow, and a **Recent news** section from the symbol's Yahoo RSS feed |
| Stock news (fragment) | `/stocks/:symbol/news` | Renders just the Recent-news section; polled by `public/stock-news.js` so a cold feed fills in without a reload |
| Triage | `/triage` | AI triage (Claude Sonnet 4.6) โ classifies unread into must-read / optional / skip with rationale; per-topic chips; daily cron history |
| Digests | `/digests` | Daily snapshots of unread articles + summaries (produced by `make digest`) |
| Digest detail | `/digests/:id` | Inline render of a stored digest + opt-in "Summarize with Claude" |
| Feeds | `/feeds` | Manage RSS subscriptions; โจ AI feed recommender (Claude) + "Recommended for you" + ๐ฅ Popular-with-other-readers top charts + client-side filter toolbar (search + topic chips) + full 79-entry catalog. Apple Podcasts URLs auto-resolve to RSS |
| Tags | `/tags` | User-defined tag rules + activity |
| Search | `/search` | Full-text search across article history (PG `tsvector` + `ts_headline`); pre-search suggestion chips, card-style results with snippet highlighting |
| Bus mode | `/bus` | Podcast episodes โค15 min โ pick something for the commute |
| Sign up | `/sign-up` | Passkey registration ceremony; receive 10 single-use recovery codes (shown once) |
| Sign in | `/sign-in` | Passkey authentication; "Use a recovery code" fallback |
| Welcome | `/welcome` | First-time-user onboarding โ pick topic chips (Tech / Sports / Nature / Podcasts / Humor) and one-click-subscribe to curated starter feeds. Fires automatically when a signed-in user has zero subscriptions |
| Account | `/account` | Manage display name, list / add / revoke passkeys, regenerate recovery codes, delete account (typed-confirmation gate) |
| Admin | `/admin` | System overview, integration status, sub-page links |
| Admin dashboard | `/admin/dashboard` | Article counts, 7-day Activity chart, top feeds + tags |
| Cache admin | `/admin/cache` | Per-feed cache age + manual refresh |
| Provider health | `/admin/health` | Per-feed fetch health |
| Backgrounds | `/admin/backgrounds` | Page-background image pool (refreshes 100 random IDs from Picsum) |
| Traces | `/admin/traces` | Recent OpenTelemetry spans (in-memory ring buffer) |
| Health (JSON) | `/health` | Liveness probe + dependency checks |
| Metrics | `/metrics` | Prometheus exposition format |
## Documentation
- **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** โ system topology, request lifecycle, ingestion pipeline, data model, and deploy pipeline, with Mermaid diagrams.
- **[SPEC.md](SPEC.md)** โ project brief: goals, non-goals, data model, page list, roadmap.
- **[AGENTS.md](AGENTS.md)** โ architecture, caching contract, store inventory, common gotchas.
- **[CONTRIBUTING.md](CONTRIBUTING.md)** โ PR workflow (branch / commit / tests / CI / merge).
- **[DEVELOPER.md](DEVELOPER.md)** โ pointer doc for new contributors.
- **[DEPLOYMENT.md](DEPLOYMENT.md)** โ production runbook (Docker Compose, Caddy, DigitalOcean).
## License
Licensed under the **[GNU Affero General Public License v3.0](LICENSE)** (AGPL-3.0). You're free to use, study, modify, and self-host it; if you run a modified version as a network service, the AGPL requires you to offer that version's source to its users. See [LICENSE](LICENSE) for the full terms.
## Conventions inherited from `t-money-terminal`
Branch naming `outten/TODO-NNN`, single-commit-per-PR, rebase-merge, `make test` is gating, docs updated in the same PR as the behaviour change, review-before-shipping. Full workflow in [CONTRIBUTING.md](CONTRIBUTING.md).
## Feline contributors
The codebase has occasional unsolicited assistance from the maintainer's cats, who sleep on the keyboard. Their contributions to date include:
```
]88DLSK34///ลยก โฅโข ` โโ:?{))))))))))))))))))))))))))))))))))))))))))))))))))))
```
None have shipped to `main` (yet). Pull requests reviewed but not merged. ๐