docs: storage — the mount map is built; the path is the id (S2)

Flip the storage docs to match S2: filesystems.csv (signature -> binary) and
volumes.csv (identity -> optional override) are built; a volume's mount path IS
its content id (/volumes/<id>), never a port name; the label is display metadata
a `volumes` query returns. fat receives its mount path via argv[2] rather than
hardcoding it. Still pending: rung 2 (filesystem UUID, needs a non-FAT engine),
multi-volume (fat's boot rewrites stay unconditional until S3), medium_changed
consumption, remount bench-verification.
This commit is contained in:
Daniel Samson
2026-08-10 00:49:32 +01:00
parent df61693065
commit 6d4992ae02
2 changed files with 36 additions and 28 deletions
@@ -3,18 +3,23 @@
> **Status:** the layered model below is the settled design
> ([storage-design-rationale.md](storage-design-rationale.md) records how it was
> reached, and [volume-manager-plan.md](../volume-manager-plan.md) how it was
> built). **Built** (the volume-manager track, V0–V4): the data path, the driver
> built). **Built** (V0–V4 + the storage-stack S1/S2): the data path, the driver
> range confinement (per-sender clamp + the confinement gate), the `medium_changed`
> presence event, the volume manager itself — it probes the partition table,
> confines each filesystem to its partition, spawns one filesystem per volume, and
> supervises it — and the removal half of the lifecycle (a pulled stick unmounts).
> **Still pending**: the fuller identity ladder and the `volumes.csv` mount map,
> multi-volume (one FAT volume today), the volume manager *consuming*
> `medium_changed` (removal is detected by device-presence polling; the event is
> published but only a card-reader medium change needs the subscription), and the
> remount-on-replug end-to-end (the logic is in place; QEMU can't re-present the
> boot-controller device, so it is bench-verified). A few markers below are left
> where a duty is still pending.
> supervises it — the removal half of the lifecycle (a pulled stick unmounts), the
> identity ladder (GPT GUID + name, FAT serial + label, MBR), and the mount map:
> `filesystems.csv` (signature → binary) + `volumes.csv` (identity → optional
> override), a volume's mount path IS its content id (`/volumes/<id>`), with the
> label as display metadata a `volumes` query returns. **Still pending**: the
> `filesystem UUID` rung (needs a non-FAT engine), multi-volume (one FAT volume
> today; fat's boot rewrites are unconditional until S3 makes them
> content-conditional), the volume manager *consuming* `medium_changed` (removal
> is detected by device-presence polling; the event is published but only a
> card-reader medium change needs the subscription), and the remount-on-replug
> end-to-end (the logic is in place; QEMU can't re-present the boot-controller
> device, so it is bench-verified). A few markers below are left where a duty is
> still pending.
## The model
@@ -85,16 +90,17 @@ manager's tree for a storage provider; when one appears it consumer-hellos for
the block channel, reads the partition table and the first blocks itself
(**it** is the prober), defines the volume's sub-range on the driver, spawns the
matching filesystem service confined to that range, and supervises it (backoff,
crash-loop cap). *(Pending)*: it decides mount placement from `volumes.csv` and
picks the filesystem binary from `filesystems.csv` — today it hands every
FAT-shaped volume to the FAT service and the FAT service carries hardcoded mount
prefixes. Those tables are CSV configuration, read by it (the policy), enforced
by nobody else:
crash-loop cap). *(Built)*: it picks the filesystem binary from
`filesystems.csv` by the volume's content signature, and mounts the volume at its
content id (`/volumes/<id>`) — or a `volumes.csv` override. The label is display
metadata the `volumes` query returns, never the path. Those tables are CSV
configuration, read by it (the policy), enforced by nobody else:
- `filesystems.csv` *(pending)* — content signature → filesystem binary. Adding
- `filesystems.csv` *(built)* — content signature → filesystem binary. Adding
a filesystem adds a row.
- `volumes.csv` *(pending)* — the mount map, danos's fstab: **volume identity → mount
prefix**, keyed on content identity and never on port, path, or arrival
- `volumes.csv` *(built)* — the mount map, danos's fstab: an OPTIONAL **volume
identity → mount prefix** override (a volume with no row mounts at its default
`/volumes/<id>`), keyed on content identity and never on port, path, or arrival
order (the lesson of Linux's `/dev/sda1`-era fstab, which broke on every
port move until `UUID=` replaced it). Identity is read off the medium by
the prober, strongest first: GPT partition GUID → filesystem UUID → FAT
@@ -105,8 +111,8 @@ by nobody else:
identity (cloned sticks, together) is policy: first keeps the name, the
second mounts suffixed and is logged loudly. The boot volume is the
recorded identity of the volume carrying `/system/configuration` and
`/system/logs`, findable on any port. Unknown volumes mount under
`/volumes/<derived name>`.
`/system/logs`, findable on any port. Every volume's default mount is
`/volumes/<id>` — its rendered content identity.
**Filesystem service** (the FAT service today; one process per volume): the
proven unit — block-client + engine + file-protocol provider in one binary. It
@@ -114,12 +120,13 @@ receives its block channel at spawn; it never discovers devices. It registers
its own mounts with the kernel; its write cache lives inside the process, so a
write error is observed by the code that owns the volume and surfaces on the
owning channel (the anti-fsyncgate rule — never a system-wide dirty pool).
*(Today, interim:)* fat still hardcodes its mount prefixes (`/volumes/usb` plus
the two boot-volume hierarchy subtrees it rewrites in place); a `volumes.csv`
mount map will migrate that to the volume manager. It no longer self-acquires a
volume — the V3b flip made it receive its volume id at spawn and its block
channel from the volume manager's hello reply, consistent with "it never
discovers devices" above.
*(Built:)* fat receives its mount path as `argv[2]` from the volume manager (the
volume's id-path, e.g. `/volumes/fat-12345678`) and mounts its root there, plus
the two `/system` hierarchy rewrites it installs in place (unconditional this
increment; S3 makes them content-conditional across volumes). It no longer
self-acquires a volume — the V3b flip made it receive its volume id and block
channel from the volume manager, consistent with "it never discovers devices"
above.
**Kernel** (mechanism only): the mount table routes paths to backend
endpoints — resolve and redirect, never data. Remount-replace is the restart