{"slug": "i-brought-google-reader-and-yahoo-pipes-back-from-the-dead-with-ai-at-it-s-core", "title": "I Brought Google Reader and Yahoo Pipes Back From the Dead with AI at it's core!", "summary": "A developer built Tributary, a self-hosted feed reader that combines Google Reader-style reading with a Yahoo Pipes-style visual pipeline builder, running a local gemma3:1b model through Ollama on an 8 GB, GPU-less 2013 laptop. Pipes are ordered lists of boxes (fetch, search, merge, dedupe, filter, sort, limit, fulltext, summarize, meaning, whycare) that each take and return item lists, letting users reshape feeds and publish results back into the reader. The project is open source with no public demo, since the self-hosted build lacks login and per-user limits.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine](https://dev.to/challenges/hacktoberfest-week4-2026-10-26)*\n\nGoogle Reader closed in 2013. Yahoo Pipes closed in 2015. Nothing has replaced either of them, and I have been quietly annoyed about it ever since.\n\nReader was the calm inbox: unread counts, stars, `j` and `k`, no algorithm deciding what I saw. Pipes was the clever one: drag a few boxes onto a canvas, wire them together, and get a brand-new feed out the other end. Both were killed because someone else owned the server.\n\nSo for \"Ghost in the Machine\" I put the two ghosts in one body. **Tributary** is a self-hosted feed reader whose feeds you shape with a visual pipe builder, and a small local Gemma sits inside as one of the boxes. It runs on my 2013 laptop: 8 GB of RAM, no GPU, Ubuntu.\n\n**The short version**\n\n`gemma3:1b` through Ollama: Two dead products, one app. Here is what I kept from each:\n\n| Ghost | What it was | What Tributary keeps | \n|---|---|---|\n| Google Reader (gone 2013) | A quiet inbox for the sites you chose | Views for All, Unread and Starred, labels, per-feed unread counts, and keyboard shortcuts ( `j` /`k` ,`o` ,`s` ,`m` ,`r` ,`/` ). No ranking, ever. | \n| Yahoo Pipes (gone 2015) | Wire boxes together to reshape a stream | A React Flow canvas at `/pipes` . Drag boxes, connect them, run, inspect every step, and publish the result back into the reader as a feed. | \n\nThe first ghost is the lost product: the thing you loved that vanished. The second is the one in the title, the model inside the machine, doing a small job quietly on hardware that should have been retired years ago.\n\nA pipe is an ordered list of boxes, and every box takes a list of items and returns a list of items. That one rule is the whole engine.\n\n| Group | Boxes | \n|---|---|\n| Sources | `fetch` (your stored feeds),`search` (SerpApi, for the web beyond your subscriptions) | \n| Combine | `merge` ,`dedupe` | \n| Transform | `filter` ,`sort` ,`limit` | \n| Enrich | `fulltext` (fetch and extract the article body) | \n| Local AI | `summarize` ,`meaning` ,`whycare` | \n\nA pipe I actually use looks like this on the canvas:\n\n``` php\nfetch (Hacker News + 3 blogs) -> dedupe -> fulltext -> meaning (\"practical engineering, no hype\") -> summarize -> sort -> limit\n```\n\nRun it and it comes back as a feed in the reader, in order, with one-sentence summaries. That is the Pipes idea finally sitting inside the Reader idea.\n\nVIDEO DEMO COMING SOON\n\nThere is **no public demo link**, and that is deliberate. The self-hosted build has no login, so putting it on the open web would hand everyone a free reader and a free model on my bill. The hosted version needs accounts and per-user limits first. That is the cloud tier, and it is coming soon.\n\n**Run it yourself in about ten minutes.** You need Python 3.11+, [uv](https://docs.astral.sh/uv/), Node with pnpm, and [Ollama](https://ollama.com).\n\n```\ngit clone https://github.com/Ekomjah/tributary\ncd tributary\nollama pull gemma3:1b\ncp .env.example .env\n\ncd backend\nuv sync\nuv run tributary init-db\nuv run tributary serve\n```\n\nIn a second terminal:\n\n```\ncd frontend\npnpm install\npnpm dev\n```\n\nOr you can simply run `make setup` , then `make dev` to spin it all up quickly \n\nOpen `http://localhost:3000`, click **Add feed**, and paste `https://news.ycombinator.com/rss`. Paste a blog's homepage instead and Tributary will look for its feed. Then run `uv run tributary doctor` to see what is wired up.\n\nTwo things are optional, and both are off until you set a key in `.env`:\n\n`SERPAPI_API_KEY` turns on the `search` box.`SENTRY_DSN` turns on tracing, so you can watch each pipe run as a trace.\nA free-tier key for either is enough to try the features\n\n| Layer | Choice | Job | \n|---|---|---|\n| Reader and editor | Next.js 16, React 19, TanStack Query, Tailwind 4, React Flow | Feed list, article view, drag-and-wire canvas | \n| API and engine | Python, FastAPI, Pydantic, SQLAlchemy | Validate and run pipes, serve the reader | \n| Ingestion | feedparser, httpx, trafilatura | Read feeds, extract readable article text | \n| Storage | SQLite | Feeds, items, labels, pipes, runs, caches | \n| Local model | Ollama running `gemma3:1b` | Summaries, meaning filter, relevance notes | \n| Web search | SerpApi, opt-in | Results beyond your subscribed feeds | \n| Monitoring | Sentry, opt-in | Traces per run, metrics, GenAI spans | \n\n**A box is a small list transform.** This is the shape of every one of them:\n\n``` python\nclass WhyCareBox:\n    box = \"whycare\"\n\n    def run(self, items: list[ItemOut], context: RunContext) -> list[ItemOut]:\n        ...\n```\n\nBecause every box has the same signature, boxes compose in any order, and each one is easy to test. The backend validates a pipe's JSON with strict Pydantic models before a run starts, so unknown keys and bad values are rejected up front.\n\n**The model's job is small and the failure mode is boring.** I did not want a 1B model making decisions it cannot be trusted with, so the boxes lean toward keeping things:\n\nAnswers are cached by `(item, box, params hash, model)`. The hash includes a prompt version and a SHA-256 of the text, so an edited article or a changed criterion misses the cache, and repeating a run is free. That matters on a CPU-only laptop. Model errors report the host and failure type, never the prompt or the article.\n\n**Search starts with a local model.** You write \"AI regulation this week\" and Gemma rewrites it into a structured query before SerpApi sees anything:\n\n``` php\n\"AI regulation this week\"\n        |\n        v\nOllama refinement (local) -> q, engine, gl, hl, freshness, max_results\n        |\n        v\nSerpApi (cloud, opt-in)\n        |\n        v\nnormalize -> items -> six-hour cache -> pipe / reader\n```\n\nThe refinement uses a Pydantic schema at temperature 0, an allowlisted set of engines, clamped result counts, and a fallback to your raw query if Ollama is down or returns bad JSON. The CLI prints the query it sent, so you can see what the model did to your words.\n\n**Privacy is spelled out, not implied.**\n\n| What | Where it goes | \n|---|---|\n| Feeds, items, stars, labels, extracted text | A local SQLite file | \n| Article text for AI boxes | Your local Ollama only (cloud model is a separate opt-in) | \n| SerpApi | The refined query and options. Never article text or reading history | \n| Sentry | Nothing, unless you set a DSN. API keys and URL query strings are redacted | \n\n**RSS feeds don't deliver data in the same structure** There is no way to guess the structure in which an RSS feed gives us it's data. While one may simply dump the article's text in the XML, others may just include a link to it with a brief summary of the article. This was a challenge since m RSS file had to properly display it's reader data without making it's users go somewhere else to read more on it. I had to use `BeautifulSoup` and `trafilatura` python packages to fetch and transform inconsistent RSS feeds into well formatted HTML to be parsed by the reader \n\n**My first summaries summarized nothing.** The Hacker News feed does not contain the articles. Each entry is a title and a line that says `Article URL: https://...`. So my first Summarize box was faithfully summarizing a link. The fix was the `fulltext` box, which fetches the page and extracts the body with trafilatura, and a rule that Summarize only reads real text.\n\n**A blog URL \"worked\" and gave me nothing.** I pasted a blog's homepage, got a 200 response, zero items, and a healthy-looking feed. I asked a coding agent to audit my fetcher and it returned **34 findings**. The worst one was exactly this: an HTML page was being counted as a successful fetch. The fetcher now checks that the response really is a feed, rejects empty bodies and HTML pages, follows redirects one hop at a time and re-checks every hop so a public feed cannot bounce into a private address, caps responses at 2 MB, and has a discovery step that finds a feed from a blog URL.\n\n**Apostrophes arrived as `’`.** Feed text carries HTML entities, and my cleanup skipped short snippets. Titles and summaries are now normalized and feed HTML is sanitized before it is stored.\n\n`tributary refresh`.\nGoogle Reader and Yahoo Pipes are not gone because they were bad. They are gone because they lived on someone else's server and that someone changed their mind. That is the whole argument for open, and it shaped Tributary:\n\n`OLLAMA_MODEL` swaps it. A bigger model on a better machine is one environment variable away.\n**Where a closed model would be better.** A frontier model would almost certainly judge \"is this practical engineering without hype?\" better than a 1B model, and would write sharper summaries. I have not measured the gap, so I will not put a number on it. The honest trade is quality against ownership, and for a tool I want to still have in ten years I chose ownership. There is also an opt-in cloud model (`gemma4:3b` through Ollama Cloud) for people who want a stronger model. When it is on, article text and prompts do leave your machine, and the docs say so.\n\nThe tools that quietly let you read what you wanted, in the order you wanted, until someone shut the server off.\n\n`gemma4:3b` cloud mode is available for larger models.`gen_ai.chat` spans with model and token counts for every AI and refinement call, and feed-refresh and search metrics. Keys and URL query strings are redacted, and prompt capture is a separate switch that is off by default. The cloud tier. The code is built around the same service layer, with PostgreSQL and Auth0 accounts, kept as an adapter rather than a fork so the self-hosted build never grows a login screen. I am not opening signups until per-user limits and abuse protections are in. After that: a built-in scheduler, durable pipe runs, OPML, and an evaluation set from my Keep/Drop labels so I can finally try a fine-tune and report numbers, whether or not they flatter me.", "url": "https://wpnews.pro/news/i-brought-google-reader-and-yahoo-pipes-back-from-the-dead-with-ai-at-it-s-core", "canonical_source": "https://dev.to/ekomjah/i-brought-google-reader-and-yahoo-pipes-back-from-the-dead-with-ai-at-its-core-53aa", "published_at": "2026-11-01 02:39:45+00:00", "updated_at": "2026-11-01 02:52:29.645240+00:00", "lang": "en", "topics": ["ai-tools", "ai-products", "large-language-models", "developer-tools"], "entities": ["Tributary", "Google Reader", "Yahoo Pipes", "Ollama", "gemma3:1b", "SerpApi", "Next.js", "FastAPI"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/i-brought-google-reader-and-yahoo-pipes-back-from-the-dead-with-ai-at-it-s-core", "markdown": "https://wpnews.pro/news/i-brought-google-reader-and-yahoo-pipes-back-from-the-dead-with-ai-at-it-s-core.md", "text": "https://wpnews.pro/news/i-brought-google-reader-and-yahoo-pipes-back-from-the-dead-with-ai-at-it-s-core.txt", "jsonld": "https://wpnews.pro/news/i-brought-google-reader-and-yahoo-pipes-back-from-the-dead-with-ai-at-it-s-core.jsonld"}}