A self-hosted personal news digest that runs on your own hardware. A local
LLM scores, summarizes, and de-duplicates your feeds, then learns what you
actually read. No cloud, no accounts, no tracking.

What it is

Cruxwire is a news reader in a single Docker container. Point it at your RSS/Atom
feeds and your own Ollama server, and a local model turns
the firehose into a clean, ranked digest of what’s actually worth reading —
scored, summarized, and de-duplicated, all on hardware you control.

It’s deliberately small: one Python process serving the UI and an in-process
scheduler, a vanilla-JS single-file frontend, and state kept as plain JSON on a
volume. No database, no build step, no external services beyond your feeds and
your Ollama endpoint.

How it works

On a schedule (every couple of hours during your waking window, plus once on
start), each run:

  1. Fetches & filters your feeds, dropping anything too old or on your
    blocklists.
  2. Carries forward unread stories so a good piece doesn’t vanish when its
    feed rotates it out.
  3. Scores, summarizes & embeds each article 0–10 via your local model.
  4. Clusters same-story coverage across outlets into a single card.
  5. Retains within a floor/ceiling band by rank-weighted lifespan.
  6. Writes the new digest atomically — the frontend picks it up on next load.

Ranking blends four signals: the model’s relevance score, a bounded
cross-source coverage boost, an embedding-based “taste” match to what you save and
open, and a learned per-source affinity multiplier (0.5×–2.0×) that moves with
your opens, saves, and dismisses.

Retention keeps unread stories between a floor and a ceiling. Each story’s
lifespan scales with its rank — the best linger for up to a few days, weak ones
age out fast — with a hard age cap so nothing stale lingers. Read Later is
curated by hand and never expires.

Features

  • LLM-ranked digest — 0–10 relevance, a one-line summary, and a category for
    every article, from your local model.
  • Source-agnostic dedup — same story across outlets collapses into one card
    with the rest of the coverage underneath.
  • Automatic personalization — learns from what you open, save, and dismiss.
    No manual tuning.
  • On-demand TL;DR — summarize the real article body of a saved story, not
    just the feed blurb.
  • Semantic search — find stories by meaning, not keywords, 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-app and applies on the next run, no restart.
  • Multi-device state — read state syncs through the server and works offline
    from local cache.

Privacy

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 search all run locally. Cruxwire ships with no authentication by design —
it’s built for a single trusted user on a private network, so keep it on your LAN
or a Tailscale/WireGuard tailnet, never exposed directly to the internet.

Running it

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 (defaults:
qwen2.5 and nomic-embed-text). The setup guide
walks through requirements, the Compose file, and tuning.

Tech: Python (standard library) · Ollama · vanilla JS · JSON-on-a-volume ·
Docker · MIT licensed.ur own hardware, and then gets out of your way. It’s effectively killed doom scrolling for me.