I read too much news, badly. A dozen RSS feeds, three “read it later” apps I never went back to, and the creeping sense that the algorithm-driven version of all this was optimizing for someone else’s goals, not mine. So I built the thing I actually wanted, a personal news digest that runs on my own hardware, ranks stories with a model I control, and never phones home. It’s called Cruxwire, and it’s open source.

What it is

Cruxwire is a self-hosted news reader in a single Docker container. You point it at your RSS/Atom feeds and at your own Ollama server, and a local LLM does the work a feed reader won’t. It scores every article 0–10 for relevance against the interests you define, writes a one- or two-sentence summary, tags it, and embeds it for clustering and search. The result is a clean, ranked digest of what’s actually worth your attention. There is no infinite scroll, no engagement bait, no cloud.

The whole thing is deliberately small: one Python process serving the UI and running an in-process scheduler, a vanilla-JS single-file frontend, and state stored as plain JSON on a volume. No database, no build step, no external services beyond the feeds you choose and the Ollama endpoint you already run.

How it decides what rises to the top

This is the part I’m most happy with. A story’s rank blends four signals:

  • Relevance — the local model’s 0–10 score against your category interest descriptions.
  • Cross-source coverage — articles about the same story across outlets are clustered by embedding similarity and shown as one card; the more coverage, the bigger the (bounded) boost, so a genuinely big story climbs without one noisy topic swamping the page.
  • Your taste — an embedding-based “taste” vector quietly boosts stories similar to the ones you save and open.
  • Source affinity — a per-source multiplier (0.5×–2.0×) that drifts up for sources you read and down for ones you keep dismissing.

It all learns quietly in the background. There’s no manual tuning, no “rate this article” friction. It watches what you open, save, and dismiss, and adjusts.

An inbox that regulates itself

The other problem I wanted to solve was the feed reader that’s either empty on a slow Sunday or a thousand-item wall on a busy news day. Cruxwire keeps unread stories between a floor and a ceiling, and each story’s lifespan scales with its rank: the best stories linger for up to a few days, weak ones age out fast, and a hard cap guarantees nothing stale overstays its welcome. Stories you haven’t gotten to are carried forward across runs, so a good piece doesn’t vanish just because its feed rotated it out. Read Later is curated by hand and never expires.

Local by default, private by design

Nothing leaves your network. No accounts, no analytics, no third-party calls beyond fetching your feeds and talking to your Ollama box. Scoring, embeddings, and semantic search all run locally on hardware you own. The flip side: Cruxwire
ships with no authentication by design. It’s built for a single trusted user on a private network.

A few of the niceties

  • On-demand TL;DR — for a saved story, pull a summary of the actual article body (Cruxwire fetches and summarizes the real page, not just the feed blurb).
  • Semantic search — find stories by meaning, not keywords, entirely on local embeddings.
  • Blocklists — literal keywords plus a semantic “topics to avoid” filter, on top of a built-in spam/deal filter.
  • Live settings — every threshold, schedule, and retention band is editable in the app and applies on the next run, no restart.
  • Multi-device — read state syncs through the server, and it works offline from local cache.

Running it yourself

If you can run docker compose up, you can run Cruxwire. You’ll need Docker and a reachable Ollama server with a chat model and an embedding model pulled (I use qwen3 and nomic-embed-text). Clone the repo, point it at your Ollama host, and bring it up — it’s running in about a minute.

There’s a full walkthrough, the architecture, and every tunable knob documented on the site:

It’s early, it’s opinionated, and it scratches a very specific itch, but it’s been quietly running on my homelab for a while now and it’s genuinely changed how I keep up with things. If you give it a spin, I’d love to hear what you think.