Files
danos/system/configuration/protocol.csv
T
Daniel Samson d603d40b5c establishment: usb-transfer stops being a name
P1 of docs/establishment-planes-plan.md. The bus no longer binds
/protocol/usb-transfer — the bind race made whichever instance came second
unreachable, which on a real three-controller Ryzen meant a mouse no class
driver could reach ("could not open device 50"). Each instance hands its
serving endpoint up in the hello that already delegates its controller, and
class drivers receive their OWN controller's channel from helloForChannel —
routed by the manager's lineage, retried while a provider is mid-restart,
on one manager handle so retries never spend handle-table slots.

usb.open(bus, id) now takes the channel it used to look up; the name rows
leave protocol.csv with the code (bind 67, opens 119/120/122); the
conformance fixture's prose stops claiming the bus binds; and the stale
input-client import leaves the bus with the channel one.

Gate: 19 QEMU cases green (usb family, hubs, both IOMMU variants, fat chain,
boot-from-USB, orderly shutdown, conformance).
2026-08-09 11:48:31 +01:00

12 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/fat, /system/services/init, bind, vfs
57/system/services/display, /system/services/init, bind, display
58# The discovery service ships under one neutral name per firmware (docs/discovery.md);
59# on x86 it is the acpi service, and what it provides is the power contract.
60/system/services/discovery, /system/services/device-manager, bind, power
61# --- the drivers, which the device manager spawns ---------------------------
62# usb-transfer has NO bind row: several bus processes provide it (one per
63# controller), so it is never a registry name — consumers get their controller's
64# channel from the device manager's hello, routed by lineage (communication.md
65/system/drivers/ps2-bus, /system/services/device-manager, bind, ps2-bus
66/system/drivers/usb-storage, /system/services/device-manager, bind, block
67/system/drivers/virtio-gpu, /system/services/device-manager, bind, scanout
68# --- the same providers when the kernel test harness starts them directly ---
69# A scenario boot spawns its own providers instead of letting init do it
70# (docs/security-track-plan.md, decision 9), so the same binaries appear with
71# 'kernel' as the supervisor. Nothing else changes: the binary must still match.
72/system/services/input, kernel, bind, input
73/system/services/device-manager, kernel, bind, device-manager
74/system/services/fat, kernel, bind, vfs
75/system/services/display, kernel, bind, display
76/system/services/discovery, kernel, bind, power
77# --- test fixtures ----------------------------------------------------------
78# The subtree rule, dogfooded: anything installed under /test may claim anything
79# under /protocol/test, and nothing above it — whether the harness spawned it or
80# another fixture did.
81/test/*, kernel, bind, test/*
82/test/*, /test/*, bind, test/*
83# ============================================================================
84# open — who may REACH each contract. One row per client per contract; a client
85# with no row here simply finds the name absent, forever.
86# ============================================================================
87# --- init's own services ----------------------------------------------------
88# fat reaches the block device behind the volume it mounts; the compositor
89# reaches the scanout its driver announced, its own endpoint (the mouse-listener
90# thread opens /protocol/display like any other client — threads share no
91# handles), and the input stream that moves the cursor.
92/system/services/fat, /system/services/init, open, block
93/system/services/display, /system/services/init, open, scanout
94/system/services/display, /system/services/init, open, display
95/system/services/display, /system/services/init, open, input
96/system/services/display-demo, /system/services/init, open, display
97# --- the same two when the kernel test harness starts them directly ---------
98/system/services/display, kernel, open, scanout
99/system/services/display, kernel, open, display
100/system/services/display, kernel, open, input
101/system/services/display-demo, kernel, open, display
102# --- the drivers, and the discovery service ---------------------------------
103# Every driver says hello to the manager that started it — one row for the whole
104# subtree, because that handshake is what being a driver means. The rest are per
105# driver: the storage and HID class drivers talk to their controller, the HID
106# drivers publish into the input stream, and the GPU driver announces its scanout
107# to the compositor.
108/system/drivers/*, /system/services/device-manager, open, device-manager
109/system/services/discovery, /system/services/device-manager, open, device-manager
110/system/drivers/usb-hid-keyboard, /system/services/device-manager, open, input
111/system/drivers/usb-hid-mouse, /system/services/device-manager, open, input
112/system/drivers/virtio-gpu, /system/services/device-manager, open, display
113# --- the PS/2 child drivers, one hop further down ---------------------------
114# The keyboard and mouse drivers are started by the BUS driver, not by the
115# device manager — the one three-deep chain in the tree. Init cannot vouch for
116# the bus by acquaintance (it never started it), so the manifest authorizes it
117# explicitly, and only for the two contracts its children need.
118/system/drivers/ps2-bus, /system/services/device-manager, supervise, ps2-bus
119/system/drivers/ps2-bus, /system/services/device-manager, supervise, input
120/system/drivers/ps2-keyboard, /system/drivers/ps2-bus, open, ps2-bus
121/system/drivers/ps2-keyboard, /system/drivers/ps2-bus, open, input
122/system/drivers/ps2-mouse, /system/drivers/ps2-bus, open, ps2-bus
123/system/drivers/ps2-mouse, /system/drivers/ps2-bus, open, input
124# --- test fixtures ----------------------------------------------------------
125# The /protocol/test subtree is theirs whole, the way the bind rows give it to
126# them. Everything ABOVE that subtree is named one fixture at a time, so a
127# fixture reaches a system contract only where a scenario needs it — which is
128# what leaves the rest genuinely absent for the rest of them (the protocol-denied
129# case asks for one it was not given, and is told there is no such thing).
130/test/*, kernel, open, test/*
131/test/*, /test/*, open, test/*
132/test/*, kernel, open, device-manager
133/test/*, /system/services/device-manager, open, device-manager
134/test/system/services/input-source, kernel, open, input
135/test/system/services/input-test, kernel, open, input
136# The guessable-id probe (test/system/services/badge-scope-test) runs as two
137# processes of one binary: the owner, which the scenario spawns, and the intruder,
138# which the owner spawns with the ids it holds. Both reach the compositor — the
139# owner to create the layer, the intruder to be refused it — so the binary is
140# named twice, once per supervisor. The second row needs no 'supervise'
141# delegation: the owner was spawned by the KERNEL, which is a chain init can
142# vouch for on its own.
143/test/system/services/badge-scope-test, kernel, open, display
144/test/system/services/badge-scope-test, /test/*, open, display
145# The conformance probe (test/system/services/protocol-conformance-test) asks
146# every provider its boot bound for the envelope's reserved verbs. It reaches
147# ONLY the two contracts its own scenario boots a provider for, named one at a
148# time exactly like the two rows above — no subtree, no wildcard. Everything else
149# under /protocol stays absent for it, which is the point: the fixture walks the
150# namespace listing and reports what it could not open rather than being handed
151# the tree to make the test look broad.
152/test/system/services/protocol-conformance-test, kernel, open, input
153/test/system/services/protocol-conformance-test, kernel, open, display
154# The laundering-deputy probe (test/system/services/protocol-registry-test) runs
155# a grandchild whose supervisor is a fixture nobody authorized — that is the
156# point of it, and its bind must stay refused. It still has to report the verdict
157# it got, so its reporting channel, and nothing else, is delegated.
158/test/*, /test/*, supervise, test/verdict