Everything else in 1.6

Part 7 of 7 of ./blog.sh v1.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.

check knows a type it does not know

type: story was stored on the post and read by nobody — no listing, no menu entry, no icon, and not a word about why. check names the eight types it knows now, and the route somebody reaching for a ninth usually wants: a tag named in nav:, which gives a listing with its own pagination, a menu entry and an RSS feed. Asked for in issue #42.

What three reviews found

The engine, the new page and the receiver each went through a review before the tag, by someone whose job was to break them, and every finding closed with a test that fails on the old code. Four of them are worth knowing about even if you never meet them.

Two posts written at the same instant left one post. Settling a post's name and writing its file were two steps with a media copy between them, and a copy with photographs takes seconds. Two runs that started together — a delivery from a phone while an import ran, two phones one after the other — were both told the name was free, and the second one's file replaced the first one's. Both callers were told it had gone well. The name is taken by creating the file now, which of two runs asking at the same instant only one can do; the other walks on to the next serial, and both posts survive.

./setup.sh | tee setup.log echoed the access token in clear text. The prompt hid what was typed only when both streams were terminals. Anyone who set a site up that way should assume the token is in that log and rotate it.

Attaching a photo through a symlinked directory in incoming/ deleted the original. ln -s ~/Pictures incoming/photos is the obvious way to stop copying photographs twice, and the tidy-up after a save compared path text, which follows no symlinks — so every original behind that link was inside its reach.

An embed could still smuggle a script past the sanitiser, in three spellings: <svg/onload=…>, javascript&#58; written as an entity, and an <animate> whose values were a script. The pass now walks a tag's attributes and asks each one what it is, rather than matching shapes. It is the same guard that dresses a tag's own icon_svg.

Eleven places where a terminal was watching

The engine behaved differently in eleven places depending on whether a terminal happened to be attached — a question that flushed only on a tty, a key offered on screen and accepted everywhere, a tag with a letter outside ASCII that crashed under docker exec and cron, where LANG is unset. Nine of them are named in the changelog. All of them matter more now that a post can arrive from a cron job or a phone, where no terminal is ever watching.

Full notes, all of it, in CHANGELOG.md under 1.6.

Comments