CLI Reference¶
Every subcommand, flag, and exit behavior below matches the CLI source
(cmd/warden). With no subcommand, warden prints usage and exits 1.
CLI Experience¶
Warden has a consistent, security-focused terminal identity. Colors and banners are semantic and degrade gracefully.
-
Banner: A large
WARDENASCII banner is shown forwarden(no args),warden init, andwarden doctorwhen stderr is a TTY. It is never shown forwarden --version,warden --help, orwarden runso that scripted and sandboxed invocations stay clean. On narrow terminals (COLUMNS<60) the banner falls back to a compact header; on CI or when output is piped it is omitted entirely. -
Header:
warden doctorandwarden initshow a compact header (WARDEN+MCP Server Sandbox Runtime+Version …) when the large banner is not used. -
Colors: Green marks success/allowed/sandbox-active, red marks blocked/denied/errors, cyan is used for structural headings/paths/metadata, and normal foreground is used for body text. Colors are semantic and never the sole signal — every state also has a text marker (
✓/✗+ALLOWED/BLOCKEDwhere applicable) for accessibility. -
Color control: Honors
NO_COLOR=1(https://no-color.org),FORCE_COLOR/CLICOLOR_FORCE, andTERM=dumb. SetNO_COLOR=1orWARDEN_NO_COLOR=1to disable all ANSI. UseWARDEN_NO_UNICODE=1to force ASCII fallbacks (OK/xinstead of✓/✗). -
Non-TTY / CI: When
CIis set or stderr is not a TTY, Warden avoids animations and uses deterministic bracketed progress ([1/4] … OK) so logs stay clean. Spinners are short, never leave stray characters, and are disabled in CI. SetWARDEN_NO_SPINNER=1to force static output. -
First-run: The first time Warden is invoked interactively it shows a one-time welcome (
Welcome to Warden+ capabilities +warden init/run/doctorhints) and then never again. The marker lives at${XDG_STATE_HOME:-~/.local/state}/warden/welcomed(0600). It is suppressed in CI, whenWARDEN_NO_FIRST_RUN=1is set, or when neither stdout nor stderr is a TTY. It never blockswarden runand never requires interactive input. -
Installation: The npm wrapper (
build/npm-wrapper/install.js) shows polished progress with TTY spinner vs CI static fallback, matching the Go banner. See Install for the expected output. -
Fail-closed messaging: When a backend cannot be initialized, Warden prints
✗ Warden refused to start: sandbox backend unavailablewith the specific reason andfails closed by design — it will never run your MCP server without a working sandbox. This is not just UI — it reflects thesandboxerr.RefuseToRunerror that prevents any unsandboxed fallback.
warden run¶
Run a server under a policy. The sandbox backend is auto-detected (native first, Docker fallback, fail-closed when nothing is available).
warden run --policy <file> [--backend auto|linux|seatbelt|windows|docker]
[--approve] [--approve-timeout <dur>] -- <command...>
| Flag | Meaning |
|---|---|
--policy <file> |
Required. Policy YAML. Relative filesystem grants resolve against this file's directory. |
--backend <name> |
Force a backend. Default auto. Aliases: bwrap → linux; macos, darwin, sandbox-exec → seatbelt; appcontainer, lowbox → windows. Unknown or unavailable backends fail closed. |
--approve |
Interactive approval: prompt on first out-of-policy access instead of hard-failing. Network approvals apply live; filesystem approvals are saved to the policy and the server restarts. Requires a terminal — fails closed without one. |
--approve-timeout <dur> |
Per-prompt timeout (e.g. 30s, 2m). Zero waits indefinitely; expiry denies. Requires --approve. |
-- <command...> |
Command to sandbox. Optional separator — everything after the flags is the command anyway; -- lets the command itself start with a flag-like token. |
Command resolution: the CLI tail wins, otherwise the policy's command: is
used, otherwise the run fails with a usage error. Bare executable names
(npx, uvx, node) are resolved via PATH lookup and fail closed when
unresolvable; prefer absolute paths in checked-in policies. Exit code: the
sandboxed process's exit code, 1 on runtime failure, 2 on usage/policy
errors.
Run output: warden run never prints the large banner — it prioritizes
runtime information. When stderr is a TTY (and CI is not set and
WARDEN_QUIET is empty) it prints a compact pre-launch summary to stderr
only (so MCP stdio is untouched):
WARDEN
──────────────────────────────────────
Policy policy.yaml
Backend linux
Command /usr/bin/node server.js
Filesystem
✓ /workspace/project (read)
✗ everything else
Network
✓ api.github.com
✗ everything else
Environment
✓ GITHUB_TOKEN
✗ all unspecified variables
──────────────────────────────────────
✓ Sandbox active
Only information from the loaded policy is shown — no permissions are
invented. In CI / piped / WARDEN_QUIET=1 the summary is suppressed so
warden run stays silent apart from the sandboxed process's own stdio.
Security events are recorded to the audit log (warden logs) as structured
allowed/blocked JSON; the CLI formats them as ✓ ALLOWED / ✗ BLOCKED
when displayed (color reinforces but never replaces the text marker).
warden trace¶
Run unsandboxed but instrumented; record every file/network access to the
trace log for init to consume:
Prints the trace file path on stderr on exit. Exits with the command's exit code.
warden init¶
Generate a conservative starter policy from a trace log. Never overwrites an existing file:
| Flag | Meaning |
|---|---|
--log <file> |
Trace log to read. Default: the latest trace. |
--output, -o <file> |
Where to write. Default policy.yaml. Refuses to overwrite. |
-- <command...> |
Recorded into the generated policy's command: field. |
Only successful observed accesses become grants; blocked events never do;
runtime paths (/usr, /proc, …) are omitted. Review and tighten the
output before relying on it.
warden logs¶
Inspect the persistent JSONL audit log
(${XDG_STATE_HOME:-~/.local/state}/warden/audit.jsonl):
--tail 0 prints nothing; --follow polls for appended events (survives
truncation). Prints "no audit events recorded yet" when the log doesn't exist.
warden doctor¶
Check that the host can actually enforce a sandbox. It probes the real
backends (bubblewrap/Seatbelt/AppContainer/Docker), strace availability on
Linux, the egress proxy, and the policy engine, and prints a
security-oriented report:
Example output (TTY):
WARDEN DOCTOR
Environment
────────────────────────────────
✓ Operating system linux/amd64
✓ Sandbox backend linux
✓ Namespace support available (bwrap)
✓ Network proxy available (egress allowlist)
✓ Policy engine ready (YAML + validation)
✓ Fail-closed enabled (never runs unsandboxed)
Security posture
────────────────────────────────
✓ Filesystem isolation explicit paths only
✓ Environment filtering explicit vars only
✓ Network policy explicit hosts only
✓ Fail-closed behavior enforced
Status: READY
When the backend is unavailable it prints Status: NOT READY in red,
the underlying RefuseToRun reason, and Warden fails closed when
sandboxing is unavailable. Exit code is always 0; the status line is
the signal for scripts.
warden version¶
Print the stamped build version and exit 0. This stays script-friendly and never prints a banner:
Single line warden version <semver> (or dev for unstamped builds).
Release builds stamp the version at link time (the npm launcher downloads
that exact version's binary); unstamped source builds report dev.
Accepts version, --version, -version, and -v.
warden gateway¶
Wrap gateway-registered servers so the gateway launches each one sandboxed:
warden gateway init --config <file> --policies <dir>
# Per-server deny-by-default policies (<name>.yaml). Remote (SSE/HTTP)
# servers are skipped — no local process to sandbox. Never overwrites.
warden gateway run --config <file> --policies <dir> --server <name> \
[--backend ...] [--approve] [--approve-timeout <dur>] -- [extra args...]
# Run one registered stdio server sandboxed. Fails closed when the policy
# is missing or the server is remote. Bare launcher names resolve via PATH.
warden gateway wrap --config <file> --policies <dir> \
[--warden-bin <path>] [--backend <name>] [--output <file>]
# Emit a gateway config whose stdio commands are prefixed with
# 'warden run --policy ...'. Point the gateway at the wrapped file.
warden gateway list --config <file> [--policies <dir>]
# Show registered servers: stdio vs remote, and policy presence.
Supported configs: Claude-style mcpServers JSON (Claude Desktop/Code,
Cursor, VS Code servers, Docker MCP Gateway clients) and gateway YAML
registries (upstreams: à la jonfairbanks/mcp-gateway, backends: à la
MikkoParkkola/mcp-gateway).
Exit codes¶
| Code | Meaning |
|---|---|
0 |
Success (for run: the sandboxed process exited 0) |
| other | For run/trace: the sandboxed/traced process's own exit code |
1 |
Runtime failure (backend unavailable, enforcement error, approval restarts exhausted) |
2 |
Usage or policy error (bad flags, unreadable/invalid policy, no command) |