The two doors
The two doors, and the sandbox
What may an agent touch on my machine, and who can ask for what? The engine serves two doors, and every worker it starts is caged before it runs.
Two doors, one engine
The same handlers are served on two addresses with two different tables of verbs. One door is for the operator, and it carries every verb. The second is for the agents, and it carries a smaller set, so a worker cannot accept work, cannot change the settings, and cannot read the operator's material.
The boundary is held by measurement, not by convention: an operator act on the agents' door answers 403 with a sentence saying why.
The wall
The operator's door has a wall in front of it: socket credentials identify the caller, and writes carry a token. The page the binary serves is inside that wall — it is just another client of the same API, and every request it makes is one an operator could type himself.
The sandbox
Every worker is started through a launcher that applies a Landlock
policy to itself and then execs the target. The process is replaced, so
the domain is inherited directly — a live worker shows NoNewPrivs=1.
The policy is an allow-list, and it is short enough to read: the system directories read-only, TCP 443 (the model), the run's own tree, and the IPC scopes restricted. Everything not named is not there.
The policy is per role, and it lives in the engine's own database beside the role's tool declarations — so "what this kind of worker may do" is one row to look at, not a convention spread across a machine.
The tools, per role
- A worker-external runs on the machine: it may read, search and write files, run a shell, drive a browser — and it delivers.
- A worker-internal runs inside the hub, so its cost is a queue and not a machine: it gets the reader tools and the internal APIs, and no shell and no file writes.
- The operator is not a role. He is a door.
What the sandbox does not grant
The policy does not give a caged worker what the Go toolchain needs — a writable temporary directory and build cache, and a readable module cache. A caged worker therefore cannot build the project: it fails on the environment before the compiler speaks. That is a declared limit, not a hidden one, and the workaround is a worker outside the cage, on purpose, for that step.