Agent shims
On this machine, the claude and codex commands are not the real programs. They are wrappers that build the walls first, then start the agent inside them.
Most of the enforcement plane only works if it actually runs. The gate is a hook — an agent started
without the hook never calls it. Landlock is a kernel wall — but someone has to install it before the
agent’s process starts. The shims solve that: ~/.local/bin/claude and
~/.local/bin/codex sit earlier on the PATH than the real binaries, so typing
the normal command gets you the wrapped version. There is no separate “safe mode” to remember to use.
The safe mode is the only mode.
What the Claude Code shim does
Section titled “What the Claude Code shim does” operator types: claude │ ▼ ~/.local/bin/claude (the shim — scripts/operator-authority-claude-code-shim.py) │ ├─ 1. load AGENT_POLICY.yaml ── fails? ─▶ refuse to start (exit 1) │ + log "shim-init-failed" ├─ 2. install Landlock on itself │ (kernel wall — inherited by every child process) │ ├─ 3. open a session row on the dashboard │ (operator can see the agent is live) │ └─ 4. exec claude-real ── the agent starts, already inside the wallStep 4 uses exec, which means the shim becomes the agent process rather than
starting it as a child it could lose track of. The Landlock rules installed in step 2 are a property of
the process itself, inherited by every shell command and subprocess the agent ever spawns. There is no
later moment where they can be removed — the kernel does not allow loosening, only tightening.
What the Codex shim adds
Section titled “What the Codex shim adds”Codex runs less interactively than Claude Code, so its shim
(scripts/operator-authority-codex-shim.py) wraps the whole run in a before-and-after
check on top of the same Landlock floor:
- Before: take a snapshot of the worktree — a content hash of every file — so there is an exact record of the starting state.
- Run: start Codex inside Landlock, same as above.
- After: run the post-hoc sweep against the snapshot. Every file that changed is checked against the policy and the worktree’s scope manifest.
If the sweep finds a change Codex was not allowed to make, the shim prints the violations and exits with code 2 — the same “this is a block, not a warning” convention the gate uses. The calling automation sees a hard failure, and the violation is in the log before anyone has to notice it by hand.
Commands: Codex’s native approval, hardened
Section titled “Commands: Codex’s native approval, hardened”The shim guards file writes (Landlock + the sweep). Shell commands are governed by
Codex’s own approval system, set to untrusted by default for this repo:
~/.codex/config.toml marks /home/mareekkk/steelmoth as
trust_level = "untrusted" (overriding the trusted parent), so interactive Codex escalates any
non-trivial command to the operator for approval instead of auto-running it.
Two honest limits. Codex’s rule grammar can’t express the policy’s globby denies at all, so interactive
command safety rests on the untrusted-escalation. And the unattended Morpheus
drain runs Codex with danger-full-access — with no operator to answer an escalation, command
approval is effectively bypassed there. Closing that edge by routing the drain’s shell commands through the
gate’s denied_commands (shim-installed PATH wrappers) is in progress;
it’s tracked on Known gaps.
The session trail
Section titled “The session trail”Both shims report the start of a run to the dashboard’s control plane, which records who the agent is, which worktree it’s in, and what scope it claims to have. When the run ends, the session is closed. The operator’s “Active Sessions” panel is therefore a live list of every governed agent on the machine — and an agent that doesn’t appear there is, by definition, running outside the shims and worth investigating.

