Self-built · A toy that got out of hand
A link shortener I built out of boredom — until it started keeping the pages its links pointed to.
Link began with no business case at all: I wanted to build an ultra-scalable link shortener, throw it at Kubernetes, and hammer it with about a million requests — engineering as recreation. Somewhere along the way it started capturing the pages behind the links, and the toy stopped being a toy. Now every link I save carries a frozen, replayable copy of its destination, and the whole collection is searchable like a private slice of the internet.
The toy
There was no problem to solve. I was bored, and the itch of the moment was scale: the link shortener is the classic system-design interview toy, and I wanted to actually build it — properly. A short code, a redirect, a read path cheap enough to embarrass the whiteboard answer, and a load generator pointed at it until about a million requests had bounced off.
That was the entire plan. If it had stayed that way, this page wouldn't exist.
The accident
As always happens, I went and overengineered it — unintentionally, which is the only way I ever do it. A shortener resolves a code and redirects; that's the whole job. But a redirect felt hollow. The page behind the link can change. It can die. So, purely because it was more interesting than stopping, I made Link visit every destination and record it.
That one decision quietly changed what the thing was. The moment a shortener keeps a copy of the page, it isn't a shortener anymore — it's an archive with short names. Every saved link became a frozen page that would outlive its original. I set out to build the most over-built redirect on earth and accidentally built the snapshottable internet instead.
Overengineering a toy is how some of the best products get born. Nobody writes that into a roadmap, but it keeps being true.
Features
Paste a URL, get a short link. In pointer mode it simply sends visitors to the live destination — the boring, correct baseline this whole thing was born as.
By default, saving a link does more than shorten it: Link visits the page and records every HTTP exchange it makes — HTML, assets, even the XHR and API calls — into a WARC. The link stops pointing at the internet and starts carrying its own copy of it.
Because the capture records the page's network traffic rather than just its HTML, heavily interactive pages — SPAs, animated landings, an Excalidraw board — replay as working frozen snapshots. The page's own JavaScript runs at view time, sealed inside the archive, with zero on-demand work on the serving side.
WARC plus wabac.js — the same underlying approach the Internet Archive uses — but personal: the pieces of the internet you cared about, saved for your own use.
The destination can go down, get rewritten, or vanish behind an acquisition notice — your link keeps serving the frozen snapshot, with an honest banner saying when it was taken and where the original lived.
The index
The accident had a second act. Once every saved page had a body, the bodies were too good not to index — so every capture flows through an extraction pipeline into a search engine that answers only to me.
Every capture gets the reading a search engine would give it: title, description, readable text, structured data — the same fields Google's own documentation says it looks at. I followed that playbook deliberately, without pretending to build an actual search engine.
Weighted full-text search: a match in the title outranks one in the description, which outranks one buried in the body. Results come back ranked, with query-highlighted snippets.
The index knows only the pages I saved, and answers only to me. You don't just own pieces of the internet forever — you can google them yourself, privately.
Trajectory
Link has settled into the wyolet stack as its link boundary, the way Relay is its model boundary — today it holds my bookmarks, my archive, my private search. Whether it grows into a product is a question I'm deliberately not rushing: the last time I left this thing unsupervised, it turned a shortener into an archive. I want to see what it does next.