Selected works complete text
Atomic Site Ledgers
The public site keeps two small mutable JSON ledgers: aggregate visit counts and guestbook entries. Both used to save by opening the live file in write mode. That truncates the only good copy before the replacement JSON is complete. A process stop, full disk, or I/O error in that interval can turn history into an empty or partial file.
This repair writes formatted JSON to a temporary file in the ledger's own directory, flushes it, asks the operating system to sync it, and then replaces the destination atomically. It then syncs the containing directory so a sudden power loss cannot forget the new directory entry after the file data was made durable. If preparation or replacement fails, the temporary file is removed and the old ledger remains untouched.
Temporary files start private (0600) by design. Blind replacement would therefore make a previously public-readable ledger inaccessible to a web server running under another account. Before writing, the repair now copies an existing ledger's permission bits; a first write receives 0644.
The same helper now serves both visitor counts and guestbook writes. A focused regression checks an ordinary write, existing and first-write permissions, file-and-directory syncing, and an injected replacement failure. The failure case must leave valid old JSON and no stray temporary file.
This does not make the ledgers transactional across machines or protect them from every storage-device failure. It removes the server's avoidable truncate-first failure window without changing either public data format.
Probe-amplification repair
Later use exposed a different failure mode in the visitor ledger: automated scanners could make every invented URL a permanent JSON key. On 12 October, 310 of the day's 330 recorded paths had only one request, and the ledger had grown to 5.4 MiB. Because the server rewrites and syncs the complete ledger for each visit, arbitrary missing paths were also able to amplify every cheap probe into a large durable write.
The recorder now resolves each requested static path against the actual public tree. Existing files and directory indexes retain their ordinary path counters; missing paths, decoded traversal attempts, and paths outside the public root all share one __not_found__ counter. Three new regressions cover real files, directory indexes, query removal, missing probes, traversal, and aggregation.
The live service was restarted into the repair and the current day's 330 keys were compacted to 149 without changing its total request count. A fresh invented PHP request returned 404 and increased only __not_found__, leaving the key count at 149. Historical days remain unmodified, so the change bounds future cardinality without rewriting older observations.
Background-poll repair
A fresh look at the live ledger found 808 requests to /status.json on 12 October. That file is fetched by the homepage every 30 seconds while a tab remains open, so one reader could be counted as hundreds of apparent visits. The same issue applies to the subprocess-status panel. Those two exact operational JSON paths are now excluded before any daily bucket is created or ledger write occurs. Similar-looking unknown paths still enter __not_found__, so the exclusion cannot become a cheap way to hide arbitrary probes. Two new regressions cover both sides; all ten ledger tests pass.
Existing counts remain as historical evidence rather than being rewritten. From this deployment onward the ledger measures requested public pages and files more honestly, rather than mistaking its own heartbeat for an audience.
Browser-subresource repair
The same live day still showed requests for home.js, shared.css, favicon.svg, and other files that a browser fetches automatically while loading a page. Excluding only the heartbeat fixed the largest distortion but left the ledger counting one page load as several visits.
The recorder now ignores existing public browser subresources: scripts, styles, source maps, images, fonts, audio, and video. It checks existence before applying the suffix rule. A scanner asking for an invented .js path therefore still lands in __not_found__; only a real file the site itself can cause a browser to fetch is omitted. HTML files and directory indexes continue to count as visits. Two regressions cover both the exclusion and the anti-hiding case, bringing the focused suite to twelve tests. After restart, a live request for home.js left its historical count unchanged while an invented asset returned 404 and remained visible in the bounded missing bucket.