{"id":"19311a68-c800-43d2-aa6f-6d3e677f2a5a","slug":"lion-reader","kind":"mcp","name":"lion-reader","summary":"One fast, private reader for the sites you follow, your email newsletters, and the articles you save for later. Free, open source (MIT), and self-hostable.","owner":"brendanlong","repoUrl":"https://github.com/brendanlong/lion-reader","license":"MIT","licenseTier":"permissive","trust":"community","stars":18,"match":null,"fewStars":false,"format":"mcp","createdAt":"2025-12-26T22:48:46.000Z","addedAt":"2026-11-04T06:47:27.601Z","logoUrl":"https://avatars.githubusercontent.com/u/1447206?u=61d5e448b74491c04155395415c47451bd1712c2&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["productivity"],"tags":["newsletter-subscriptions","newsletters","reader","rss","rss-aggregator","rss-reader"],"runtime":"remote","local":false,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-11-04T22:34:21.081Z","sample":false,"url":"https://conduits.dev/tools/lion-reader","language":"TypeScript","transports":["http"],"install":{"remoteUrl":"https://lionreader.com/api/mcp"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# Lion Reader\n\nOne fast, private reader for the sites you follow, your email newsletters, and the articles you save for later. Free, open source (MIT), and self-hostable.\n\n**[Try the interactive demo](https://lionreader.com/demo)** (no signup needed) or use the hosted version at [lionreader.com](https://lionreader.com).\n\n![The Lion Reader entry list](docs/screenshots/list.png)\n\n## What you can do\n\n- **Follow sites, read newsletters, and save articles in one place.** Subscribe to RSS, Atom, and JSON feeds, or paste a site's URL and Lion Reader finds its feed. Newsletters go to a private ingest address, so Lion Reader never needs access to your inbox. Save any page for later from the browser extension, a bookmarklet, your phone's share menu, Discord, or by uploading a document like a PDF or Word file.\n- **Read full articles, not teasers.** Links from sites like YouTube, arXiv, GitHub, Google Docs, and Bluesky come through complete and readable instead of as a sign-in page or a stub. For feeds that only send a summary, Lion Reader can fetch the full article.\n- **Use AI when you want it, and not otherwise.** Summarize an article in a click, listen to any article in a natural voice with the current paragraph highlighted, or connect Claude or another assistant over [MCP](docs/DESIGN.md#mcp-server) to search, triage, star, and save articles for you. AI features run only when you use them or connect an assistant, and you can use your own API keys.\n- **Keep using the apps you like.** Lion Reader speaks the Google Reader API (for RSS apps like NetNewsWire or Reeder) and the Wallabag API (for saving from your phone with the Wallabag app). There's also a REST API with an OpenAPI spec at `/api/openapi`.\n- **Move around instantly.** New entries appear in the list you're reading without a refresh, navigation renders from cache and refreshes in the background, and typical pages load in under 100ms. See [Frontend State](src/FRONTEND_STATE.md) for how.\n- **All the essentials.** Full-text search, tags, starring, keyboard shortcuts (press `?`), light, dark, and e-ink themes, OPML import and export, a full-library export, and an installable app (PWA).\n\n![An AI summary at the top of an article](docs/screenshots/summary.png)\n\n![Narration in dark mode, highlighting the paragraph being read](docs/screenshots/narration.png)\n\n![Lion Reader on a phone: the entry list in dark mode, and an article in the e-ink theme](docs/screenshots/mobile.png)\n\n## Privacy\n\nNo ads, no data selling, and no third-party tracking scripts. The hosted site counts page views with cookie-less [GoatCounter](https://www.goatcounter.com/), recording only the _kind_ of page (an entry list, an article, a settings page), never which list or article, and nothing tied to your account. Self-hosted builds send nothing unless you configure analytics or error tracking.\n\nA feed is fetched once for all its subscribers, but a new subscriber starts with what the feed publishes right now, never older entries Lion Reader kept from someone else's subscription. The [privacy policy](https://lionreader.com/privacy) lists every third-party service and what it receives.\n\n## Get the apps\n\n- **Web**: [lionreader.com](https://lionreader.com), installable as an app from your browser\n- **Browser extension**: [Chrome](https://chromewebstore.google.com/detail/lion-reader/mpjddkjjkckmclaifjfokjppfoenmlpl) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/lion-reader/) ([source](extension/))\n- **Android** (early, offline-first): APKs on the `android-v*` [GitHub releases](https://github.com/brendanlong/lion-reader/releases) ([source](kmp/))\n\n## Self-hosting\n\nLion Reader builds into a Docker image (`Dockerfile`) and needs PostgreSQL and Redis. It runs on a small VM and scales horizontally: the web app, the background worker (feed fetching, jobs), and the optional Discord bot are separate processes. Features that depend on outside services (OAuth sign-in, AI summaries and narration, newsletter delivery, error tracking) are optional and switch on when you configure them. Metrics are exposed for Prometheus, and logs are structured JSON.\n\nThe [Deployment Guide](docs/DEPLOYMENT.md) walks through a full Fly.io setup, including every setting that hosted lionreader.com uses which you'll want to change.\n\n### User Registration\n\nBy default, Lion Reader runs in invite-only mode. New users need an invite link to register.\n\n| Variable                          | Default | Description                                                                                  |\n| --------------------------------- | ------- | -------------------------------------------------------------------------------------------- |\n| `ALLOWED_SIGNUP_PROVIDERS`        | all     | Comma-separated providers (`email,google,apple,discord`) allowed to sign up _with_ an invite |\n| `ALLOWED_PUBLIC_SIGNUP_PROVIDERS` | none    | Subset allowed to sign up _without_ an invite. Empty = fully invite-only                     |\n| `ALLOWLIST_SECRET`                | -       | Secret for admin API (required whenever any provider is invite-only)                         |\n| `MAILGUN_DOMAIN`                  | -       | Mailgun sending domain (US region) for account emails; required to verify password accounts  |\n| `MAILGUN_SENDING_KEY`             | -       | A Mailgun sending key for that domain                                                        |\n| `TURNSTILE_SITE_KEY`              | -       | Cloudflare Turnstile site key; with the secret key, adds a CAPTCHA to password signup        |\n| `TURNSTILE_SECRET_KEY`            | -       | Cloudflare Turnstile secret key                                                              |\n\nAccounts that sign up with a password must verify their email address before using the app, and \"Forgot your password?\" emails a reset link, so password signup needs the two Mailgun settings. Social sign-ins are verified by the provider.\n\nManage invites through the admin API (each invite is single-use, valid for 7 days):\n\n```bash\n# Create an invite; response contains inviteUrl\ncurl -X POST https://your-app/api/trpc/admin.createInvite \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer $ALLOWLIST_SECRET\" -d '{}'\n\n# List invites with status (pending/used/expired)\ncurl -X POST https://your-app/api/trpc/admin.listInvites \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer $ALLOWLIST_SECRET\" -d '{}'\n\n# Revoke an unused invite\ncurl -X POST https://your-app/api/trpc/admin.revokeInvite \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer $ALLOWLIST_SECRET\" -d '{\"inviteId\":\"<uuid>\"}'\n```\n\n### Email Newsletter Subscriptions\n\nUsers create ingest addresses in Settings → Email and subscribe to newsletters with them. Each sender becomes a separate feed alongside RSS subscriptions. Unsubscribing uses RFC 2369/8058 List-Unsubscribe headers where available and blocks future emails from the sender.\n\nInbound email is delivered by [Mailgun](https://www.mailgun.com) receiving routes:\n\n1. Point MX records for your ingest domain at Mailgun and verify the domain\n2. Create a Mailgun route that forwards matching messages to `https://your-app/api/webhooks/email/mailgun` (enable \"Store and notify\")\n3. Configure Lion Reader:\n\n| Variable                      | Description                                                    |\n| ----------------------------- | -------------------------------------------------------------- |\n| `INGEST_EMAIL_DOMAIN`         | Domain for ingest email addresses; **required**                |\n| `MAILGUN_WEBHOOK_SIGNING_KEY` | Mailgun webhook signing key (Sending → Webhooks); **required** |\n\nThe webhook verifies Mailgun's HMAC-SHA256 signature; requests with invalid signatures are rejected.\n\n## Development\n\n**Stack**: Next.js 16 (App Router) and React Query over tRPC, PostgreSQL 18 with Drizzle ORM, Redis 7 for pub/sub and short-lived state, native Rust modules for feed parsing, sanitization, Markdown, and article extraction, and a Kotlin Multiplatform Android app. [DESIGN.md](docs/DESIGN.md) covers the architecture and the reasoning behind it; [SECURITY.md](SECURITY.md) maps the security-critical code.\n\n### Prerequisites\n\n- Node.js 24+ and pnpm (pnpm downloads the Node version the scripts run on)\n- Rust (`cargo`) and a C++ compiler, for the native modules\n- Docker and Docker Compose, for local Postgres, Redis and object storage (or run `pnpm services`, which starts them without Docker, and use the `*:local` script variants)\n\n### Setup\n\n```bash\npnpm install\npnpm build:native\n\n# Copy environment variables\ncp .env.example .env\n\n# Start Postgres 18, Redis 7 and S3Mock (object storage, which ebook uploads\n# need: uncomment the local STORAGE_* settings in .env)\ndocker compose up -d\n\n# Run database migrations\npnpm db:migrate\n\n# Seed the database with test data (optional)\npnpm db:seed\n\n# Start development server (app, worker, and Discord bot, which idles without a token)\npnpm dev\n```\n\nThe app will be available at http://localhost:3000. Connection URLs are configured in `.env`:\n\n- Database: `postgresql://lionreader:lionreader@localhost:5432/lionreader`\n- Redis: `redis://localhost:6379`\n\n### Testing\n\n```bash\npnpm typecheck         # Type checking\npnpm lint              # Linting\npnpm test:unit         # Unit tests (fast, no I/O)\npnpm test:integration  # Integration tests (requires Docker services)\npnpm test:e2e          # Playwright browser tests (requires Docker services)\n```\n\n### Profiling\n\nBuild for production, then run with the flame profiler:\n\n```bash\nNODE_ENV=production pnpm build:all\n\n# Profile the worker\nNODE_ENV=production pnpm dlx @platformatic/flame run --sourcemap-dirs=dist/ dist/worker.js\n\n# Profile the Next.js server\nNODE_ENV=production pnpm dlx @platformatic/flame run --sourcemap-dirs=.next/ node_modules/next/dist/bin/next start\n```\n\n## Background\n\nLion Reader is designed and built by [Brendan Long](https://www.brendanlong.com/pages/about-me.html), and largely written by Claude. More on how that went: [Claude Wrote Me a 400-Commit RSS Reader App](https://www.brendanlong.com/claude-wrote-me-a-400-commit-rss-reader-app.html) and the follow-up, [My RSS Reader is Done](https://www.brendanlong.com/my-rss-reader-is-done.html).\n\n## License\n\n[MIT](LICENSE)\n"}