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 > **Status:** the layered model below is the settled design
> ([storage-design-rationale.md](storage-design-rationale.md) records how it was > ([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 > 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` > range confinement (per-sender clamp + the confinement gate), the `medium_changed`
> presence event, the volume manager itself — it probes the partition table, > presence event, the volume manager itself — it probes the partition table,
> confines each filesystem to its partition, spawns one filesystem per volume, and > 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). > supervises it — the removal half of the lifecycle (a pulled stick unmounts), the
> **Still pending**: the fuller identity ladder and the `volumes.csv` mount map, > identity ladder (GPT GUID + name, FAT serial + label, MBR), and the mount map:
> multi-volume (one FAT volume today), the volume manager *consuming* > `filesystems.csv` (signature → binary) + `volumes.csv` (identity → optional
> `medium_changed` (removal is detected by device-presence polling; the event is > override), a volume's mount path IS its content id (`/volumes/<id>`), with the
> published but only a card-reader medium change needs the subscription), and the > label as display metadata a `volumes` query returns. **Still pending**: the
> remount-on-replug end-to-end (the logic is in place; QEMU can't re-present the > `filesystem UUID` rung (needs a non-FAT engine), multi-volume (one FAT volume
> boot-controller device, so it is bench-verified). A few markers below are left > today; fat's boot rewrites are unconditional until S3 makes them
> where a duty is still pending. > 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 ## 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 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 (**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, matching filesystem service confined to that range, and supervises it (backoff,
crash-loop cap). *(Pending)*: it decides mount placement from `volumes.csv` and crash-loop cap). *(Built)*: it picks the filesystem binary from
picks the filesystem binary from `filesystems.csv` — today it hands every `filesystems.csv` by the volume's content signature, and mounts the volume at its
FAT-shaped volume to the FAT service and the FAT service carries hardcoded mount content id (`/volumes/<id>`) — or a `volumes.csv` override. The label is display
prefixes. Those tables are CSV configuration, read by it (the policy), enforced metadata the `volumes` query returns, never the path. Those tables are CSV
by nobody else: 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. a filesystem adds a row.
- `volumes.csv` *(pending)* — the mount map, danos's fstab: **volume identity → mount - `volumes.csv` *(built)* — the mount map, danos's fstab: an OPTIONAL **volume
prefix**, keyed on content identity and never on port, path, or arrival 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 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 port move until `UUID=` replaced it). Identity is read off the medium by
the prober, strongest first: GPT partition GUID → filesystem UUID → FAT 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 identity (cloned sticks, together) is policy: first keeps the name, the
second mounts suffixed and is logged loudly. The boot volume is the second mounts suffixed and is logged loudly. The boot volume is the
recorded identity of the volume carrying `/system/configuration` and recorded identity of the volume carrying `/system/configuration` and
`/system/logs`, findable on any port. Unknown volumes mount under `/system/logs`, findable on any port. Every volume's default mount is
`/volumes/<derived name>`. `/volumes/<id>` — its rendered content identity.
**Filesystem service** (the FAT service today; one process per volume): the **Filesystem service** (the FAT service today; one process per volume): the
proven unit — block-client + engine + file-protocol provider in one binary. It 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 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 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). owning channel (the anti-fsyncgate rule — never a system-wide dirty pool).
*(Today, interim:)* fat still hardcodes its mount prefixes (`/volumes/usb` plus *(Built:)* fat receives its mount path as `argv[2]` from the volume manager (the
the two boot-volume hierarchy subtrees it rewrites in place); a `volumes.csv` volume's id-path, e.g. `/volumes/fat-12345678`) and mounts its root there, plus
mount map will migrate that to the volume manager. It no longer self-acquires a the two `/system` hierarchy rewrites it installs in place (unconditional this
volume — the V3b flip made it receive its volume id at spawn and its block increment; S3 makes them content-conditional across volumes). It no longer
channel from the volume manager's hello reply, consistent with "it never self-acquires a volume — the V3b flip made it receive its volume id and block
discovers devices" above. channel from the volume manager, consistent with "it never discovers devices"
above.
**Kernel** (mechanism only): the mount table routes paths to backend **Kernel** (mechanism only): the mount table routes paths to backend
endpoints — resolve and redirect, never data. Remount-replace is the restart endpoints — resolve and redirect, never data. Remount-replace is the restart
@@ -218,9 +218,10 @@ matrix-proven shape; genuinely open.
**content identity, never port or discovery order**. Build status: rungs 1, **content identity, never port or discovery order**. Build status: rungs 1,
3, and 4 (GPT partition GUID, FAT serial + label, MBR signature + index) are 3, and 4 (GPT partition GUID, FAT serial + label, MBR signature + index) are
implemented (S1); rung 2 waits on a non-FAT engine. The `volumes.csv` map and implemented (S1); rung 2 waits on a non-FAT engine. The `volumes.csv` map and
the id-derived mount path land with S2, so today a single volume still mounts the id-derived mount path are built (S2): a volume's mount path IS its content
at the fixed `/volumes/usb` and its recorded identity is not yet consulted to id (`/volumes/<id>`, e.g. `/volumes/fat-12345678`), or a `volumes.csv`
pick a path. The ladder the prober reads off the medium, strongest first: override; the label is display metadata a `volumes` query returns, never the
path. The ladder the prober reads off the medium, strongest first:
1. GPT partition GUID — 128-bit, unique, stable for the volume's life — **built (S1)**; 1. GPT partition GUID — 128-bit, unique, stable for the volume's life — **built (S1)**;
2. filesystem UUID (ext-family and most modern formats, in the superblock) *(planned)*; 2. filesystem UUID (ext-family and most modern formats, in the superblock) *(planned)*;
3. FAT volume serial + label — 32 bits, weak (dd-cloned sticks share it) 3. FAT volume serial + label — 32 bits, weak (dd-cloned sticks share it)