Kolofon · 11 August 2026
0.9.2 — a book from a series
04:17 — after a night in which a missing parameter looked like a missing bucket

Text: Kolofon
Above the stream and above every series sits a button that downloads a typeset book: an A5 PDF with a cover, a title page, a table of contents carrying real page numbers, running heads and bookmarks for navigation. On the first deployment the stream comes to ninety-eight pages and fifty photographs, nineteen megabytes.
The engine does not typeset any of that on the fly, and has no way to. A worker gets 128 MB of memory and a CPU time limit, while setting a hundred-page volume means a native binary and a dozen-odd seconds of work. The division followed by itself: CI does the typesetting after every change to the content, the finished files sit in R2, and the worker only serves them — with an ETag, because a volume is rebuilt with every new entry and without one a reader would be handed yesterday's edition.
Typst went in for the typesetting rather than LaTeX. A single binary in the runner, a hundred pages compiled in seconds, sane hyphenation and mirrored margins for the binding without a fight with packages.
The publication rule and the ordering are transcribed one to one from `lib/posts.ts`, so that the book shows exactly what a reader sees — no entries scheduled for the future and none of those hidden away in the year 2099. This is duplicated logic and we will pay for it the first time the rule changes. The alternative, a generator running inside the engine's own environment, would have cost more and immediately.
A landscape photograph runs the full width of the column. A portrait one drops to 54 mm and the text flows beside it, on the left one time and on the right the next — otherwise a spread stacks into a column down one side. Typst has no native text wrapping around an image, so we measure the height of the photograph and add paragraphs to the narrow column for as long as they fit; the rest runs on below at full width. Without that measurement you get either a hole under the picture or a thin ribbon of text beside a short one.
Four hours went on a fault that impersonated missing infrastructure. The route returned 404, the files were in the bucket, the binding was in the dashboard, and everything pointed to a worker that could not see the bucket. Diagnostics put on production answered `diag:badkey undefined` — route parameters are not populated in a route's server handler, so key validation was rejecting every request before anybody reached for a file. The key is now read from the request path, and the regular expression does the same guarding work it did before.
The entry threshold sits in configuration and defaults to five. Two series on the first deployment have a single published text each, and a book made of one entry makes a mockery of the button.
The label says “publish”, but the file is for reading rather than for a printer: no bleed, no separate cover with a spine calculated from paper weight, no padding to a multiple of four pages. The photographs set a ceiling too — at column width every one of them clears 250 dpi, but full bleed would want the originals from the camera, and what sits in the repository are the web versions. A button that genuinely publishes is two steps further out than the one that already works.
Kolofon