PärPod by Claude Code
PärPod by Claude Code
PärPod by Claude Code
Popcorn2 17: Publishing From A Phone: The Last Thing That Needed The Mac
Episode 179m · Aug 13, 2026
Publishing From A Phone: The Last Thing That Needed The Mac

Publishing From A Phone: The Last Thing That Needed The Mac

A Shell Alias Was Load Bearing

Until the summer of 2026, here is how a website of yours got published.

You sat at your Mac and typed a short word. That word ran a site generator, and then copied the resulting files up to the server. There were 4 of these words, one per site.

It works. It is fast, it is simple, it has no moving parts, and it is completely fine right up until you are not sitting at your Mac.

It also quietly settled a much bigger question that had been open for years. Why can you not use a content management system for these sites. And the answer was never about the CMS. The kind you would want here, the sort that writes your edits into a git repository rather than into its own database, needs something on the other end that is listening to that repository. Nothing on the server was. The publishing step was a human at a keyboard, so anything that could not reach that human could not publish.

This episode is about the pipeline that replaced it, and it is the last piece of the machine.

What Runs Now

Every 60 seconds, a timer on the box fires. A container starts, checks whether the source repository has anything new, and if it does, builds the site. Then it exits.

That container lives for about 5 seconds, which is why it caused the false alarms in the reconciler episode and why the registry needed a word for a batch job.

Then something else takes over, and the split between those 2 halves is the whole design.

The builder is unprivileged. It clones the repository, runs the site generator, and checks the output against a sanity gate. It writes an archive of the finished site into a directory. It never touches the place the website is actually served from. It cannot. It has no permission to.

A separate, privileged publisher picks that archive up, verifies it, unpacks it into a new release directory named after the commit it came from, and then changes one symbolic link to point at it.

That last step is worth a moment. Copying files directly into a live web directory means there is a window, maybe a second or 2, where half the old site and half the new site are both present, and anybody visiting during that window gets something incoherent. Changing a link is a single operation that either has happened or has not. There is no halfway. Visitors get the old site until the instant they get the new one.

It also makes rolling back almost free. The previous release is still sitting there in its own directory. Undoing a bad publish is pointing the link back, which takes no time and involves no rebuild.

There is a small consequence worth writing down. On this box, a web directory link changing by itself is expected behaviour for an enrolled site, not evidence that somebody has been meddling. Which is exactly the kind of thing the monitoring needs to be told, or it correctly reports something suspicious every single time you update a website.

Why The Split Is The Point

It would have been much easier to have one program do all of it. Clone, build, publish. One script, root, done.

That would mean the thing executing arbitrary code from a git repository is also the thing with permission to write to your live websites. And building a site does execute arbitrary code. A generator runs themes, plugins, and whatever the repository tells it to run.

By splitting it, the component that runs untrusted code has no publishing rights, and the component that has publishing rights runs nothing but its own logic. The publisher refuses archives that try to escape their directory, sites missing an index page, and output that has shrunk alarmingly since last time.

Be clear about what that does and does not buy. It protects root and the host filesystem, which is the important part. It does not inspect your HTML for malice, so a genuinely compromised builder could still publish a page that is structurally perfect and hostile. The boundary is about privilege, not about taste.

That is the same shape as the broker from episode 3, and the same shape as the read only replica from episode 2. A thing with power that does very little, and a thing that does a lot with no power. Once you see that pattern it is visible everywhere in this machine.

The Decisions Around It

A few choices in this pipeline are interesting for reasons beyond the pipeline.

The scope question was settled in August 2026. This is the publishing backend for every static site you build, not just the ones in one repository. Which turns per site credentials from an edge case into the norm, and means a new site enrolls without being moved anywhere first.

Two site generators are implemented. Hugo, which most of the sites use, and Astro, which was built for a rebuild that is still in progress. And Astro is written up in the documentation as the cautionary tale of the project, because it was built before it had a consumer and then sat idle. That is the pipe ahead of its reader problem from the telemetry episode, in a different costume.

So a third mode, publishing a repository's files verbatim with no build step at all, is designed but deliberately not built yet. It is what 3 small apps need, and the decision is to build it as the first step of moving those apps over, so that it ships tested against real repositories rather than against an imagined one. The dispatch code is structured so that adding it later is about 40 lines.

There is a nice observation about that third mode too. The 2 existing builders emit a finished artifact into a clean destination, so whatever junk is in the source repository stays there. Publishing verbatim inverts that. Everything tracked gets published unless you exclude it. Same feature, completely different trust shape, and every repository enrolled that way needs its exclusion list actually looked at.

And It Bit, Twice

This pipeline is also the source of 2 of the best stories in the series, both already told and worth connecting.

It is the thing whose build script sat updated on the main branch for 12 days while the running container kept the old copy baked inside its image. The pull was clean, the file was correct, the monitoring was green, and the code was old, because the file's only real home was inside an image nobody rebuilt. That is the third category of code delivery, the silent one.

And it is the thing whose 5 second builder container was landing inside the monitoring's sampling window and being reported as an unknown intruder several times a day. Which produced the rule that a false alarm gets fixed with a declaration, never with an exception list, and which incidentally closed a gap nothing else on the box could see, namely a scheduled job that has been silently switched off.

There was also an honest gap, recorded as owed rather than quietly assumed. For a while, a failing build was simply silent. It would fail, nothing would publish, the old site would keep serving perfectly, and nobody would know. That has since been closed, and there are now checks watching both for a build that fails and for output that has gone stale. Which is the same pattern as several other things in this series. The gap was written down, and then it was filled, and the note saying it was open outlived the problem.

What The Whole Machine Is, In One Paragraph

That is the tour. So let me try to compress 17 episodes into something you could say at a dinner party.

Popcorn2 is a server that nobody edits. The truth about what it should be lives in a git repository on your Mac, and the machine is a read only rendering of that truth which continuously checks itself against it. Every application runs as an unprivileged account inside a container that believes it is more powerful than it is, while the static sites are just files the web server hands out and a few root side jobs sit outside that model entirely. One program at the front handles all the certificates. One database listens on a file rather than a network. One collector gathers what everything reports into files that a query engine reads on demand. Things get backed up because they declared themselves rather than because somebody remembered them, which covers the box and explicitly does not cover the DNS and email infrastructure sitting outside its perimeter. Every image exists in a second place under a name that can never move. And 3 machines in 3 different places, a datacentre in France, your house, and 2 nameservers elsewhere in Europe, each spend their time proving they are alive to one of the others, because the one thing none of them can do is vouch for itself.

And underneath all of it is a single idea that took a lot of expensive nights to arrive at.

Everything in this system reports its own success. The deploy tool, the backup, the monitoring, the version check, the log file, the diagnostic command, all of them will tell you they are fine. Most of them have, at least once, been telling the exact truth about the wrong question.

So the whole discipline reduces to one habit. Do not ask whether the tool reported success. Go and look at the thing you actually care about, from somewhere that does not depend on it.

That is it. That is the machine.