A self-hosted, single-user RSS reader with full-text article fetching, Reddit comment threads, and a daily AI digest — no cloud, no tracking, no algorithmic interference.
Heads up: This project was vibe-coded — built iteratively with AI assistance, not architected upfront. I use it every day as my primary RSS reader and it works well for me, but it hasn't been battle-tested by a wide audience. Expect rough edges. Issues and PRs are welcome.
| Layer | Technology |
|---|---|
| Backend | Python 3.12, FastAPI, SQLAlchemy 2 (async), aiosqlite |
| Frontend | SvelteKit 2, Svelte 5 (runes), TailwindCSS 3 |
| Database | SQLite with FTS5 full-text search |
| LLM | Ollama (local inference, GPU or CPU) |
| Feed conversion | RSSHub (Bluesky, YouTube, and 400+ other sources) |
| Deployment | Docker Compose + nginx reverse proxy |
git clone https://github.com/AdrienLF/readr
cd readr
# Pull the Ollama model (first run only)
docker compose up -d ollama
docker exec -it readr-ollama-1 ollama pull qwen3.5:9b
# Start everything (auto-detects GPU)
bash start.sh
Open http://localhost:7755 in your browser.
The start.sh script auto-detects nvidia-smi and enables GPU passthrough if available (requires NVIDIA Container Toolkit or Docker Desktop with WSL2 GPU support). To force CPU mode:
docker compose up -d
| Source | URL format |
|---|---|
| Any RSS/Atom feed | Direct feed URL |
| Reddit subreddit | https://www.reddit.com/r/{subreddit}.rss |
| Bluesky user | http://rsshub:1200/bsky/user/{handle} |
| YouTube channel | http://rsshub:1200/youtube/user/{username} |
| GitHub releases | https://github.com/{user}/{repo}/releases.atom |
RSSHub supports hundreds of additional sources — see docs.rsshub.app.
Settings are available through the UI (/settings) or via environment variables:
| Setting | Default | Description |
|---|---|---|
digest_time |
07:00 |
Daily digest generation time (HH:MM) |
ollama_model |
qwen3.5:9b |
Ollama model used for digests |
fetch_interval |
3600 |
Feed poll interval in seconds |
Environment variables:
| Variable | Default |
|---|---|
OLLAMA_BASE_URL |
http://ollama:11434 |
RSSHUB_BASE_URL |
http://rsshub:1200 |
DATABASE_URL |
sqlite+aiosqlite:////app/data/rss_reader.db |
cd backend
uv sync
uv run uvicorn app.main:app --reload # dev server on :8000
uv run pytest # run all tests
cd frontend
npm install
npm run dev # dev server on :5173
npm test # unit tests (vitest)
readr/
├── backend/
│ └── app/
│ ├── main.py FastAPI app + lifespan
│ ├── models.py SQLAlchemy ORM models
│ ├── schemas.py Pydantic schemas
│ ├── routers/ REST API endpoints
│ └── services/
│ ├── fetcher.py Feed polling + Reddit normalization
│ ├── extractor.py Full-text extraction (trafilatura)
│ ├── scheduler.py APScheduler (poll + digest cron)
│ ├── llm.py Ollama digest generation
│ └── smart_search.py Saved-search matching
└── frontend/
└── src/
├── routes/ SvelteKit pages
└── lib/
├── api.js Typed fetch wrapper
├── stores/ Svelte 5 reactive state
└── components/ UI components
Readr is designed for local / private network use (e.g. behind Tailscale). It has no authentication layer — anyone who can reach the port can use it. Do not expose it to the public internet.
Protections in place:
MIT