What the Matt Shumer incident reveals about agent trust boundaries, how macOS Seatbelt works at the kernel, and how to lock Claude Code, Codex, Cursor, and OpenCode to your work directory in under five minutes.
Product setup and fail-closed design live at docs.clawql.com/agent-setup and ADR 0008. This essay is the engineering lesson: why prompts fail as security, and why the kernel has to be the one that says no.
This pairs with the forthcoming kernel and sidecar layers in Hardened Agentic Stack (process containment, seccomp, tool sandbox) — Seatbelt is the macOS laptop layer of the same trust-boundary idea.
What happened on July 10, 2026
At 12:03 PM on July 10, 2026, Matt Shumer posted on X:
“GPT-5.6-Sol just accidentally deleted almost ALL of my Mac’s files.”
The model’s own trace explained the failure:
“I caused a serious local data-loss incident. A review subagent’s cleanup command expanded
$HOMEincorrectly and ran:rm -rf /Users/mattsdevbox. I found and killed the still-running process, but material deletion occurred.”
The post got 2.7 million views in 24 hours.
Read that trace carefully. The model is describing what it did in the past tense. It caught the error. It killed the process. The deletion was already underway — because nothing sat between “agent decides to run a command” and “command executes.”
No sandbox. No kernel-level containment. No path restriction. The agent had full write access to the home directory. When $HOME expanded incorrectly and cleanup ran, there was nothing to stop it.
This is not a model-quality problem. Variable expansion bugs happen. Shell commands run with the permissions of the launching user. The failure was architectural: an AI coding agent ran with the same filesystem access as the human who started it, with no enforcement layer in between.
Lesson: if the only thing standing between a bad rm and your home directory is the model’s judgment, you do not have a security boundary. You have a hope.
Why this keeps happening
The instinct after an incident like this is to blame the model. Make it smarter. Add a system prompt that says “be careful with destructive commands.” Ask for confirmation before rm.
Those interventions are not wrong. They are insufficient — for a structural reason.
A prompt is not a security control.
A prompt is a request. A well-aligned model will usually honor it. A model that misunderstands context, hallucinates a variable value, or follows a locally coherent chain that ends in destruction will not. You cannot prompt-engineer a security guarantee: the model is the entity you’re trying to constrain, and prompts are advice to that entity, not enforcement against it.
Prompts are like asking an employee to be careful. Seatbelt profiles are like a lock on the filing cabinet. Both have a role. Only one is a control.
AI coding agents in 2026 commonly combine three capabilities that fail badly without containment:
Subagent spawning. The primary agent spins review, test, or cleanup agents that run commands the primary never typed. In Shumer’s case the destructive command came from a review subagent. The primary was as surprised as the user.
Shell access. Bash/shell tools are necessary for coding work. They are also the blast radius.
Variable expansion in generated commands. Agents write shell with variables. Mis-expansion bugs are a classic class even for experienced humans. Agents generate the same class of bugs.
Intersection: a subagent the primary doesn’t fully control, with full filesystem access, generating a command with a variable expansion bug. That’s the Shumer incident. It will recur on whatever model is current next month, because the vulnerability is in the environment, not the weights.
Lesson: hardening the model without hardening the environment trains you to be surprised by the next model.
What macOS Seatbelt actually is
macOS Seatbelt is a mandatory access control framework in the kernel since macOS 10.5 Leopard. It is not a userspace daemon you can kill, not a launchd item you can disable. When Seatbelt denies an operation, the syscall fails before it completes. The process gets an error. Circumventing that without elevated privileges to change the policy is not a creative-prompt problem — it’s a non-option for the sandboxed process.
The CLI for applying a profile is sandbox-exec: a small declarative profile plus a command to run under those constraints.
A minimal profile looks like this:
(version 1)
(allow default) ; allow everything by default
(deny file-write*) ; then deny all file writes
(allow file-write* ; then re-allow writes to specific paths
(subpath "/tmp")
(subpath "/Users/daniel/company-repos"))
The useful pattern is default-allow, broad deny for writes, then re-allow only the paths that must be writable for real work. Any write outside those paths is denied at the kernel — regardless of what the process intends.
Critical property: the sandboxed process cannot expand its own Seatbelt policy after launch. It cannot call an API to grow permissions. Child processes inherit the policy unless you explicitly configure otherwise. If the profile denies file-write* outside /Users/daniel/company-repos, then rm -rf /Users/daniel fails whether it came from the primary agent, a subagent, a subprocess of a subagent, or any other descendant.
Not “might fail.” Not “fails unless the model finds a workaround.” The kernel denied it.
What the Shumer incident looks like with Seatbelt
Same path, profile active:
- The review subagent generates cleanup. Variable expansion produces
rm -rf /Users/mattsdevbox. rmcallsunlink()(or equivalent) on the first path under that directory.- The kernel evaluates the Seatbelt policy. Writes are denied except
/tmpand the active work directory./Users/mattsdevboxdoes not match. - The kernel returns
EPERM.rmerrors; subsequent targets fail the same way.
No files deleted. The agent’s trace shows a permission error. The primary can surface it. The user sees “permission denied,” not a viral post about losing a home directory.
The critical property: this happens before any deletion. Kernel-level containment means the syscall is intercepted before the filesystem is mutated.
Setting up Seatbelt for AI coding agents
Two paths: manual (so you understand the tool) and clawql sandbox (production-ready wrappers for Claude Code, Codex, Cursor, and OpenCode).
Manual path
1. Choose one work root
mkdir -p ~/company-work/repos
Clone company repositories here. That path is what you grant write access to.
cd ~/company-work/repos
git clone [email protected]:your-org/your-project.git
2. Write a Seatbelt profile
Create ~/.sandbox-profiles/agent.sb:
(version 1)
; Start by allowing everything (default-allow model)
(allow default)
; Deny all file writes globally
(deny file-write*)
; Re-allow writes to safe locations
(allow file-write*
(subpath "/tmp")
(subpath "/var/folders") ; macOS user temp
(subpath (param "WORK_DIR")) ; active project
(subpath (param "CLAWQL_DIR"))) ; ~/.ClawQL for memory/vault
; Explicitly deny reads from sensitive locations
(deny file-read*
(subpath (param "HOME_SSH")) ; ~/.ssh
(subpath (param "HOME_AWS")) ; ~/.aws
(subpath (param "HOME_CONFIG"))) ; ~/.config (tokens for many tools)
(param "…") values are filled at launch with -D flags, so one profile works for any project directory.
Read denials matter. An agent that cannot write $HOME can still read SSH keys and cloud credentials unless you deny those paths. /var/folders is macOS’s user temp tree — blocking it casually breaks ordinary tooling.
3. Launch through sandbox-exec
sandbox-exec -f ~/.sandbox-profiles/agent.sb \
-D WORK_DIR="$PWD" \
-D CLAWQL_DIR="$HOME/.ClawQL" \
-D HOME_SSH="$HOME/.ssh" \
-D HOME_AWS="$HOME/.aws" \
-D HOME_CONFIG="$HOME/.config" \
-- claude
Same pattern for codex (swap the trailing binary). Shell aliases make that survivable:
# ~/.zshrc
alias claude-safe='sandbox-exec -f ~/.sandbox-profiles/agent.sb \
-D WORK_DIR="$PWD" \
-D CLAWQL_DIR="$HOME/.ClawQL" \
-D HOME_SSH="$HOME/.ssh" \
-D HOME_AWS="$HOME/.aws" \
-D HOME_CONFIG="$HOME/.config" \
-- claude'
alias codex-safe='sandbox-exec -f ~/.sandbox-profiles/agent.sb \
-D WORK_DIR="$PWD" \
-D CLAWQL_DIR="$HOME/.ClawQL" \
-D HOME_SSH="$HOME/.ssh" \
-D HOME_AWS="$HOME/.aws" \
-D HOME_CONFIG="$HOME/.config" \
-- codex'
4. Verify
cd ~/company-work/repos/my-project
claude-safe
# Ask the agent: create a file at ~/test.txt
# Expected: permission denied
If the file appears, sandbox-exec isn’t wrapping the process or the profile path is wrong. Fix that before trusting the setup.
5. Claude Code native sandbox as a second layer
Claude Code has built-in Seatbelt support via /sandbox or settings.json. Outer sandbox-exec plus Claude’s inner sandbox is laptop-level defense in depth — the same idea as Kata + Istio in enterprise, applied to a MacBook:
// ~/.claude/settings.json
{
"sandbox": {
"enabled": true,
"allowedPaths": ["~/company-work/repos"],
"deniedPaths": ["~/.ssh", "~/.aws", "~/.config", "~/Documents", "~/Desktop", "~/Downloads"]
}
}
If the outer profile has a gap, Claude’s sandbox can catch it. If Claude’s sandbox has a gap, the outer profile can catch it.
Lesson: two independent deny paths beat one thoughtful system prompt.
ClawQL path — four harnesses, fail-closed
clawql sandbox automates the profiles and wrappers for Claude Code, Codex, Cursor, and OpenCode:
curl -fsSL https://clawql.com/install | bash
clawql sandbox init # generate profiles + Claude settings.json
clawql sandbox verify # kernel-level containment probes
clawql claude # sandbox-exec → Claude Code
clawql codex # sandbox-exec → Codex
clawql cursor # sandbox-exec → Cursor
clawql opencode # sandbox-exec → OpenCode
What init does:
- Writes
~/.ClawQL/sandbox/claude.sb,codex.sb,cursor.sb, andopencode.sb— parameterized profiles per harness - Writes Claude
settings.jsonallowed/denied paths for double-layer containment on that harness - Sets fail-closed by default: if
sandbox-execis missing orclawql sandbox verifyfails, launch aborts instead of proceeding unsandboxed
Fail-closed is the design decision that matters. Silently falling back to an unsandboxed harness means every sandbox misconfiguration quietly removes your protection. If containment cannot be established, the agent does not start. You get an error. You fix it. Then you launch. See ADR 0008.
Command surface:
clawql sandbox init
clawql sandbox status
clawql sandbox verify
clawql sandbox edit --harness claude
clawql doctor --smoke # includes sandbox verify when enabled
Generated template (same shape as the manual profile):
(version 1)
(allow default)
(deny file-write*)
(allow file-write*
(subpath "/tmp")
(subpath (param "WORK_DIR"))
(subpath (param "CLAWQL_DIR")))
(deny file-read*
(subpath (param "HOME_SSH"))
(subpath (param "HOME_AWS"))
(subpath (param "HOME_CONFIG")))
Inspect or edit with clawql sandbox edit --harness <name>. Full reference: Local agent sandbox.
Escalation ladder: how much containment you need
Seatbelt is the right daily tool for most macOS developers. Higher risk has a ladder:
| Level | Tool | Use when |
|---|---|---|
| 1 — macOS Seatbelt | clawql sandbox init | Daily coding. Covers the Shumer class. |
2 — sandbox_exec MCP tool | CLAWQL_ENABLE_SANDBOX=1 | In-process isolation for agent-generated snippets. |
| 3 — Kata Containers | Helm sandboxKata | Enterprise Kubernetes — VM-level isolation for agent pods. |
| 4 — UTM VM | Separate macOS/Linux VM | Computer Use / screen control, or any task that must be physically off the host. |
Most developers stop at Level 1. Five minutes of setup prevents an entire incident class.
Computer Use — screen control, clicking UI, actions above the filesystem — needs Level 4. Seatbelt cannot fully contain what isn’t a path write. With UTM:
- Install UTM (Mac App Store or utmapp.github.io).
- Create a macOS or Linux VM.
- Share only
~/company-work/repos— not the full home directory. - Install the agent inside the VM.
- Do Computer Use work only there.
Overhead on modern Apple Silicon is manageable. Isolation is complete: damage inside the VM stays inside the VM.
Levels 2–3 connect to the enterprise story in ClawQL’s sandboxing module and the Hardened Agentic Stack runtime-integrity posts.
Seatbelt and Panguard are complementary, not the same control. Seatbelt is OS-level: which syscalls and paths a process may perform. Panguard (with ATR claims) is protocol-level: which tool calls an agent is allowed to attempt. If Panguard allows a call but the sandbox denies the underlying syscall, the sandbox wins. Fail-closed harness launch (clawql sandbox verify) is about whether the agent starts sandboxed at all — that is launch policy, not ATR. Mixing those names muddies the trust boundary.
Process isolation is not content isolation
A perfectly configured Seatbelt profile still leaves a hole this essay has not closed yet: what the agent reads.
The Shumer class is a process/blast-radius failure — a bad command executing with user privileges. Another class looks like Nutrient’s June 2026 near-miss: attacker-controlled text (bug reports, documents, telemetry) treated as instructions by an agent that was already constrained on disk. Kernel denials on file-write* do not inspect PDF steganography, hidden prompt payloads, or injected “runbooks.” Process sandboxing and input-content security are different boundaries.
In a full stack that means:
- Stage documents before the agent reasons. IDP / ingest scrubbing — steganography and injection detection — runs upstream so poisoned content never becomes trusted context. Part 1’s zero-trust ingest closes the telemetry side of that door; document pipelines need the same discipline.
- Log intent and outcome separately. Protocol policy can record “agent tried to run X”; Seatbelt records whether the syscall was denied. Correlate those in your observability path (LGTM orientation) rather than pretending one log line is a security architecture.
- Keep Seatbelt for daily coding latency. Profile generation via
clawql sandbox initis automation on top of a native primitive — near-zero startup cost compared with always-on VMs — which is why it is the laptop default while Kata/gVisor stay on the untrusted-document / enterprise ladder.
Lesson: sandboxed process + poisoned document still equals a steered agent. Contain the process and the content.
What Seatbelt does not protect against
Limits matter as much as capabilities.
Network is unrestricted by default. The profiles above constrain the filesystem, not egress. A manipulated agent can still make outbound requests. Host allowlists via (deny network-outbound) with re-allowances are possible and fiddly — you must know every legitimate destination.
Seatbelt does not constrain what the model says — or what arrives as “data.” It constrains what the process can do. Exfiltration through an allowed channel (write under WORK_DIR, then a legitimate sync process picks it up) still requires logging, egress policy, and tracing. Prompt injection and steganography in documents require the content gate above — not a tighter (subpath …) list.
The profile must match the workflow. Global package installs or writes outside WORK_DIR will fail until you widen the allow list or keep work local to the project. Most legitimate coding stays in-tree; the exceptions hurt if you discover them mid-session.
sandbox-exec is macOS-only. Linux has seccomp, namespaces, cgroups, bubblewrap; Windows has different primitives. Multi-platform teams need platform-specific containment — Seatbelt is not a cross-OS story.
The outer sandbox trusts Seatbelt’s correctness. The framework has been in production since 2007 and heavily used (App Store apps, iOS-style isolation). For highest-risk work, prefer the VM so you are not solely dependent on one host MAC stack.
Why policies and recommendations aren’t enough
Advice after incidents often looks like: better system prompts, confirmation for destructive commands, safer-trained models.
Do those things. Do not confuse them with security controls.
The Shumer failure mode was not “the primary agent decided to destroy the disk.” It was “a subagent generated a command with a variable expansion bug.” Careful prompting of the primary does not constrain a bug in generated shell from a child agent.
Ask of every safeguard: does it still prevent the bad outcome if the model behaves unexpectedly? A system prompt does not. Seatbelt does. The write either happens or it doesn’t, based on the kernel — not on intent.
That is ordinary defense in depth: each layer independently effective. Seatbelt still holds when the model errs, the subagent wanders, and the human is not watching the terminal in time.
Lesson: advice reduces probability. Kernel denials remove capability.
Five-minute setup
Protect now without rereading the essay:
# Option 1: ClawQL (all four harnesses, fail-closed)
curl -fsSL https://clawql.com/install | bash
clawql sandbox init
clawql sandbox verify
# Then: clawql claude | clawql codex | clawql cursor | clawql opencode
# Option 2: Manual (one harness, you own the profile)
mkdir -p ~/.sandbox-profiles
cat > ~/.sandbox-profiles/agent.sb << 'EOF'
(version 1)
(allow default)
(deny file-write*)
(allow file-write*
(subpath "/tmp")
(subpath "/var/folders")
(subpath (param "WORK_DIR")))
(deny file-read*
(subpath (param "HOME_SSH"))
(subpath (param "HOME_AWS"))
(subpath (param "HOME_CONFIG")))
EOF
# Add to ~/.zshrc, then source it:
alias claude-safe='sandbox-exec -f ~/.sandbox-profiles/agent.sb \
-D WORK_DIR="$PWD" \
-D HOME_SSH="$HOME/.ssh" \
-D HOME_AWS="$HOME/.aws" \
-D HOME_CONFIG="$HOME/.config" \
-- claude'
Verify:
cd ~/company-work/repos/my-project
claude-safe
# Ask: create ~/test-outside-sandbox.txt
# Expected: permission denied
If that fails closed, you are protected against the Shumer class. The next rm -rf $HOME expansion bug dies at the kernel before files disappear.
The kernel said no
The Matt Shumer incident was an environment failure, not a model vulnerability. Nothing stood between decision and execution with full user permissions.
macOS Seatbelt closes that gap. It has been in the kernel since 2007. App Store apps and iOS-style isolation already rely on the same family of controls. It is battle-tested, low-overhead, and takes minutes to wrap an AI coding agent.
Prompts are advice. Seatbelt is a lock. You need both. Prompts raise the odds the agent does the right thing. Seatbelt bounds the blast radius when it does the wrong thing — through a bug, a misunderstanding, or a subagent outside the primary’s direct control.
The only answer that reliably holds is still the oldest one in systems security: the kernel said no.
Reference: Local agent sandbox (macOS Seatbelt) · ADR 0008 — fail-closed local sandbox · Sandboxing: Kata, gVisor, Seatbelt.