DEV Community

Cover image for Link Séance: a local Gemma writes epitaphs for the dead links in your bookmarks (and isn't allowed to make anything up)
Abdul AmberNeel
Abdul AmberNeel

Posted on

Link Séance: a local Gemma writes epitaphs for the dead links in your bookmarks (and isn't allowed to make anything up)

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission 👻🌼

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

What I Built

Every old list of links is slowly turning into a graveyard. A README from 2014, a "useful resources" blog post, the bookmarks folder you haven't opened since college. Nobody deletes those pages. They just stop answering, one by one, and you only find out when you click.

Link Séance is a small command-line tool that holds a séance for them. You give it any file with URLs in it (plain text, Markdown, or a browser's bookmarks.html export) and it:

  1. checks every link's pulse. Each one comes back alive, DEAD (DNS gone, broken TLS, 404/410/5xx), or WANDERING: it still answers, but only by redirecting you to a shutdown notice or to whoever swallowed it.
  2. summons each lost page from the Internet Archive. It finds the last moment the page was still itself in the Wayback Machine and reads what the archive kept.
  3. lets a local open-weight model speak for it. Gemma 3, running on my own CPU, writes a two-sentence epitaph: what the page was, then a farewell.
  4. lays them on an ofrenda. OFRENDA.md goes in your repo, and ofrenda.html is a small candle-lit page with papel picado and flickering flames that you can open anywhere, offline.

The theme asked for something spooky or something that honours the past. Dead links are both. They're the ghosts of the web: still listed, still clickable, gone. For Día de los Muertos I wanted to give them what an ofrenda gives the people we've lost: a name, the dates, a few true words, and a candle.

The important word there is true. That's where most of the engineering went.

Demo

Here's a real run on a reconstructed 2009 bookmarks list: Google Reader, Posterous, Google Wave, Google+, GeoCities, EveryBlock, Yahoo Answers, Megaupload, with Orkut, Wikipedia, Vine and Joel on Software mixed in as the living.

Link Séance terminal session

A few of the epitaphs, exactly as Gemma wrote them and the guardrail accepted them:

Google Wave Preview (last seen alive 9 September 2009)
"Here lies Google Wave Preview, a tool for communication and collaboration on the web. It waits quietly now, hoping to connect with others."

EveryBlock (today it redirects to Nextdoor; last seen alive 15 July 2018)
"Here lies EveryBlock, the site for neighborhood discussions and local news. Its purpose is now complete; may its conversations continue elsewhere."

Yahoo Answers (today it redirects to Yahoo Search; last seen alive 1 May 2021)
"Here lies Yahoo Answers, a platform for discussing 100M+ topics through questions and answers. Its voices fade now, returning to the quiet of shared knowledge."

And this is my favourite moment in the whole run, because it's the guardrail working:

🕯️ Google Reader – Google  (born 2 July 2013, last seen 1 May 2021)
    ✗ rejected draft (first sentence uses words the archive never said: managing):
      "Here lies Google Reader – Google, the page for managing subscriptions. It is now silent; its connections have been severed."
    "Here lies Google Reader – Google, the page where subscription data was systematically deleted. It rests now; its connections have been irrevocably severed."
Enter fullscreen mode Exit fullscreen mode

Google Reader really did manage your subscriptions. But the page the archive kept is the retirement notice, and the retirement notice never says that. So the claim got turned away, and Gemma was told exactly which word it had made up. Its second try was built from what the page actually said, and it's darker and more accurate: the subscription data really was systematically deleted.

Some pages don't get a model-written epitaph at all, on purpose:

  • Your actual Google Reader feed (/reader/view/) lived behind a login, so the archive never saw it alive. The ofrenda says exactly that and invents nothing.
  • Posterous: Gemma tried three times to call it "a platform for organizing and sharing information". The archived page never says any of that, so all three drafts were turned away and the entry quotes the archive's last words instead: "Posterous Spaces is no longer available Thanks to all of my @posterous peeps."
  • Megaupload and Google+ left too little readable text for an honest summary, so they're quoted too. Google+'s last words, fittingly: "Your browser is not supported by Google+."

The full outputs from that run are in the repo: OFRENDA.md, the candle-lit ofrenda.html, and the full terminal session.

Code

GitHub logo cryptocloud786 / link-seance

Find dead links, summon their last snapshot from the Wayback Machine, and let a local Gemma model write each one an epitaph. Offline-first, MIT.

🕯️ Link Séance

Find the dead links in a list, summon their last snapshot from the Internet Archive, and let a small open-weight model running on your own machine write each one an epitaph.

Give it an old bookmarks export, a README, or a blog post. It checks every link's pulse, asks the Wayback Machine when each lost page was last seen as itself, reads what the archive kept, and lets a local Gemma model write a two-line epitaph from those words and nothing else. The results are laid out on an ofrenda: OFRENDA.md for your repo, and ofrenda.html, a small candle-lit page you can open in any browser.

Link Séance running on a list of 2009 bookmarks

Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 4: Ghost in the Machine.


Why

A lot of the web many of us grew up on is gone: Orkut scrapbooks, Posterous blogs, the Google Reader tab that was always…




It's one Python file (standard library plus llama-cpp-python), MIT licensed, with offline tests for the guardrail:

pip install -r requirements.txt
curl -L -o models/gemma-3-4b-it-Q4_K_M.gguf \
  https://huggingface.co/ggml-org/gemma-3-4b-it-GGUF/resolve/main/gemma-3-4b-it-Q4_K_M.gguf
python link_seance.py your-bookmarks.html --model models/gemma-3-4b-it-Q4_K_M.gguf
Enter fullscreen mode Exit fullscreen mode

Or with Ollama: python link_seance.py links.md --backend ollama --model gemma3:4b.

How I Built It

The model: Gemma 3, locally, through llama.cpp. I used Google's open-weight gemma-3-4b-it as a 4-bit GGUF (2.5 GB) through llama-cpp-python, on a plain 2-core CPU with no GPU. The whole 13-link séance above took about six and a half minutes, most of it Gemma thinking. I also tested gemma-3-1b-it (800 MB). It runs about four times faster and works fine, but its epitaphs were vaguer and drifted into generic web words more often, so the guardrail sent more of them back. 4B was the sweet spot for writing that's both specific and grounded. There's an Ollama backend too, for anyone who already has gemma3 pulled.

Finding when a page actually died. This turned out to be the interesting problem. The Wayback Machine keeps capturing a URL after it dies, and those later captures often come back as HTTP 200: an error page, a parking page, or the redirect target itself. Ask for "the latest snapshot" of answers.yahoo.com and you get Yahoo Search. So Link Séance:

  • asks the CDX API for one capture per month,
  • records what the URL redirects to today (the impostor),
  • then binary-searches the timeline for the last month where the snapshot still looked like the original page: not an error, not a parking page, and not the impostor.

On the two I checked by hand, it lands on the right month. Megaupload's last living capture is January 2012 (the site was seized on 19 January 2012). Yahoo Answers' is May 2021 (it closed that month). Nobody told the tool either date. It's an estimate, not a death certificate: for Google Wave it stops in September 2009, when the preview page changed, even though Wave lived on until 2012.

The guardrail: the medium may only speak from the archive. This was the part I cared about most. A small model is wonderfully fluent, and that's exactly the danger when you ask it to describe something that's gone: it'll fill the gap with what it thinks the page was. On an ofrenda that's a lie told about the dead. So every draft has to pass verify() before it can go on the ofrenda:

  • No invented names or numbers. Every proper noun and number in the epitaph must appear in the retrieved text.
  • The first sentence must be built from the archive's own words. At least 60% of its content words (beyond the page's title) must appear in the snapshot.
  • No first person. The ghost doesn't get to say "we".
  • Two sentences, starting "Here lies".

When a draft fails, it isn't silently retried. Gemma is shown its own draft and told precisely why it was rejected ("first sentence uses words the archive never said: managing") and asked again. After three failures the tool stops asking and quotes the archive verbatim. If a page has too little text to summarise, it quotes. If there's no living snapshot at all, it says so. Rejected drafts are never hidden: they're printed in the terminal and folded away under each entry in OFRENDA.md, so anyone can audit what the model tried to say.

The check is deliberately simple and deliberately strict, so I can explain every decision it makes. It works on words, not meaning, so it can't judge whether a farewell line is too sentimental. But it reliably stops the thing that matters here: a confident, plausible description the archive never gave.

Offline-first. Every pulse check, CDX answer, and snapshot is cached on disk. Run a séance once with a connection, and --offline replays it from the cache with the network unplugged. The model never needs the network at all.

Why Does Open Innovation Matter?

Because this tool is about things that disappeared when the company behind them changed its mind.

Google Reader, Posterous, Google+, Yahoo Answers: every one of them was a closed service someone depended on, and every one of them was switched off. It would be a strange kind of tribute to mourn them with a tool that depends on another closed API, one that could change its pricing, deprecate the model, or shut down next year.

Open weights make a different promise. The Gemma file I downloaded will run exactly the same in ten years as it does tonight. It doesn't need an account, a key, or anyone's permission. So the whole thing is made only of pieces that can't be taken away: an open model, an open-source runtime (llama.cpp), the Python standard library, and the Internet Archive, a non-profit whose entire purpose is that the web shouldn't simply vanish.

Running locally also changed what I could build:

  • It works with the lights off. Once the archive answers are cached, the séance runs with no internet at all, on a train, on a plane, on a laptop in a village with bad signal.
  • Your history stays yours. A bookmarks export is surprisingly personal: old projects, old interests, old accounts. With a local model, the only things that leave your machine are the link checks and the archive lookups, which have to. None of it goes to a model provider.
  • I could see, and constrain, every word. Because the model runs in my own process, the guardrail can inspect each draft, reject it, explain why, and ask again, as many times as it needs, for free. With a metered cloud API, a three-strikes verify-and-retry loop on every entry costs money, so you'd be tempted to accept the first answer. Here the honest path is also the cheap one.
  • Small is enough. A 4-bit, 4-billion-parameter open model on a two-core CPU writes something tender and accurate about a GeoCities neighbourhood from 1999. You don't need a frontier model to keep a memory alive. You need the right constraints around a small one.

The Spirit of It

The moment it landed for me was watching the run reach Yahoo Answers.

Today, answers.yahoo.com quietly forwards you to Yahoo Search, as if it had never existed. Link Séance noticed the impostor, searched back through years of monthly snapshots, and stopped at May 2021, the last month the page was still itself. Then a small open model running on a plain CPU read its last words and wrote:

"Here lies Yahoo Answers, a platform for discussing 100M+ topics through questions and answers. Its voices fade now, returning to the quiet of shared knowledge."

The "100M+" came straight from the page's own last words. Nothing was invented, and nothing went to a model provider. The only traffic was a few polite questions to the Internet Archive.

That's what I wanted: not a spooky chatbot pretending to be the dead, but a candle, a date, and something true. If you run it on your own old bookmarks, I'd love to hear whose name turns up on your ofrenda.

🕯️ Please consider donating to the Internet Archive. They keep the candles lit for all of us.

Prize Categories

  • Best Use of Gemma: the epitaphs are written by Google's open-weight Gemma 3 (gemma-3-4b-it, 4-bit GGUF) running locally through llama.cpp, with Gemma 3 1B also tested and supported, plus an Ollama backend for gemma3.

Top comments (0)