{"id":50926957,"url":"https://github.com/outten/tech-feed-reader","last_synced_at":"2026-06-17T00:02:11.349Z","repository":{"id":364574033,"uuid":"1227353278","full_name":"outten/tech-feed-reader","owner":"outten","description":"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.","archived":false,"fork":false,"pushed_at":"2026-06-13T15:15:54.000Z","size":8745,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T16:23:20.025Z","etag":null,"topics":["agpl","feed-reader","postgresql","rss","rss-reader","ruby","self-hosted","sinatra","webauthn"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/outten.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-02T15:08:44.000Z","updated_at":"2026-06-13T15:18:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/outten/tech-feed-reader","commit_stats":null,"previous_names":["outten/tech-feed-reader"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/outten/tech-feed-reader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outten%2Ftech-feed-reader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outten%2Ftech-feed-reader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outten%2Ftech-feed-reader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outten%2Ftech-feed-reader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outten","download_url":"https://codeload.github.com/outten/tech-feed-reader/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outten%2Ftech-feed-reader/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34428197,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agpl","feed-reader","postgresql","rss","rss-reader","ruby","self-hosted","sinatra","webauthn"],"created_at":"2026-06-17T00:00:50.447Z","updated_at":"2026-06-17T00:02:11.342Z","avatar_url":"https://github.com/outten.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tech Feed Reader\n\n[![CI](https://github.com/outten/tech-feed-reader/actions/workflows/ci.yml/badge.svg)](https://github.com/outten/tech-feed-reader/actions/workflows/ci.yml)\n[![Release](https://img.shields.io/github/v/release/outten/tech-feed-reader)](https://github.com/outten/tech-feed-reader/releases)\n[![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)](LICENSE)\n\nA 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.\n\n\u003e **🎉 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).\n\n\u003e **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.**\n\n## What it does\n\n- **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.\n- **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.\n- **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.\n- **Home \u0026 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.\n- **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.\n- **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.\n- **Sports** (`/sports`) — followed-team score tiles, iCal calendar export, per-team detail pages, league standings, tennis rankings, and \"articles mentioning Sinner\"-style entity surfaces.\n- **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.\n- **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.\n- **Comics** (`/comics`) — subscribed webcomic series tiles (latest panel as cover) + recent panels list; comic-aware article hero (no crop) + click-to-zoom image lightbox.\n- **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\u0026P 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.\n- **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.\n- **Topics \u0026 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.\n- **Observability** — `/health` (liveness JSON), `/metrics` (Prometheus), `/admin/dashboard` (article counts + 7-day Activity chart), `/admin/traces` (OpenTelemetry ring buffer).\n- **CLI** — `make refresh-feed FEED=\u003cid-or-url\u003e`, `make refresh-feeds`, `make scheduler`, `make digest`, `make triage`, `make sync-sports`, `make run-all` / `make stop-all`.\n\n## Getting started\n\n```bash\nmake install\nmake seed-feeds # optional: insert the 5 starter feeds (browse 222 more on /feeds)\nmake run        # dev server with rerun auto-reload → http://localhost:4567\nmake test       # RSpec — smoke suite passes out of the box\n```\n\nRuns 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.\n\n## Pages\n\n| Page | URL | What it shows |\n|---|---|---|\n| 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 |\n| About | `/about` | Philosophy, anti-swivel-chair argument, how-it-works, tech stack |\n| 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 |\n| Bookmarks | `/bookmarks` | Every article you've saved with the ☆ button, newest first; reuses /articles list affordances |\n| Article | `/article/:uid` | Single article + cached summary + Read-next. Podcasts get a \"Play episode\" affordance; YouTube articles embed the player with watch-progress + resume |\n| Topics | `/topics` | Trending term clusters with sample articles; 7/14/30-day window selector |\n| Topic detail | `/topics/:term` | Synthesized \"highlights\" + every article in the cluster, summaries inline |\n| Podcasts | `/podcasts` | Subscribed shows grouped freshest-first + recent episodes |\n| YouTube | `/youtube` | Subscribed YouTube channels grid + \"+ Add channels\" bulk-textarea (resolves @handles via channel-page scrape) |\n| YouTube channel | `/youtube/:feed_id` | 10 most recent videos for one subscribed channel as 16:9 tiles |\n| Comics | `/comics` | Subscribed webcomic series tiles (latest panel as cover) + recent panels linear list |\n| 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 |\n| Games | `/games` | Daily games index — progress tiles for Sudoku and News Trivia |\n| Sudoku | `/games/sudoku` | Daily 9×9 puzzle (shared, one per day); pencil notes, live timer, AJAX autosave, leaderboard |\n| News Trivia | `/games/trivia` | 5 Claude-generated multiple-choice questions from today's articles; progressive reveal + explanations |\n| Sports | `/sports` | Followed-team score tiles + per-sport landings (NFL / NBA / soccer / rugby / tennis). Calendar + standings + per-team detail + tennis player follows nested below |\n| Sports calendar | `/sports/calendar` | Upcoming fixtures across followed teams + iCal export |\n| 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) |\n| 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 |\n| 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 |\n| 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 |\n| Triage | `/triage` | AI triage (Claude Sonnet 4.6) — classifies unread into must-read / optional / skip with rationale; per-topic chips; daily cron history |\n| Digests | `/digests` | Daily snapshots of unread articles + summaries (produced by `make digest`) |\n| Digest detail | `/digests/:id` | Inline render of a stored digest + opt-in \"Summarize with Claude\" |\n| 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 |\n| Tags | `/tags` | User-defined tag rules + activity |\n| Search | `/search` | Full-text search across article history (PG `tsvector` + `ts_headline`); pre-search suggestion chips, card-style results with snippet highlighting |\n| Bus mode | `/bus` | Podcast episodes ≤15 min — pick something for the commute |\n| Sign up | `/sign-up` | Passkey registration ceremony; receive 10 single-use recovery codes (shown once) |\n| Sign in | `/sign-in` | Passkey authentication; \"Use a recovery code\" fallback |\n| 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 |\n| Account | `/account` | Manage display name, list / add / revoke passkeys, regenerate recovery codes, delete account (typed-confirmation gate) |\n| Admin | `/admin` | System overview, integration status, sub-page links |\n| Admin dashboard | `/admin/dashboard` | Article counts, 7-day Activity chart, top feeds + tags |\n| Cache admin | `/admin/cache` | Per-feed cache age + manual refresh |\n| Provider health | `/admin/health` | Per-feed fetch health |\n| Backgrounds | `/admin/backgrounds` | Page-background image pool (refreshes 100 random IDs from Picsum) |\n| Traces | `/admin/traces` | Recent OpenTelemetry spans (in-memory ring buffer) |\n| Health (JSON) | `/health` | Liveness probe + dependency checks |\n| Metrics | `/metrics` | Prometheus exposition format |\n\n## Documentation\n\n- **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** — system topology, request lifecycle, ingestion pipeline, data model, and deploy pipeline, with Mermaid diagrams.\n- **[SPEC.md](SPEC.md)** — project brief: goals, non-goals, data model, page list, roadmap.\n- **[AGENTS.md](AGENTS.md)** — architecture, caching contract, store inventory, common gotchas.\n- **[CONTRIBUTING.md](CONTRIBUTING.md)** — PR workflow (branch / commit / tests / CI / merge).\n- **[DEVELOPER.md](DEVELOPER.md)** — pointer doc for new contributors.\n- **[DEPLOYMENT.md](DEPLOYMENT.md)** — production runbook (Docker Compose, Caddy, DigitalOcean).\n\n## License\n\nLicensed 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.\n\n## Conventions inherited from `t-money-terminal`\n\nBranch 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).\n\n## Feline contributors\n\nThe codebase has occasional unsolicited assistance from the maintainer's cats, who sleep on the keyboard. Their contributions to date include:\n\n```\n]88DLSK34///œ¡\t≥™ `\t‘’:?{))))))))))))))))))))))))))))))))))))))))))))))))))))\n```\n\nNone have shipped to `main` (yet). Pull requests reviewed but not merged. 🐈\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutten%2Ftech-feed-reader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutten%2Ftech-feed-reader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutten%2Ftech-feed-reader/lists"}