Carrier Confinement
A Carrier starts with exactly the filesystem and descriptor authority its Worker's grant names.
- Standing
- STATED CHECKABLE RUNNABLE EXECUTED STAGED PUBLISHED REPRODUCED
- Last checked
- never run
- Source
- none — this record cites no check
- Limit
- Landlock ABI 9 on one host, in one lane (super/) this session does not edit. It establishes confinement for that carrier on that kernel. It does not establish confinement on any other kernel, and a same-UID battery measures yama unless a bare control runs beside it.
- Next rung
in_tree— the battery referenced here lives in super/, a lane this session does not edit, so this catalog has no witness of its own to run. Copying it in with a receipt is the step. Running it on a kernel nobody in this tree configured is the step above that, and it is the one that would count.
Why this page says STATED — the derivation, not the word
- ✗ a witness is named
- ✗ its evidence kind is one the ledger already uses
- ✗ the witness path resolves in this tree
- ✗ its rung is in_tree or above
- ✗ a run is recorded for these exact bytes
- ✓ no claim is cited that could be REFUTED
- ✗ a counterexample is shipped (required once WITNESSED)
- ✗ not staged on this site, so it cannot run from the page
WITNESSED requires every line above to hold. The label is computed from them by build.mjs and cannot be typed into the registry — the build refuses a record that carries it.
Intent
Capability-Bounded Composition at the operating-system boundary. Before a Carrier's payload runs, its filesystem view is restricted to the directories the grant names and every inherited descriptor beyond the allowlist is sealed. Sealing is done with a range close-on-exec, never a blind close, because a blind close leaves a duplicated master in the census.
Technical register
Super D.1.3b floor: Landlock (ABI 10 on this host per super/docs/reviews/D_1_3B_2F.md), the descriptor seal is CLOSE_RANGE_CLOEXEC; the refusal token carrier-confinement-unacceptable. Measured findings recorded in that lane: a same-UID battery measures yama unless it carries a bare control; static-link or grant all of /usr/lib; the ruleset fd collides with the allowlist. The witness lives in super/, which this book references and never copies.
Problem
A worker needs to write one directory and gets the parent's whole environment: every open file, every socket, the whole filesystem. The grant it was started under is then decoration. Every escape story starts here.
Solution
Build the ruleset from the grant, not from the environment. Apply it before exec. Seal descriptors by range with close-on-exec so the census can prove the seal. Run a red control: a Carrier that should be refused, and is.
Real-world analogy
A contractor given keys to one room, with the rest of the building's keys removed from the ring before they walk in — and a locksmith who checks the ring, not the contractor's promise.
Structure — on the surface
An illustration on a compute surface: loci above, carriers below. Press Play or Step; the takeaways collect as you go. Nothing here is evidence — the witness section is.
The chapter in WRL — and the chain so far
Chapter 10 of 32 — the fragment _patterns/wrl/chain/carrier-confinement.wrl, sealed alone by wrl.js
; CARRIER CONFINEMENT — the carrier (a Door) opens only through the grant (a relay); nothing else can reach
; its one input port. cc_grant is the entry and a router: chapters 11 and 12 draw from it.
[relay:cc_grant]{sig_in, sig_out}
[door:cc_carrier]{sig_in}
[cc_grant] --sig--> [cc_carrier]Its test bench _patterns/wrl/chain/carrier-confinement.bench.wrl — drives the entry for this chapter's own film; never part of the chain
; TEST BENCH — drives this chapter's entry alone; the chain replaces it with a wire from an earlier chapter
[pulser:cc_bench](every 1){sig_out}
[cc_bench] --sig--> [cc_grant]module + bench seal to → sem-ee08d1c837f3576064779542af916b85e8a15aa93c2762ae03895be01db3b26e
Reduced by the native reducer (ic32); the reference reducer reproduces every epoch's film hash (0.004s).
The Film, epoch by epoch (4)
epoch 1 · sha256:ec848f247b374548950cf62a265d8751d1f8bfa6aab6c02e30e9449c8f333eb1
FILM v0.7 t=1 pulser:cc_bench:mode=periodic,p=1,phase=0,armed=0,done=0,nf=1 door:cc_carrier:open=0,next_open=0 relay:cc_grant:cur_out=0,next_out=1 wire:w__cc_bench__cc_grant:cur=1,nxt=1 wire:w__cc_grant__cc_carrier:cur=0,nxt=0 admit:policy=admit_candidate_min_firstreceipt_v1,fact_capacity_fault=0,receipt_capacity_fault=0,capacity_fault=0
epoch 2 · sha256:994705ad72855d18169fc57966df0900266427884829470b1bb522ca78c9c3e6
FILM v0.7 t=2 pulser:cc_bench:mode=periodic,p=1,phase=0,armed=0,done=0,nf=1 door:cc_carrier:open=0,next_open=0 relay:cc_grant:cur_out=1,next_out=1 wire:w__cc_bench__cc_grant:cur=1,nxt=1 wire:w__cc_grant__cc_carrier:cur=0,nxt=1 admit:policy=admit_candidate_min_firstreceipt_v1,fact_capacity_fault=0,receipt_capacity_fault=0,capacity_fault=0
epoch 3 · sha256:d3987e4f20a91763df420e3929ef9f972d053d3200d7d5aa8c129a0a250dce2d
FILM v0.7 t=3 pulser:cc_bench:mode=periodic,p=1,phase=0,armed=0,done=0,nf=1 door:cc_carrier:open=0,next_open=1 relay:cc_grant:cur_out=1,next_out=1 wire:w__cc_bench__cc_grant:cur=1,nxt=1 wire:w__cc_grant__cc_carrier:cur=1,nxt=1 admit:policy=admit_candidate_min_firstreceipt_v1,fact_capacity_fault=0,receipt_capacity_fault=0,capacity_fault=0
epoch 4 · sha256:3136c8ef6014cafc588db0009a855e6e74ffafa9aa132d09b2bbb27b9750c2c4
FILM v0.7 t=4 pulser:cc_bench:mode=periodic,p=1,phase=0,armed=0,done=0,nf=1 door:cc_carrier:open=1,next_open=1 relay:cc_grant:cur_out=1,next_out=1 wire:w__cc_bench__cc_grant:cur=1,nxt=1 wire:w__cc_grant__cc_carrier:cur=1,nxt=1 admit:policy=admit_candidate_min_firstreceipt_v1,fact_capacity_fault=0,receipt_capacity_fault=0,capacity_fault=0
Composes with the 9 chapters before it
The chain through this chapter — every earlier fragment, this one, and the links — seals to sem-14361f945ede4619dcaa814dedc1b218bc45e91f79958e82a5a49ddf4342a3e8: 30 objects, 29 edges (was 28 / 27; every earlier object and edge is still present — checked, or the build refuses).
Links only the chain carries
[cb_in] --sig--> [cc_grant]
How to read this board
Five kinds of object, two kinds of wire, and one band per Part of the book. Signal flows left to right: it starts at a clock, travels through relays, and ends at a door — or turns a spinner, which drives an orb. Nothing below is the book's own vocabulary; each line is quoted from where the definition lives.
| shape | is | and so |
|---|---|---|
| a clock; the only source of signal | Every signal on the board starts at one of these. Nothing else can make one. | |
| a pass-through, so signal can travel | One arrives, any number leave — a relay that fans out is the board's router. | |
| a sink; signal arrives and stops | It latches what reached it and passes nothing on. A door is where a path ends. | |
| rotation: takes signal, drives a pose | The only object on the board that holds a value a claim can rewrite — and only if its config says configurable. | |
| the thing that gets moved | It is driven, never driving: an orb is what you watch to see whether anything happened. | |
| not a WRL role — the book's own drawing of the receipts in the epoch's Film | It counts what the run admitted, and turns red on a Rejected outcome. | |
| SignalWire | signal: a sig_out to a sig_in | Legal from a Pulser or Relay, into a Relay, Door or Spinner. This is how the board moves. |
| SocketControl | control: a socket to a pose | Legal only from a Spinner into an Orb. At most one may land on any input port — fan-in is a typed refusal. |
Hover any object for what it is, which chapter put it there, and every field of its line in that epoch's Film — split into what it is doing now and how it was built. Click to pin the readout, then click a wired name to follow the signal. The field definitions come from TRVM/forge/film.py (the emitter), TRVM/forge/forge_state.py (nf, derived from the decoded counter and never from t), TRVM/forge/lower_e2a.py (the commit/react law), TRVM/FORGE_SEMANTIC_IR_v1_MEASURE.md §1.3; the shapes from WRL/learn.html and WRL/docs/spec/README.md. The build refuses if a Film emits a field this key does not explain.
The board so far: one band per Part, signal flowing left to right; relays that fan out are routers, doors are switches, pulsers are clock domains. Hover an object — or click the board and walk it with the arrow keys — for its role, its Part and what it is wired to. This board is the chain’s sealed shape; no Film drives it, so it has no state to report, and the whole board in the conclusion is where every object’s state is read epoch by epoch. Wheel zooms · drag pans · double-click fits.
Syntax — quoted from the tree at build time
The refusal token, in the review that measured it (super/docs) super/docs/reviews/D_1_3B_2F.md:177
start_carrier refused: carrier-confinement-unacceptable
Forces
Confinement must happen before the payload runs and after the runtime has what it needs, and the two windows barely overlap. Dynamic linking needs /usr/lib; a ruleset lives in a descriptor that could itself be swept by the seal. And a battery that shares a UID with its subject measures the wrong enforcement layer unless it carries a control that would fail.
Applicability
Any process that runs on behalf of a grant: agent workers, plugin hosts, build sandboxes, the T&R shell's native apps.
Transformations
- narrowing the ruleset
- adding a read-only directory the grant names
- re-measuring on a host with a different ABI
- inheriting the parent's descriptors by default
- a blind close in place of the seal
- a battery without a bare control
A refusing transformation is not one that is discouraged: it is one that, applied, makes the invariant above false. The word is the tree's, and it is the same word the join uses.
Consequences
A Carrier's authority becomes a fact about its start, checkable from the outside. The honest status here: STATED — the receipts are in another lane, and this page cannot run them.
Failure mode it answers
Ambient Authority — Granting access to an environment because some part of it needs one capability. Paid for at: AGENCY.md §4 (the phrasing that walks into it); Miller 2003 is the term's source
Witness
No witness. This pattern is STATED — the tree has no check for its invariant.
Counterexample
No counterexample shipped (required only when WITNESSED).
What to take away
- from the animationThe request with everything was refused; the same request with a ruleset and a seal started, confined.
- from the syntaxThe refusal is a named token, not a crash; the seal is a range close-on-exec.
- from the literatureLandlock, Capsicum and the E language's confinement; Miller's ambient authority is the thing being removed.
- from the witnessSTATED: the witness is a review document in super/, referenced by path and never copied; ABI 10 on this host.
Prior art — and what is not claimed
| work | relation | what it shares | where it differs |
|---|---|---|---|
| Landlock | realization | a process restricts its own filesystem authority irreversibly | ABI 9 on this host, not 10 — the number was measured and corrected (super/docs/reviews/D_1_3B_2F.md) |
| Capsicum | close analogue | capability-mode confinement of a running process | FreeBSD's design reaches it by removing the global namespace rather than by layering rules |
| The E language's confinement | antecedent | a subject cannot exceed the authority it was given | E enforces it in the language; here it is enforced by the kernel under the language |
Novelty not claimed. Confinement is a solved problem with multiple shipped implementations. The witness lives in super/, a lane this session does not edit — it is referenced, not copied, and the reference is not a claim of authorship.
Realizations in the tree
- super/docs/reviews/D_1_3B_2F.md:114
Relations with other patterns
Capability-Bounded Composition WITNESSED