Skip to content

Running Commands

Terminal window
wardcell run [OPTIONS] -- <command> [args...]

Everything after -- is the command. wardcell resolves the policy, renders it for the host backend, starts the proxy, and execs.

Three ways, and they compose:

Terminal window
# By command name — fragments with selects_on = { commands = ["git"] }
wardcell run -- git push
# By profile — loads <name>.sbx from the policy root as a root fragment
wardcell run --profile developer -- bash
# By zone — when the machine defines zones.toml
wardcell run --zone user -- git push

selects_on matching always applies. --profile adds a named fragment on top. Both then expand requires.

On macOS the command may be an application bundle:

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

Nothing else is needed — not the executable inside the bundle, not a --profile, not the bundle identifier. wardcell reads the bundle’s Contents/Info.plist and takes two things from it: CFBundleExecutable, which is the program it execs, and CFBundleIdentifier, which selects any fragment whose selects_on = { app_bundles = [...] } names it.

Reading the plist is not optional politeness — the executable is often not named after the bundle. Codex.app runs Contents/MacOS/ChatGPT.

A bare name works too, searched in the working directory, then /Applications, ~/Applications and /System/Applications:

Terminal window
wardcell run -- Claude.app

Arguments after the bundle go to the application:

Terminal window
wardcell run -- /Applications/Codex.app --disable-gpu

A fragment may append arguments of its own — every Chromium application needs one or two before it will start or can be inspected. Those are announced on stderr, and yours always win: see Program Arguments.

Fragments for a bundle grant read on the bundle with the $APP_BUNDLE token, which expands to wherever it is installed — so a fragment works whether the application is in /Applications or ~/Applications. Without that grant the launch aborts in dyld, because the executable cannot read the frameworks beside it.

Chromium-based applications — Claude, Codex, VS Code and anything else built on Electron — need more than this, and it is worth reading macOS: Chromium applications before writing a fragment for one.

--policy-root, --profile and --zone choose what resolves; --yes and --no-init control the first-run picker. The full option table, with defaults, is in the CLI reference.

The first wardcell run in a directory with no .wardcell.sbx opens a setup picker before the sandbox starts — it scans the checkout, offers the toolchains and integrations it found, and writes your choice so the run, and every run after it here, starts with that reach. It opens only on a terminal; a piped or CI run prints a one-line hint and carries on, and --yes / --no-init skip or silence it.

Working directoryyours — and $WORKDIR in policy expands to it
stdin/stdout/stderrinherited, so it is interactive
Environmentonly what the env blocks build, plus proxy variables wardcell sets
Exit codepassed straight through

The environment is the one that surprises people: it is not your shell’s environment minus some entries, it is empty plus whatever policy adds. See Environment Variables.

A run prints a few lines before the sandboxed child takes over — the run id, where its log is, and how to tail it — then one summary line after the child exits. Between the two wardcell writes nothing of its own, so a TUI agent in the sandbox is never corrupted. The summary names anything that was blocked with the exact DSL line that would allow it; everything else — every request, grant and denial — goes to the run’s log. See Viewing Logs for the summary and how to read the log, and re-run under policy draft to turn denials into rules.

Filesystem and process denials aren’t in wardcell’s data path — the kernel enforces them, so an enforcing run gets no per-path signal and a blocked file surfaces only as the command’s own non-zero exit. wardcell notices that, and on any non-zero exit prints what the code means and (unless the command was simply stopped) the policy draft --print command that reveals what was denied without touching your .wardcell.sbx. The CLI reference has the full exit-code table.