Skip to content

Sandboxing an Agent by Default

Running an agent under wardcell is explicit — you type wardcell run -- claude, not claude. That is the right default for wardcell itself: nothing is sandboxed unless you say so. But for an agent you always want sandboxed, the wardcell run -- in front of every invocation is friction, and friction is what gets skipped at 6pm on a Friday.

A shell alias removes it. Alias claude to the sandboxed form, and typing the bare name does the safe thing — with the real binary one keystroke away for the times you mean to bypass it.

Put this in your shell’s startup file — ~/.zshrc, ~/.bashrc, or the fish file below — and open a new shell:

Terminal window
alias claude='wardcell run -- claude'
alias codex='wardcell run -- codex'

Now claude expands to wardcell run -- claude, claude --resume to wardcell run -- claude --resume, and so on — every argument rides along.

It does not loop. The claude after -- is an argument to wardcell, not a command the shell re-reads, so the alias fires once and no further. wardcell then execs the real claude off your PATH directly — it never goes back through an interactive shell, and the sandboxed process gets an empty-plus-policy environment that never reads your .zshrc at all. There is nowhere for the alias to re-trigger.

To run the real binary unsandboxed for a single command — without removing the alias — sidestep alias expansion:

Terminal window
command claude --version # or: \claude --version

If every sandboxed run of an agent should carry the same flag — a non-default policy root, a --profile, a --zone — a function reads more clearly than a long alias and still takes your extra arguments:

Terminal window
claude() {
wardcell run --profile developer -- claude "$@"
}

A function does not loop either, for the same reason the alias does not: its command word is wardcell, and the claude it names is an argument wardcell resolves off PATH.

An alias is a shell thing, so it covers the agent you launch by typing its name. It does not reach a GUI launch — double-clicking Claude.app, or open -a, hands the launch to launchd, and a process launchd starts never enters the sandbox. To sandbox the desktop application, launch its bundle through wardcell instead:

Terminal window
wardcell run -- /Applications/Claude.app

which you can alias the same way if you launch it from a terminal.

The alias is deliberately only as strong as your habit of typing the name. The step up in coverage is a PATH shim — a small wrapper script named claude placed in a directory earlier on $PATH than the real one, so that scripts and other programs resolving claude by name hit the wrapper too. It is a sharp tool, and worth being clear about its two edges before reaching for it:

  • It can loop. wardcell resolves the command off PATH just as the shell does, so a wrapper that calls wardcell run -- claude sends wardcell straight back to the wrapper. The wrapper has to hand wardcell the real binary by absolute path — wardcell run -- /usr/local/bin/claude.real "$@" — which means renaming the real binary and keeping the two in step across upgrades.
  • It is still not a boundary. Anything that calls the agent by its absolute path, or sets its own PATH, walks straight past the shim. A PATH shim raises the floor; it does not seal the room.

The only way to make an unsandboxed launch genuinely impossible is to remove or rename the real binary so the sandboxed path is the only path — at which point the wrapper is not raising a floor, it is the binary. That is a real option for a locked-down machine, but it is a deployment decision, not a shell alias, and wardcell does not manage it for you.

wardcell’s own boundary is the sandbox the run executes inside, not the launcher that starts it. On a managed fleet, pin that boundary where it belongs: a root-owned policy root no run can weaken, and policy_root.allow_override = false so no run can point itself at a softer one. The alias is for the single developer who wants the safe thing to be the easy thing — not the control that makes it the only thing.