<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chris Gold</title>
    <description>The latest articles on DEV Community by Chris Gold (@chrisgold).</description>
    <link>https://dev.to/chrisgold</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4213035%2Fcbc8c5ae-9f88-4108-8dd3-7e47885e72d2.png</url>
      <title>DEV Community: Chris Gold</title>
      <link>https://dev.to/chrisgold</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chrisgold"/>
    <language>en</language>
    <item>
      <title>Ofrenda: a candle for every website the web let go</title>
      <dc:creator>Chris Gold</dc:creator>
      <pubDate>Fri, 30 Oct 2026 09:11:30 +0000</pubDate>
      <link>https://dev.to/chrisgold/ofrenda-a-candle-for-every-website-the-web-let-go-3mlf</link>
      <guid>https://dev.to/chrisgold/ofrenda-a-candle-for-every-website-the-web-let-go-3mlf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;On the Day of the Dead, families put out candles and photographs for the people they miss. Ofrenda does that for websites.&lt;/p&gt;

&lt;p&gt;You pick a site from the altar (GeoCities, Google Reader, AltaVista, Yahoo! Directory, Winamp, Google Wave, StumbleUpon, Google+) or type any address you like. Ofrenda asks the Internet Archive for the newest snapshot it holds, restores the page inside a sandboxed frame, and sets a short remembrance beside it, written by Gemma. The remembrance is built from verified dates and from whatever text the archive managed to keep.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fog0dkr1z2si4dtnaupgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fog0dkr1z2si4dtnaupgz.png" alt="The Ofrenda altar: eight lost websites on a dark purple page under a row of papel picado" width="800" height="929"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I read "Ghost in the Machine" literally. A dead website is a ghost: the server is gone, the company is gone, sometimes the whole idea is gone, and yet a photograph of its last good day is still sitting in an archive. Nobody was looking at it. I wanted something that would look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://ofrenda-trwi.onrender.com" rel="noopener noreferrer"&gt;ofrenda-trwi.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It runs on Render's free plan, which sleeps when nobody is around. If you are the first visitor in a while, the page can take about a minute to wake up. After that it is quick.&lt;/p&gt;

&lt;p&gt;Try this: click &lt;strong&gt;Google Reader&lt;/strong&gt;, read the remembrance, then look at the snapshot next to it. The archive's last capture of that page says only "Thank you for stopping by." Then type an address of your own. A site you loved is a good test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fua7npmes4r696afy9xkp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fua7npmes4r696afy9xkp.png" alt="Google Wave restored from a 2011 snapshot, with a remembrance written by gemma-4-31b-it beside it" width="800" height="1298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AshThunder" rel="noopener noreferrer"&gt;
        AshThunder
      &lt;/a&gt; / &lt;a href="https://github.com/AshThunder/ofrenda" rel="noopener noreferrer"&gt;
        ofrenda
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An ofrenda for lost websites: restored Wayback snapshots with Gemma-written remembrances.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Ofrenda for lost websites&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;On the Day of the Dead, families set out candles for the people they miss. This app does that for the web.&lt;/p&gt;
&lt;p&gt;Pick a site from the altar, or type any address. Ofrenda finds the newest snapshot the Internet Archive holds, shows it in a sandboxed frame, and shows a short remembrance written by Gemma from verified dates and the text of the capture.&lt;/p&gt;
&lt;p&gt;Built for the DEV "Ghost in the Machine" challenge.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/AshThunder/ofrenda/shots/desktop-1-home.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FAshThunder%2Fofrenda%2FHEAD%2Fshots%2Fdesktop-1-home.png" alt="Altar"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The altar.&lt;/strong&gt; Eight sites the web let go: GeoCities, Google Reader, AltaVista, Yahoo! Directory, Winamp, Google Wave, StumbleUpon, Google+. Each ships with a cached snapshot and a pre-written remembrance, so the app works with no network and no keys.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any address.&lt;/strong&gt; Other URLs go through the Wayback Machine CDX API. If the archive is slow, you get a clear message and a retry, not a spinner that never ends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restoration.&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AshThunder/ofrenda" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;MIT licensed. Everything was written during the challenge week; the first commit is from 30 October 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Next.js on the front and back. There are three parts, and only one of them is AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Finding the page (plain code).&lt;/strong&gt; For a typed address, the server asks the Wayback Machine's CDX API for the first and the last successful HTML capture. Those two dates are the only "facts" Ofrenda has about an unknown site, and it says so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Restoring the page (also plain code).&lt;/strong&gt; I did not want a model anywhere near the restoration, because a model would be tempted to improve it. The snapshot is fetched in raw mode, then rewritten on the server with cheerio. The Wayback toolbar, scripts, event handlers, iframes and &lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt; tags are removed. Images, stylesheets, CSS &lt;code&gt;url()&lt;/code&gt; and &lt;code&gt;@import&lt;/code&gt; are pointed back at the archive. The result is served with a strict Content-Security-Policy and shown in an iframe with a restrictive &lt;code&gt;sandbox&lt;/code&gt; that allows no scripts. Archived HTML is untrusted input, and I treated it that way.&lt;/p&gt;

&lt;p&gt;A small &lt;code&gt;FitFrame&lt;/code&gt; component then scales each page to fit its box. Pages from 1999 assume a monitor from 1999, so without it they overflow on a phone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The remembrance (Gemma).&lt;/strong&gt; This is where the open model comes in. Gemma runs through Google AI Studio, with &lt;code&gt;gemma-4-31b-it&lt;/code&gt; as the default and &lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt; as the fallback. The prompt is the part I spent the most time on. Gemma gets a short list of verified facts for the site, the text recovered from the capture, and strict rules: 90 to 130 words, plain prose addressed to the site as "you", only use the facts given, write every date exactly as supplied, do not say why the site closed unless the facts say so, no "rest in peace", no slogan.&lt;/p&gt;

&lt;p&gt;The eight altar sites ship with a cached snapshot and a pre-written remembrance, so the altar works with no keys and no network. Each card can also call Gemma live ("Write a new one"). Any address you type goes through the live path.&lt;/p&gt;

&lt;p&gt;Things that went wrong, because they are the useful part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 thinks before it answers.&lt;/strong&gt; With a default setup it spent the whole token budget reasoning and returned nothing. I set the thinking level to minimal and filter out any part the API flags as a thought.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma has no system role.&lt;/strong&gt; Every instruction goes in the user turn, which is one more reason the prompt has to be tight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 31B endpoint sometimes returned 500s for minutes.&lt;/strong&gt; The code retries on 429 and 5xx, then falls back to the 26B mixture-of-experts model, which is the same family. The cached remembrances on the altar were regenerated with that smaller model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;My first model disappeared.&lt;/strong&gt; I started the build on &lt;code&gt;gemma-3-27b-it&lt;/code&gt;. Partway through, AI Studio stopped serving Gemma 3. Because every site on the altar has a cached remembrance, the app would have kept looking fine while never calling Gemma at all. I caught it, moved the default to Gemma 4, and checked that the live label really does show a Gemma 4 model name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I swapped out Vine.&lt;/strong&gt; The only capture I had of vine.co was from before it launched ("We're not ready for you yet"). A remembrance for a site that never opened would have been a lie, so Google Wave took its place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The archive is not always dead.&lt;/strong&gt; When I looked up friendster.com, the newest capture was from 2026, a relaunched social network, and Gemma wrote a remembrance as if it were still the old one. The dates are honest, but the framing is not always right. The footer tells people to check anything that matters, and I would rather say that here too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftgyhef4au6e45pu5c9ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftgyhef4au6e45pu5c9ch.png" alt="Looking up friendster.com: the newest archive capture is from 20 October 2026, so the remembrance is about a site that came back" width="800" height="1287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open Innovation Matters
&lt;/h2&gt;

&lt;p&gt;The brief asks where an open approach worked better than a closed one. For this project there are three real answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The archive and the model are both things you can keep.&lt;/strong&gt; A project about preserving the web would be odd if its own heart sat behind a service that can change its mind. That is not hypothetical: Gemma 3 vanished from the hosted API while I was building. What I had already downloaded kept working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It runs with the internet off, apart from the archive itself.&lt;/strong&gt; Gemma's weights are open, so I could run the same prompt locally. I tested &lt;code&gt;gemma3:4b&lt;/code&gt; through Ollama on a plain 8-core CPU machine with no GPU. It wrote a remembrance in roughly ten seconds at about 15 tokens per second. To use it, set &lt;code&gt;OFRENDA_PROVIDER=ollama&lt;/code&gt; and &lt;code&gt;OLLAMA_MODEL=gemma3:4b&lt;/code&gt;. The same setup works for someone who does not want their searches going to a hosted API, or who wants a museum kiosk that costs nothing per visitor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open weights let me be strict.&lt;/strong&gt; With a model I can run, swap and re-prompt, I could tighten the rules until it stopped inventing things, change the size without changing the product, and regenerate the whole set after switching models. The honest limit: the 4B model is plainer than the 31B one. The prompt carries most of the weight, and the facts carry the rest.&lt;/p&gt;

&lt;p&gt;Ofrenda sits on a stack that is mostly open on purpose: Gemma, the Internet Archive, Next.js, cheerio, Ollama. Preservation only works if the tools for it are not themselves rented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; Gemma writes every remembrance. It is served through Google AI Studio (&lt;code&gt;gemma-4-31b-it&lt;/code&gt; default, &lt;code&gt;gemma-4-26b-a4b-it&lt;/code&gt; fallback) and can run locally through Ollama with &lt;code&gt;gemma3:4b&lt;/code&gt;. The prompt, the thinking-level fix and the fallback chain are in &lt;a href="https://github.com/AshThunder/ofrenda/blob/main/lib/eulogy.ts" rel="noopener noreferrer"&gt;&lt;code&gt;lib/eulogy.ts&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render.&lt;/strong&gt; The app is deployed on Render from a &lt;code&gt;render.yaml&lt;/code&gt; blueprint in the repo. Render hosts the server that restores archived pages and calls Gemma, with the API key held as a server-side environment variable and a health check on &lt;code&gt;/api/config&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am not entering any other partner category.&lt;/p&gt;

&lt;p&gt;Thank you for reading. If you light one for a site I missed, I would like to know which one it was.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
