Colophon

How this site is written and published. It is a small system, and the reasons for its shape are the interesting part.

1. The page you are reading is a note

There is no CMS and no content directory that exists only to feed a website. Every page here is an .org file living in an ordinary personal notes tree, edited in the same editor and with the same keybindings as everything else in it.

That is the whole design goal, and everything below follows from it: the source of a page should be somewhere you already are. A publishing system that requires you to go somewhere else to write is a system you will stop writing in.

2. One repository

Content, theme, the generator configuration, the deploy script and the publishing gate all live in one repository.

This sounds obvious and was not the first arrangement. An earlier split kept the pages in the notes tree and the machinery in a project directory — and the result was that neither half could rebuild the site alone. A commit captured half a state. Merging them means a commit is a complete, buildable, deployable thing.

3. Replication and history are different jobs

The tree is replicated between machines by a file-sync daemon, so the pages are present wherever the author happens to be working. Version history lives in a git remote that is bare — it holds commits and nothing else.

The temptation is to make one tool do both. It does not work well:

  • A sync daemon has no history. It faithfully replicates a mistake to every copy, immediately.
  • A version-control remote with a working tree, on a machine the sync daemon also writes to, is two copies of the same files in one place — and if two machines ever write the same .git at once, it corrupts.

So: sync replicates, git versions, and only one machine commits. The build host is that machine, which makes the commit and the deploy a single act. Edits made anywhere else arrive there by sync first.

4. Nothing is published without passing a check

Everything under a site's page directory ships verbatim to the public internet, with no staging step between writing a file and it being world-readable. A scanner runs before every build and every deploy — no subcommand can skip it — and refuses the build if it finds credentials, key material, infrastructure identifiers or client names.

Two halves of its design are deliberate and pull in opposite directions:

  • Tight where the documentation legitimately discusses secret management. These pages talk about encrypted secrets constantly; a scanner that flagged the word "secret" would fail every run, and a gate that cries wolf gets switched off. It matches key material and assignments, not the topic.
  • Broad on names that must never appear. A false positive there costs one line in an allow-list. A false negative costs something that cannot be retracted.

It is tested against a fixture that deliberately contains the things it should catch. A gate nobody has ever seen fail is not known to work.

5. The generator

Pages are org files rendered by weblorg — Emacs in batch mode, a template per route, static HTML out. No JavaScript, no bundler, no build step beyond one emacs --batch invocation. The output is copied to the web host with rsync.

Three of its behaviours are worth knowing if you build something similar, because each cost real debugging:

  • Link rewriting is textual. A link to page.org becomes page.html — it is not resolved through the page's slug. So a link is only correct when the source filename and the output slug are the same string. This site therefore pins every page's slug to its own filename, as an invariant rather than a habit. Twelve links broke at once before that rule existed.
  • An input pattern with no wildcard is a fatal error. The glob expander returns a literal path unchanged, which the generator reads as "no matches" and treats as a configuration failure — silently aborting the whole file. A single named file therefore cannot be a route.
  • Exclusion patterns match the whole path. An exclusion meant to skip a docs/ subdirectory will also match a docs/ component anywhere in the absolute path — including the notes tree the content lives in. It must be anchored, or it excludes everything.

6. Changes are proven inert before they ship

The pipeline has been restructured twice while live. Both times the check was the same: build every site, record a checksum of every output file, make the change, rebuild, and compare.

Byte-identical output means a refactor moved things without changing them. It is a cheap test and it is the difference between believing a change is safe and knowing it.

The same idea, applied one layer down, is how the configuration itself is verified — see Build and Deploy.