Tagarchive13

The archive talks back

A letter arrived at the end of September. Pavel had used this engine to back up his old blog, and wrote down what he learned doing it: over a thousand posts in nine years, and stretches when he wrote every day. He had not known. The blog had been his the whole time and the posts had been in it the whole time, and still the number came from the archive, the first time anything read it end to end.

This release is about what an archive says when it is asked. How things get into one, how they leave it and how it is checked, this site has written about enough. This is about what the archive knows about you.

What the archive counts

./blog.sh stats reads every post and answers with one screen. On the archive this engine was built around, today it reads:

Read more

Building an Archive

Steven D. Brewer has been blogging since 2002: PHPWiki, then Drupal, then WordPress. With Drupal 7 at the end of its life he wanted a home for twenty-four years of posts that would need no maintenance, and put them on blog.sh: archive.bierfaristo.com, with the Drupal posts and the Twitter feed already in, and pieces of an old Esperanto-USA blog recovered from the Wayback Machine.

Worth a read if what you're after is an archive that has to outlive the software it was written in.

#blogsh #archive #drupal

How big it can get

It is a fair thing to ask before you hand twenty years of writing to a tool: at what point does it stop being pleasant to use? Measured here in August, on an archive of 6,639 posts, the honest answer was a shrug: adding one post cost the whole archive — forty-eight seconds from pressing enter to the site being live — because every page that so much as mentioned a post had to be remade, whether it had changed or not.

That's no longer the honest answer. Since 1.6, the build keeps a record of what went into each page, and remakes only the ones whose record no longer matches. The archive underneath is the same one this was measured on in August, seven posts heavier: 6,646 now, and the engine three releases on, at 1.9.

What you wait for today

Publishing a post dated today, on that same archive: sixteen seconds of build, two of upload — where it was forty-eight. The build isn't touching pages that didn't need it any more, so the size of the archive stopped being what the wait was made of.

Where the size still shows up

Read more

The parts that don't rebuild themselves

A laptop dies, or a server gets rebuilt from nothing, and the question that actually matters is narrower than it sounds. Not "how do I back this up" — "which of these files does the engine actually need me to protect, because nothing else here can make them again on its own."

Most of an installation doesn't qualify. The engine itself is a git clone; pull it again and it's the same engine. Everything the build writes comes back with one rebuild, given what's left.

The instinct is to back up the site itself — the folder a browser actually sees. That's precisely the one thing this list leaves out. public.nosync, the build's own output, is exactly as disposable as the engine that made it: one rebuild reproduces it byte for byte, so keeping a copy of it would just be storing the same information twice, under two different names, and trusting the older copy by accident.

What's left, once the engine and the site it builds are both set aside, is a short list.

What's actually irreplaceable

Read more

Everything else in 1.6

Five posts have covered the things 1.6 is about. This one is the rest, in the order an operator meets them.

A way out of the trash and the versions

Both stores had a way back — restore, and the version picker in the post's properties — and no way out, so both grew for years and nothing said by how much. empty trash and empty versions each print what they are about to remove, in items and in megabytes, and require that count typed back before anything goes. empty versions keeps each post's newest version, because versions exist to answer "give me back what I just overwrote". doctor now notes a trash with posts in it, as a note rather than a fault: on the installation this engine was built around, the only way to see it was du on the server.

A photograph stored once

public.nosync/ used to hold a second copy of every picture. It holds a hardlink now, so the pictures take half of what they did — 1.8 GB on this project's installation. It saves nothing in a backup, and that was measured rather than assumed: 200 files under two names, 101 MB on disk, 201 MB in the backup. Nothing to do; the first build after the upgrade makes the links.

Read more

An archive you cannot see is not empty

This site's approved comments and its post counters are two small JSON files, refreshed by a cron job every half hour and uploaded with the rest of the site.

Through 1.4, a tick that ran while the archive was not there would narrow both files to nothing and upload them. An absent or empty content directory answers with an empty list and no error, and an empty list reads exactly like "every announced post was deleted". The live site lost every approved comment and every counter at once. Exit 0, nothing said, nothing mailed.

The archive lives on a synced volume. A volume that has not mounted yet, or a working copy whose files are still arriving, is not an unusual Tuesday.

Not being able to tell is a reason to keep everything

That sentence is the fix, and it now runs in two tiers.

If the content directory is not there — or if the archive reads as empty while the previous files held anything at all — nothing is narrowed, and the run says why.

Read more

Two pages the site built out of itself

Until now this engine could show you ten posts at a time and nothing else. /page/128/ is a real address on a large archive, and it tells you nothing about whether you have landed in 2009 or in 2014 — pagination is anchored from the oldest post, so the number moves under you every time something is published. The only complete list of anything lived in the terminal.

1.5 adds two pages that read the archive instead of walking it.

/archive/ — the shape of the whole thing

A row per year, with a strip of twelve months beside it. A month is shaded in one of four steps rather than simply lit or dark, because "has posts / has none" is not the question worth answering: on the reference archive a month holds anywhere from one post to eighty-seven, and drawing those the same would hide the one thing a map is for. /archive/<year>/ is one line per post, by month.

Two levels and no more. A third would be some 280 pages of individual months — a great deal of building for a question nobody asked.

Read more

A slug is an address, not a name

For most of this project's life, a post without a title had two names. Its own page called it by its date. Everything else — the browser tab, the link card somebody shares, every item in the feed — called it burtiky-opekame-hipstamatic-oggl-jane.

That is not a corner case. On the archive this engine is measured against, 2,754 posts of 4,418 carry no title. Sixty-two per cent of a site, going out into the world under an address.

A slug is made out of the words. It is not the words.

A slug is a machine's answer to "where does this live": lowercased, stripped of diacritics, joined with hyphens, cut to the first eight words. Every property that makes it a good address makes it a bad name. It has no capitals, no punctuation, and no idea where the sentence it was cut from was going.

1.5 takes the name from the same place the slug came from — the post's own opening — and stops in a better place.

Where it stops

Read more

What a crash may cost

The queue can now swap two scheduled posts that share a slug across two years — with their media and their edit history riding along, because those are keyed by year and slug exactly like the post file. That sentence is short; making it true was most of a release cycle. The interesting part is not the swap. It is what happens when the power goes out in the middle of one.

The budget is zero

A move is several renames, and a crash can land between any two of them. The rule 1.4 holds to: whatever the moment, a crash may cost you the move — never a post. Files step aside under parking names before anything writes; a failed write puts everything back, byte for byte; a hard kill that strands a parked file leaves it where check finds it and says truthfully what it is.

The advice is part of the system

Read more

The other half of check

./blog.sh check reads the whole archive and says what is wrong: media a post asks for and hasn't got, links to addresses nothing answers at, two posts that would be served at one address. Through 1.3 that was where it ended — a list of findings and an exit code, with the fixing left to you and a text editor.

--repair walks it with you

check --repair is the checker's other half: for each finding it offers the one repair that finding allows — an old address written into the target post's redirect_from, a relative link rewritten to the address it means, an orphaned file moved to the trash. Nothing is applied without a keypress, nothing is deleted outright, and a finding with no obvious answer — two posts colliding, an image only a human can judge — is shown and passed over rather than guessed at.

It proved itself before it shipped: on a real archive it took 238 findings down to 42, and the 21 repairs it proposed matched what a person had already decided by hand, one for one.

--json says everything

Read more

What rots quietly

./blog.sh doctor asks whether the installation is sound and takes a second. It has nothing to say about an archive of four thousand posts in which one picture never downloaded, three permalinks lead nowhere, and two posts claim the same old address.

./blog.sh check walks all of it and says.

What it looks for

Each finding names a post and a slug — something to go and fix — and carries a line saying what to do about it.

Read more

The way out ships with it

An engine that cannot hand your writing back is a hosting service with extra steps. The importers have always pointed one way — twenty-two sources, everything coming home. ./blog.sh export is the other direction, and it should have been there first.

What comes out

The whole archive as a tree of markdown files with YAML front matter, in the layout Jekyll reads: _posts/2026-05-01-slug.md, _drafts/, pages at the root, media under assets/<year>/<slug>/. Old addresses travel as redirect_from, in the shape the jekyll-redirect-from plugin already understands, merging both kinds — where the post lived on the platform it came from, and where it lived here before a rename. Point a static generator at the folder and it builds.

./blog.sh export ~/my-blog-export        # everything, drafts included
./blog.sh export ~/public-copy --no-drafts
./blog.sh export ~/somewhere --dry-run   # counts, writes nothing

This site, counted rather than written:

Read more

A blog engine that became an archive

The first post on this site is about a photo I put on Instagram in 2012. The link still works. The photo doesn't.

That post was about one archive — mine — and one rule: media comes home. This one is about what happened when that rule was applied to every platform I could find an export for.

The roster is closed

Twenty-two sources import now, in three groups.

Social networks: Bluesky, Facebook, Instagram, Mastodon, Pixelfed, Threads, Twitter/X.

Blogs and publishing platforms: beehiiv, Blogger, Ghost, Jekyll/Hugo and any markdown folder, LiveJournal, Medium, Movable Type and TypePad, podcasts (any feed with audio), Squarespace, Substack, Tumblr, Wix, WordPress and any RSS or Atom feed.

And dead sites: the Wayback Machine, for blogs whose platform no longer exists at all.

Read more