Devices and storage
USB over xHCI, NVMe underneath a content-addressed namespace, and a write gate that names a range.
Rings, doorbells, and a bug found from above
The USB controller is driven by rings of transfer blocks with a cycle bit marking which entries the controller owns. Devices are enumerated, given a slot, addressed, configured, and then talked to over an interrupt endpoint.
Two things there were expensive to learn.
The cycle bit is written last and on its own. A block belongs to the controller the instant that bit matches what the ring expects, so a single struct-sized store that set the bit alongside the rest of the fields let the controller see a request whose parameter had not been written yet. It is a memory-ordering bug wearing the costume of a driver bug, and it is intermittent in exactly the way that makes it expensive.
Addressing a device was refused while every field this code wrote was correct. Contexts aligned, slot context right, the input control context dropping nothing and adding the first two endpoints. Six rounds of re-reading our own side found nothing, because there was nothing there to find. The emulator's own trace said what re-reading could not: the first address succeeds, a controller will not address a second slot to a port that already has one, and enumeration had no counterpart. The network probe walked every port at boot looking for an adapter, kept none of them, and left every port owned by a dead slot — so USB enumeration had been single-shot per boot since the day that probe was written, and nothing had noticed because nothing had needed a second device.
The lesson is the one this project keeps relearning: printing bytes beats reasoning about them, and the trace from the other side of the interface is worth more than another read of your own.
Keyboards and mice use the boot protocol, where a report is state rather than an event, so events are recovered by diffing each report against the previous one. The usage codes are translated into the older scancodes the existing decoder already speaks, rather than directly into characters — because shift, caps lock, an Alt held against an Alt tapped, and Alt-Tab are all policy, and policy written twice disagrees with itself.
Content addressing, and what the hash must not cover
Objects are named by the hash of their contents and assembled into Merkle trees. A copy is free, and a snapshot is one root hash.
The content hash covers content only and never block locations. That is the decision the whole design rests on: if a location were part of the name, moving a block would rename the object, and every reference to it would break for a reason that has nothing to do with what it contains.
Directory entries are kept sorted, so children come back in lexicographic order. That is why stored blob names are zero-padded to four digits — sorted order becomes insertion order, and every positional split boundary in the training corpus depends on it. Past 9,999 the padding truncates and the property fails silently, which is why the host-side tool refuses to emit a larger bundle rather than letting it happen.
A sandboxed run copies nothing up front. It used to open by deep-copying the entire namespace so it had something to restore from, which meant every run paid for a clone of every object in the tree in order to undo a program that usually touches one file — and held a mutable borrow across a full recursive walk with interrupts disabled, twice. The note taken immediately before each mutation already has the path in hand, so each path's pre-image is saved exactly there and the cost is proportional to the change instead of to the tree. It records the shallowest path that did not exist, because writing a file creates its parent directories, and undoing only the named file would report the run as reverted while leaving directories behind.
The unlock names a range, and the range is enforced
Writes to the disk are locked by default and unlock only after a region has been found that was explicitly set aside. On a laptop whose disk is entirely allocated to another operating system there is no such region, initialisation fails, and that is the intended outcome rather than a problem to work around. Every error path re-locks, because leaving it open on the way out is how a safety mechanism becomes decorative.
For a long time the unlock was one bit. It said writes were allowed and nothing said where — so from the moment storage initialised successfully, every block on the device was writable: the partition table at sector zero, the EFI system partition, and the Windows volume that is still the only other thing on that disk.
The write path now checks each request against the window the unlock actually claimed, and the status output prints that window beside the word UNLOCKED rather than leaving an operator to assume it means the whole disk. The decision is asserted at boot with no device present and nothing written, including the two cases that matter most: a write starting inside the window and overrunning its end is refused, and a length that overflows a 64-bit integer does not wrap around into a pass.
That is a prerequisite for anything that writes the boot partition rather than a separate concern. An updater built on a global unlock is a whole-disk writer that happens to be aiming carefully.
