Files
danos/system/configuration/protocol.csv
T
Daniel Samson 301bdcaf5b volume-manager: the flip — fat is spawned, confined, and handed its channel (V3b)
The load-bearing step. The FAT service stops acquiring its own volume: the
volume manager spawns it (per volume), defines its partition range on the
storage driver BEFORE it runs, and answers its startup hello with the
range-confined block channel over a new volume-manager protocol. fat never
finds its storage by name and never sees the whole device — establishment
by lineage, one layer up from the driver tree.

- New library/protocol/volume-manager: one verb, hello(volume-id) -> the
  block channel as the reply capability (the P0 reply-cap path).
- The volume manager becomes the confinement CONTROLLER: it defines the first
  range on usb-storage, so no other party can confine a filesystem. It
  supervises the filesystems it spawns and respawns one on death (the reap-
  and-rebuild the device manager proved, one layer up).
- fat: drops acquireVolume(device-manager); hellos the volume manager for its
  channel; reads its volume id from argv[1]. main takes process.Init now.
- init.csv no longer spawns fat (the volume manager does); protocol.csv
  rewires fat to be supervised by the volume manager (bind vfs, open
  volume-manager) and drops fat open device-manager.
- The block-range fixture boots registry + device-manager only (not the full
  tree), so the volume manager is absent and the fixture stays the sole
  confinement definer — otherwise the volume manager would take the
  controller first and refuse it.

Verified end to end (VM probes -> spawns fat -> confines it -> hands over the
channel -> fat mounts) and neutral: 18/18 across the fat family, logging,
shutdown, both IOMMU variants, usb restart, vfs, conformance, confinement.
2026-08-09 18:28:44 +01:00

13 KiB

1# /system/configuration/protocol.csv — who may claim, and who may reach, a name
2# under /protocol (docs/os-development/protocol-namespace.md).
3#
4# init is the registrar: it serves /protocol, and every bind AND every open is
5# checked against this file. It is AUTHORITATIVE — a name no row grants cannot be
6# bound or reached, and a missing file means nothing may be bound or reached at
7# all.
8#
9# A refused open is answered exactly as a name nobody bound is: -ENOENT, and no
10# capability. That is not politeness, it is the model — the namespace IS the
11# restriction, so what a process may not open simply does not exist for it, and
12# Which is why a missing row here shows up as a client retrying forever rather
13# than as an error: check this file first, and `readdir /protocol` second.
14#
15# '#' starts a comment (whole-line or trailing); blank lines are ignored.
16# Whitespace around a field is trimmed, so columns may be padded. Four
17# comma-separated fields per row:
18#
19# binary the claimant's binary path, exactly as the kernel stamped it at
20# spawn (argv[0]) — unforgeable, read from the process records
21# supervisor the authorized supervising TASK, written as the binary it runs —
22# the path init was started as for its own services, the device
23# manager's path for the drivers it starts. The one word that is not
24# a path is 'kernel', because a kernel task has no binary; that is
25# what the test harness's direct spawns look like.
26# Matched by IDENTITY, not by spelling. Name alone is not identity —
27# spawn is ungated, so a hostile process can start a granted binary
28# itself and inherit its grants; and it can equally start its own
29# instance of the *supervisor's* binary and have that spawn the
30# granted one, at which point both names read correctly (the
31# laundering deputy). So init also asks which task the supervisor
32# is: 'kernel' means supervisor id 0, which only the kernel can
33# confer; init's own path means this init; any other path means a
34# task init spawned itself or one the kernel spawned. Task ids are
35# monotonic and never reused, so an id cannot be borrowed.
36# permission bind (provide this contract) | open (speak to it) |
37# supervise (stand in someone else's chain — see below)
38# name the contract, relative to /protocol
39#
40# A trailing '*' on any field matches any tail — how a subtree is granted whole.
41#
42# 'supervise' exists because attestation is one hop deep and the driver tree is
43# three: the device manager starts the PS/2 bus, and the bus starts the keyboard
44# and mouse drivers. Init never met the bus, so it cannot vouch for it by
45# acquaintance — and it must not vouch for it by name, or the laundering deputy
46# walks straight in. A 'supervise' row is the manifest saying it: a task running
47# this binary, under this supervisor, may be the supervising task an 'open' row
48# names, for this contract and no other. It grants the delegate nothing itself,
49# and it is deliberately open-only — a delegate may vouch for what its children
50# REACH, never for what they CLAIM, so every bind refusal is untouched by it.
51#
52# binary supervisor permission name
53# --- the services init spawns from init.csv ---------------------------------
54/system/services/input, /system/services/init, bind, input
55/system/services/device-manager, /system/services/init, bind, device-manager
56/system/services/volume-manager, /system/services/init, bind, volume-manager
57# fat is spawned and supervised by the volume manager now, not init — the volume
58# manager confines it to its partition and hands it the block channel.
59/system/services/fat, /system/services/volume-manager, bind, vfs
60/system/services/display, /system/services/init, bind, display
61# The discovery service ships under one neutral name per firmware (docs/discovery.md);
62# on x86 it is the acpi service, and what it provides is the power contract.
63/system/services/discovery, /system/services/device-manager, bind, power
64# --- the drivers, which the device manager spawns ---------------------------
65# usb-transfer and block have NO bind rows: several processes provide each (one
66# per controller, one per volume), so neither is ever a registry name —
67# consumers get their provider's channel from the device manager's hello,
68/system/drivers/ps2-bus, /system/services/device-manager, bind, ps2-bus
69/system/drivers/virtio-gpu, /system/services/device-manager, bind, scanout
70# --- the same providers when the kernel test harness starts them directly ---
71# A scenario boot spawns its own providers instead of letting init do it
72# (docs/security-track-plan.md, decision 9), so the same binaries appear with
73# 'kernel' as the supervisor. Nothing else changes: the binary must still match.
74/system/services/input, kernel, bind, input
75/system/services/device-manager, kernel, bind, device-manager
76/system/services/fat, kernel, bind, vfs
77/system/services/display, kernel, bind, display
78/system/services/discovery, kernel, bind, power
79# --- test fixtures ----------------------------------------------------------
80# The subtree rule, dogfooded: anything installed under /test may claim anything
81# under /protocol/test, and nothing above it — whether the harness spawned it or
82# another fixture did.
83/test/*, kernel, bind, test/*
84/test/*, /test/*, bind, test/*
85# ============================================================================
86# open — who may REACH each contract. One row per client per contract; a client
87# with no row here simply finds the name absent, forever.
88# ============================================================================
89# --- init's own services ----------------------------------------------------
90# fat reaches the volume manager to be handed its volume's block channel
91# (range-confined); the compositor reaches the scanout its driver announced, its
92# own endpoint (the mouse-listener thread opens /protocol/display like any other
93# client — threads share no handles), and the input stream that moves the cursor.
94/system/services/fat, /system/services/volume-manager, open, volume-manager
95# The volume manager reaches the device manager to be routed to each storage
96# provider's block channel, then confines a filesystem to each volume.
97/system/services/volume-manager, /system/services/init, open, device-manager
98/system/services/display, /system/services/init, open, scanout
99/system/services/display, /system/services/init, open, display
100/system/services/display, /system/services/init, open, input
101/system/services/display-demo, /system/services/init, open, display
102# --- the same two when the kernel test harness starts them directly ---------
103/system/services/display, kernel, open, scanout
104/system/services/display, kernel, open, display
105/system/services/display, kernel, open, input
106/system/services/display-demo, kernel, open, display
107# --- the drivers, and the discovery service ---------------------------------
108# Every driver says hello to the manager that started it — one row for the whole
109# subtree, because that handshake is what being a driver means. The rest are per
110# driver: the storage and HID class drivers talk to their controller, the HID
111# drivers publish into the input stream, and the GPU driver announces its scanout
112# to the compositor.
113/system/drivers/*, /system/services/device-manager, open, device-manager
114/system/services/discovery, /system/services/device-manager, open, device-manager
115/system/drivers/usb-hid-keyboard, /system/services/device-manager, open, input
116/system/drivers/usb-hid-mouse, /system/services/device-manager, open, input
117/system/drivers/virtio-gpu, /system/services/device-manager, open, display
118# --- the PS/2 child drivers, one hop further down ---------------------------
119# The keyboard and mouse drivers are started by the BUS driver, not by the
120# device manager — the one three-deep chain in the tree. Init cannot vouch for
121# the bus by acquaintance (it never started it), so the manifest authorizes it
122# explicitly, and only for the two contracts its children need.
123/system/drivers/ps2-bus, /system/services/device-manager, supervise, ps2-bus
124/system/drivers/ps2-bus, /system/services/device-manager, supervise, input
125/system/drivers/ps2-keyboard, /system/drivers/ps2-bus, open, ps2-bus
126/system/drivers/ps2-keyboard, /system/drivers/ps2-bus, open, input
127/system/drivers/ps2-mouse, /system/drivers/ps2-bus, open, ps2-bus
128/system/drivers/ps2-mouse, /system/drivers/ps2-bus, open, input
129# --- test fixtures ----------------------------------------------------------
130# The /protocol/test subtree is theirs whole, the way the bind rows give it to
131# them. Everything ABOVE that subtree is named one fixture at a time, so a
132# fixture reaches a system contract only where a scenario needs it — which is
133# what leaves the rest genuinely absent for the rest of them (the protocol-denied
134# case asks for one it was not given, and is told there is no such thing).
135/test/*, kernel, open, test/*
136/test/*, /test/*, open, test/*
137/test/*, kernel, open, device-manager
138/test/*, /system/services/device-manager, open, device-manager
139/test/system/services/input-source, kernel, open, input
140/test/system/services/input-test, kernel, open, input
141# The guessable-id probe (test/system/services/badge-scope-test) runs as two
142# processes of one binary: the owner, which the scenario spawns, and the intruder,
143# which the owner spawns with the ids it holds. Both reach the compositor — the
144# owner to create the layer, the intruder to be refused it — so the binary is
145# named twice, once per supervisor. The second row needs no 'supervise'
146# delegation: the owner was spawned by the KERNEL, which is a chain init can
147# vouch for on its own.
148/test/system/services/badge-scope-test, kernel, open, display
149/test/system/services/badge-scope-test, /test/*, open, display
150# The conformance probe (test/system/services/protocol-conformance-test) asks
151# every provider its boot bound for the envelope's reserved verbs. It reaches
152# ONLY the two contracts its own scenario boots a provider for, named one at a
153# time exactly like the two rows above — no subtree, no wildcard. Everything else
154# under /protocol stays absent for it, which is the point: the fixture walks the
155# namespace listing and reports what it could not open rather than being handed
156# the tree to make the test look broad.
157/test/system/services/protocol-conformance-test, kernel, open, input
158/test/system/services/protocol-conformance-test, kernel, open, display
159# The laundering-deputy probe (test/system/services/protocol-registry-test) runs
160# a grandchild whose supervisor is a fixture nobody authorized — that is the
161# point of it, and its bind must stay refused. It still has to report the verdict
162# it got, so its reporting channel, and nothing else, is delegated.
163/test/*, /test/*, supervise, test/verdict