OpenShell sandboxes

Sandbox CLI

Every defenseclaw sandbox command and flag for NVIDIA OpenShell sandboxes, with an example for each. Covers setup and doctor, run and connect, the activity feed and asks, undo, review and pull, policy and packs, images, wrappers, and teardown.

defenseclaw sandbox runs Claude Code, Codex, and other hook-based coding agents in NVIDIA OpenShell 0.1 sandboxes on Linux, and on Macs with Apple silicon in OpenShell MicroVMs, where every run works on a copy (see macOS). This page lists every sandbox command and flag; see also Sandbox. Packs and organization limits are covered in Sandbox policy packs and admin controls, and the openshell: keys in Configuration.

The flag tables are generated from the defenseclaw-gateway sandbox command tree, and a test keeps them in step with it. defenseclaw sandbox <command> --help prints the same flags.

How the commands run

  • One entry point. Type defenseclaw sandbox …. The Python CLI checks the command line against the same commands and flags, then replaces itself with defenseclaw-gateway sandbox … and passes your arguments on exactly as typed. legacy-cleanup is the only command the Python CLI runs itself.
  • Linux and macOS only. On Windows every command except legacy-cleanup stops with exit status 3 before doing anything; legacy-cleanup stops with status 1. setup, run, image build, and enable also stop with status 3 under WSL2, in a managed_enterprise install, and when run as root: sandboxes run as your own user. teardown also stops with status 3 in a managed_enterprise install. On a Mac sandboxes run in OpenShell MicroVMs, on Apple silicon only: on an Intel Mac every command except teardown and legacy-cleanup stops with exit status 3. A MicroVM mounts no host folders, so every run on a Mac works on a copy (see macOS).
  • The daemon does the work. Commands that create, change, or read sandboxes call the DefenseClaw daemon's sandbox API on the main API port, using the gateway token. The daemon runs them only while openshell.enabled is true, which sandbox setup turns on. The CLI keeps the terminal, the copy-mode workspace, the image builds, and the installer. pack, image, enable, disable, and doctor work without the daemon.
  • Harness names. A harness can be named by the command you type (claude, codex, opencode, copilot, kiro-cli-chat, hermes, openhands, omnigent, agy, amp, cursor-agent, devin), by its connector name (claudecode, kiro, antigravity), or by its display name. Help and hints name Kiro and Antigravity by their connector names, as image build and image list do. Setup and the image commands default to openshell.harnesses, and then to Claude Code and Codex; image rm removes only the harnesses you name. Amp, Cursor Agent, and Devin CLI are not verified end to end yet: their images build, but they never pass the hook check, so no sandbox runs them. The capability matrix says why for each harness.
  • Sandbox names. A name is lowercase letters, digits, and -, at most 19 characters: OpenShell 0.1.1 refuses to create a sandbox with a longer name. Without --name, run picks <folder>-<random>, the folder name cut to fit, for example myapp-7f3a. The harness is a label on the sandbox, not part of its name, and sandbox list shows both.
  • Output. Commands with -o, --output print text (the default) or json.
  • Exit status. 0 on success and 1 on an error. run and connect return the exit status of the harness, and exec that of its command (connect --shell exits 0 when the shell ends). When the harness exits 0 but not one of the session's hooks reached DefenseClaw, so that every tool call was blocked, run and connect exit 69. A harness that sends its first hook only with your first prompt (Kiro CLI, Hermes Agent, Antigravity, OpenHands, OmniGent, GitHub Copilot CLI) and was never prompted is not counted, unless the daemon saw it work without hooks. logs does the same when it reports a finished detached run none of whose hooks reached DefenseClaw. pull --apply exits 4 when it could not merge in place (a conflict, or git older than 2.38) and left your working tree alone: the changes went to a patch file and, in a git project, to a dc/<name> branch instead. An interrupted command returns 130 (Ctrl-C at the end-of-session question too), and an unsupported platform or setup (an Intel Mac included) returns 3.
CommandWhat it does
sandbox setupOne-time setup: OpenShell, bind mounts, telemetry, harnesses, wrappers, and images.
sandbox doctorCheck that this machine can run sandboxes.
sandbox imageBuild, list, prune, and remove the harness images.
sandbox enable, disableAdd or remove the shell wrapper that runs a harness command in a sandbox.
sandbox teardownRemove everything DefenseClaw set up for sandboxes.
sandbox runRun a harness in a new sandbox on the current folder.
sandbox connectResume a sandbox and attach the harness, or a shell.
sandbox execRun one command in a sandbox.
sandbox logsShow the output of a detached run.
sandbox list, statusList sandboxes, or show one in detail.
sandbox stop, start, deleteStop, start, or delete sandboxes.
sandbox activityThe live activity feed.
sandbox approvals, approve, rejectThe asks waiting for you, and your answers.
sandbox unblockLift an egress block.
sandbox review, undoReview a mounted project's changes, or restore its undo point.
sandbox pullBring a copy-mode sandbox's work back.
sandbox policyShow, explain, suggest, and edit the sandbox policy.
sandbox packList, show, and validate policy packs.
sandbox legacy-cleanupRemove a retired openshell-sandbox 0.0.x install.

Set up and maintain

sandbox setup

One-time setup. It checks this machine and installs OpenShell with NVIDIA's installer when you agree (sudo, sha256-verified). On Linux it enables project-folder bind mounts on your local OpenShell gateway, backing up the gateway config so that teardown can restore it, and turns OpenShell's anonymous usage telemetry off unless you keep it. On a Mac it installs OpenShell's Homebrew formula (and e2fsprogs, with the same consent) and asks instead whether to run sandboxes in OpenShell MicroVMs: yes switches the gateway to the MicroVM driver and sets its sandbox identity to your uid and gid, in one backed-up change with one restart. It then records the harnesses in openshell.harnesses, sets openshell.enabled: true, offers the shell wrappers and builds the harness images (the first build downloads about 3 GB).

defenseclaw sandbox setup
defenseclaw sandbox setup --harness claude --harness codex --wrappers
defenseclaw sandbox setup --non-interactive --skip-images
FlagTakesDefaultDescription
--harnesstext, repeatable or comma-separatedHarness to set up, added to openshell.harnesses (repeatable; default: openshell.harnesses, else claude and codex).
--install-openshellswitchInstall OpenShell with NVIDIA's pinned, sha256-verified installer (uses sudo).
--no-mountsswitchLeave bind mounts off (Linux); every run then works on a copy.
--no-wrappersswitchDo not offer the shell wrappers.
--non-interactiveswitchNever prompt: take the defaults and skip steps that need consent.
--restart-gatewayswitchRestart the OpenShell gateway to apply its configuration even while sandboxes run on it.
--skip-imagesswitchDo not build the harness images now (the first run builds them).
--upstream-telemetryswitchKeep OpenShell's anonymous usage telemetry on.
--wrappersswitchMake the harness commands run sandboxed without asking.
-y, --yesswitchAnswer every question with its default.

sandbox doctor

Checks the platform, Landlock, Docker, the OpenShell service, CLI, registration, version and compute driver, bind mounts, telemetry, ports, the DefenseClaw daemon, harness images, shell wrappers, and the organization policy. On a Mac it checks the MicroVM driver too (e2fsprogs and the driver's Hypervisor signature, the sandbox identity, the gateway-wide resources and the disk of its prepared images) and skips the checks that only matter for the Docker driver, with the reason. While the daemon serves sandboxes, it also checks that the hooks of running sandboxes reach DefenseClaw, and fails when a sandbox's hooks don't. Each failed check names its fix. --fix applies the fixes it can make as your user, after asking. Doctor exits 1 when a check fails. With --output json it prints the result and exits 0, so read the ok field instead. When sandboxes are on (openshell.enabled: true), defenseclaw doctor shows the same checks in its Sandbox section; otherwise that section is one skipped row.

defenseclaw sandbox doctor
defenseclaw sandbox doctor --fix
defenseclaw sandbox doctor --output json
FlagTakesDefaultDescription
--fixswitchApply the fixes doctor can make as your user (asks first).
--jsonswitchSame as --output json.
-o, --outputtexttextOutput format: text or json.
-y, --yesswitchApply fixes without asking.

sandbox image

The harness images are a local DefenseClaw layer on a digest-pinned NVIDIA OpenShell community base. The layer holds the harness at DefenseClaw's reviewed version, root-owned hooks, and the harness's hook configuration. An image is used only after its hooks pass a live check: they fire, a blocked tool call has no effect, and an allowed one runs.

defenseclaw sandbox image list
SubcommandPurpose
sandbox image buildBuild and hook-verify harness images (default: the configured harnesses).
sandbox image listList the harness images.
sandbox image pruneRemove superseded harness images.
sandbox image rmRemove the harnesses' images (on a Mac also their MicroVM disks).

sandbox image build

Builds and hook-checks the images of the named harnesses, or of openshell.harnesses (then Claude Code and Codex) when you name none. A current, checked image is kept unless you pass --force. The build log is written to <data_dir>/logs/sandbox-image-<harness>.log. Some builds are refused before docker runs: an openshell.image.harness_versions pin that is not an exact release, a pin with no reviewed hook contract for sandboxes, or a docker without BuildKit. Such a refusal says only why. It starts no build and keeps the previous build's log. On a MicroVM gateway it warns after a build when the volume of the driver's image cache lacks the room the first start of the new image needs (see sandbox run).

defenseclaw sandbox image build
defenseclaw sandbox image build codex --force --verbose
FlagTakesDefaultDescription
--forceswitchRebuild even when a verified image is current.
--verboseswitchStream the docker build output.

sandbox image list

Lists the recorded harness images, newest first. An image removed from Docker (with docker rmi, say) is not in the table: a note names it, the next run of its harness builds the image again, and sandbox image prune forgets its record. The FOR column says which compute driver an image is for: MicroVM for the vm driver (a Mac's gateway), docker for the docker driver. Images recorded before DefenseClaw built MicroVM images are docker. A gateway boots only the images for its driver, and when the list can tell which driver that is, a note names the images the gateway does not use. The doctor does not count those as built, and on a MicroVM gateway sandbox image prune removes them. With --output json every recorded image is listed, with "microvm": true for a MicroVM image, and one Docker no longer has carries "missing": true.

defenseclaw sandbox image list --output json
FlagTakesDefaultDescription
-o, --outputtexttextOutput format: text or json.

sandbox image prune

Removes superseded harness images. It keeps the current images and the images the daemon's sandboxes run, and leaves images that another DefenseClaw data directory recorded, or that nothing recorded. It forgets, and names, the records of images Docker no longer has. On a MicroVM (vm) gateway it also removes the run images and aliases of superseded harness images, and only while the daemon answers: without its list of sandboxes it leaves them alone. With that list, a MicroVM gateway's prune also counts every harness image built for the docker driver as superseded, however new it is (images recorded before DefenseClaw built MicroVM images are such images). Such an image goes with its run images and aliases unless a sandbox runs it. A docker gateway's prune, or one that cannot tell which driver the gateway runs, keeps the newest image of both kinds. With that list it also removes the MicroVM disk OpenShell prepared from each image it removed (about 5 GB each, in the images folder of the MicroVM driver's state directory, ~/.local/state/openshell/vm-driver by default) and says how much it freed. It removes a disk only when Docker no longer has its image and no sandbox is recorded with that image, and it never removes the driver's other state there. --dry-run says which disks it would remove. A start of an image whose disk is gone prepares it again (about a minute).

defenseclaw sandbox image prune --dry-run
FlagTakesDefaultDescription
--dry-runswitchOnly show what would be removed.

sandbox image rm

Removes the images this data directory recorded for the named harnesses, current ones included, and forgets their records: the harness image and, on a MicroVM (vm) gateway, the run images and aliases made from it. For an image already removed from Docker (with docker rmi, say) only the record goes. On a Mac it also removes the MicroVM disks OpenShell prepared from these images (about 5 GB each), by the rules of sandbox image prune: only sandbox-prepared-rootfs-* directories of image IDs Docker no longer has, and only with the daemon's list of sandboxes. When the daemon does not answer and such disks exist, it refuses and removes nothing, so that no disk is left that no command would remove later. It also refuses, removing nothing, while a sandbox uses one of the images (running, stopped, or only recorded), and names it: delete that sandbox first. A sandbox deleted with --keep-snapshot uses none. It shows what it removes and asks first; --yes does not ask, and --dry-run only shows. The next run of the harness builds its image again.

defenseclaw sandbox image rm claude --dry-run
defenseclaw sandbox image rm kiro opencode --yes
FlagTakesDefaultDescription
--dry-runswitchOnly show what would be removed.
-y, --yesswitchDo not ask.

sandbox enable

Makes typing the harness command run it in a sandbox. It writes a marked block to your shell's rc file (~/.bashrc, ~/.zshrc or $ZDOTDIR/.zshrc, or ~/.config/fish/config.fish, which is $XDG_CONFIG_HOME/fish/config.fish when XDG_CONFIG_HOME is set). The block defines a shell function named after the command that runs sandbox run <harness> with your arguments after --. The harness is also recorded in openshell.wrappers. New shells pick the block up. To skip the sandbox for one call, run the command with DEFENSECLAW_NO_SANDBOX=1 set. Inside a sandbox the function runs the harness directly. The bash and zsh block first drops an alias of the same name (Claude Code's installer adds one), which would otherwise keep running the harness outside the sandbox, and drops it again just before the first prompt, in case an installer or a file your rc sources defines one after the block. --version, --help and the harness's own management commands (claude mcp add|list|…, claude config, claude update, claude setup-token, codex login, codex mcp …) start no agent, so they run with the harness installed on this machine. Nothing outside the block changes, and removing the last wrapper removes the block. An --rc file is recorded, so disable, doctor and teardown find it again.

defenseclaw sandbox enable claude
defenseclaw sandbox enable codex --shell zsh
FlagTakesDefaultDescription
--rctextRc file to edit (default: the shell's).
--shelltextBash, zsh or fish (default: $SHELL).

sandbox disable

Removes the harness's wrapper block. Without --shell or --rc, it removes the block from the rc files of bash, zsh, and fish, and from every --rc file enable wrote.

defenseclaw sandbox disable claude
FlagTakesDefaultDescription
--rctextRc file to edit (default: the shell's).
--shelltextBash, zsh or fish (default: $SHELL).

sandbox teardown

Deletes DefenseClaw's sandboxes, its OpenShell providers, and its harness images. Provider profiles are shared by every DefenseClaw data directory on the OpenShell gateway, and teardown removes each DefenseClaw profile that no remaining provider uses: this data directory's ingress profiles, the legacy defenseclaw-ingress profile, and the shared model-credential and --credential profiles, including ones another DefenseClaw data directory imported. The daemons import them again when they need them. Another daemon's ingress profile is never removed. With the images it removes the MicroVM disks OpenShell prepared from them, once no sandbox that could boot one is left, as sandbox image prune does. Teardown restores the OpenShell gateway config that setup changed, unless the file changed since. It also removes the shell wrappers and sets openshell.enabled: false. OpenShell itself stays installed. defenseclaw uninstall runs teardown for you. It works without a DefenseClaw config, so it can clean up a half-installed host.

defenseclaw sandbox teardown --dry-run
defenseclaw sandbox teardown --yes --keep-images
FlagTakesDefaultDescription
--dry-runswitchOnly show what would be removed.
--keep-imagesswitchKeep the harness images.
-y, --yesswitchDo not ask.

Run and manage sandboxes

sandbox run

Runs a harness in a new sandbox on the current folder. On Linux (OpenShell's Docker driver) the folder is mounted live by default, secret files are masked, .git/hooks and .git/config are read-only, and a snapshot is taken first so that undo can restore the folder. --copy works on a copy with secrets held back instead, and you bring its work back with pull. On a Mac (OpenShell's MicroVM driver, which mounts no host folders) every run works on a copy, and run says so in one line before it copies anything. Skip-permissions mode is on by default, and --safe keeps the harness's own prompts. The harness gets your terminal. Arguments after -- go to the harness.

While the harness runs, the session announces what can't wait: each ask (a port on your machine, a private address, a destination the profile does not list) with its destination and the sandbox approve command that answers it from another terminal (or defenseclaw tui, key 7, then t for the Asks view); a destination DefenseClaw blocked, with the sandbox unblock command that lifts the block; a large upload the block did not cut (⚠ large upload to files.example.net (more than 25 MiB), with the block off or for a destination exempt from it); a finding such as an alert on a tool call or hook tamper; a quarantined nested repository; and a DefenseClaw daemon that does not answer (the hooks fail closed until it is back). The harness draws its own screen, so these notices go to the terminal's title and, where the terminal has them, a desktop notification, and ring the bell, instead of lines written over it. A harness that sets the title itself (Claude Code does) takes it back at once, so the end-of-session summary repeats them. Without a terminal (--prompt), they are lines on standard error. A harness that sends its first hook only with your first prompt is not warned about while it waits for one.

Before the harness runs (while the image, the sandbox and a copy are set up), Ctrl-C, a closed terminal window or a kill of the CLI end the run and delete the sandbox it created. At a question at the end of the session they leave the changes where they are (kept in the folder, or in a copy-mode sandbox for pull) and the session still ends as below; a second Ctrl-C ends the CLI at once. run then exits with status 130.

When the harness exits (Ctrl-C, a closed terminal window or a kill of the CLI end the harness, not the session), the sandbox the session started is stopped first, so nothing it left running changes the folder after the review. If the sandbox was stopped, undone or deleted from elsewhere (another terminal, the TUI), which ends the harness, the summary starts by saying so. You then get a summary line: tool calls and blocks (the blocking rule's title and ID), new sites contacted and sites blocked (destinations, each counted once; the blocked ones are the ✗ lines that follow, invalid destinations included), and files changed, followed by the session's notices. Changes that can run code on your machine are called out, and asks still waiting are named. For a mounted folder you then keep the changes, undo them, or see the diff first (a letter, then Enter; a long diff opens in $PAGER, by default less), and a kept change is confirmed (openshell.workdir.on_exit can decide for you). Ctrl-C at that question decides nothing: the changes stay, the undo point is kept for undo, the sandbox is stopped and kept, and the command exits 130. Otherwise, without a terminal or with --yes, a mounted folder keeps them. When the review fails, you are still asked, and --rm keeps the undo point. --rm also keeps it when nobody accepted the changes (no terminal and no --yes), so sandbox undo still reverts them; sandbox delete drops it. In copy mode you apply them (3-way), put them on branch dc/<name>, write a patch, or skip, which leaves them in the sandbox for pull. Without a terminal, or with --yes, copy mode skips: nothing is applied, and the end says how many files changed and names the sandbox pull that brings them back. The sandbox is then stopped and kept for connect, unless you passed --rm. In copy mode --rm is not honored while changes are left in the sandbox (you skipped, declined changes that can run code on your machine, or the pull or apply failed), so they aren't lost, and the end says so. A pull that fails because the disk is full says whose disk: this machine's, or on a Mac the MicroVM's own (overlay_disk_mib).

A kept sandbox's resume line is followed, last, by the command that continues the same conversation inside the sandbox, for every harness that can continue one: → continue this conversation: defenseclaw sandbox connect myapp-7f3a -- --continue for Claude Code, OpenCode, GitHub Copilot CLI, Hermes Agent and OmniGent, -- resume --last for Codex, -- --resume for Kiro CLI, -- --resume --last for OpenHands and -- -c for Antigravity. A plain connect starts a new conversation. The resume line the harness itself prints as it exits (claude --resume <id>, codex resume <id>, opencode -s <id>, copilot --resume=<id>, kiro-cli --resume-id <id>, hermes --resume <id>, openhands --resume <id>, omnigent run … --resume <id>, agy --conversation=<id>) works only inside the sandbox, where the conversation is: typed on this machine it does not reach it, unless the shell wrapper is on (sandbox enable) for that command, which sends it into the folder's sandbox. DefenseClaw cannot keep that line off the screen, so its own line says so. After a session with no tool call it does not say the harness printed one: GitHub Copilot CLI, for one, prints its line only after a prompt. To resume a particular conversation, pass the harness's own flag and ID after --: defenseclaw sandbox connect myapp-7f3a -- --resume <id>.

When you run interactively without --name and this folder already has a sandbox of the same harness, run offers to resume it; --new starts another one instead. A resumed sandbox keeps the settings it was created with, so when you pass flags only a new sandbox takes (--safe, --pack, --credential, --env and the like) and the sandbox doesn't already have them, the offer names them and defaults to no. It keeps its credential bindings and host ports too: the offer names those this run did not ask for and defaults to no, and the banner and sandbox status NAME show the sandbox's own. Typing the run's own command again is a plain resume. A sandbox the current policy would not start (a live mount your organization now runs on a copy, a harness it no longer allows) is not offered: run names it and the sandbox delete command, and status and connect say the same. Only one sandbox can mount a folder live, so while any sandbox that mounts the folder exists (even stopped), a new live run there is refused. In a terminal, run names that sandbox, its phase and the sandbox connect and sandbox delete commands, then asks whether to work on a copy (the default), delete it first, or quit; without a terminal, use --copy, or delete the other sandbox first. Inside a sandbox, sandbox run <harness> runs the harness directly.

run remembers, for the sandbox it created, the harness options after -- (not a prompt), the --credential names and hosts, the --env names and the banner's model line; secret values are never kept. The options stop at the first word that is not one, except for OmniGent, whose every argument is kept in order (the agent path included), an OpenCode project path, and Hermes's chat. connect, and a shell wrapper's resume, pass those options again before any new ones, and show the same Model and Secret lines as the run.

defenseclaw sandbox run claude
defenseclaw sandbox run codex --copy --name fix-tests
defenseclaw sandbox run claude --detach --prompt "fix the failing tests"
defenseclaw sandbox run claude --credential STRIPE_API_KEY=api.stripe.com -- --model sonnet
defenseclaw sandbox run claude --profile balanced --context ~/code/shared-lib --host-port 5432

A few rules the flags follow:

  • --detach needs a prompt: --prompt TEXT, or the harness's own print mode after -- (for example -- -p "TEXT" for Claude Code or -- exec "TEXT" for Codex). It can't be combined with --rm. Follow the run with sandbox logs -f.
  • Without a terminal, run needs --prompt.
  • A headless run in the foreground (--prompt, or the harness's print flag after --, as the shell wrapper's claude -p passes it) deletes the sandbox it created when it ends, by the rules of --rm: a mounted folder's undo point stays when nobody kept the changes, and a copy whose work was not brought back keeps its sandbox. --keep, or openshell.keep_headless: true, keeps it; --keep with --rm is refused. Interactive sessions, --detach runs and a run that resumes this folder's sandbox keep theirs.
  • --llm auto shares the first model credential it finds for the harness. For Claude Code that is ANTHROPIC_API_KEY, then CLAUDE_CODE_OAUTH_TOKEN. For Codex it is OPENAI_API_KEY (or CODEX_API_KEY), then the API key that codex login --with-api-key saved in ~/.codex/auth.json; a ChatGPT login is not shared. An Amazon Bedrock key (AWS_BEARER_TOKEN_BEDROCK) comes last, for the harnesses that have a Bedrock profile. --llm bedrock shares only that key, for Amazon Bedrock in --bedrock-region. OpenShell gives the harness a placeholder that works only against the provider's hosts.
  • Without --llm, a run takes openshell.llm (auto unless set). The shell wrappers, the TUI and the macOS app pass no --llm, so that key is how the runs they start share a chosen credential. A provider in openshell.llm that the harness has no profile for gives way to auto, and the run says so (a harness that shares no model credential at all, such as Kiro CLI, takes auto without a note); one whose key is not set refuses the run, naming the key. With --llm none, or when nothing is found, you log in inside the sandbox, which stores a real token there that the agent can read. A Claude subscription can instead run claude setup-token on this machine (with the shell wrapper on, DEFENSECLAW_NO_SANDBOX=1 claude setup-token) and export the token it prints as CLAUDE_CODE_OAUTH_TOKEN; the banner suggests it only when Claude Code is installed here.
  • --credential NAME=host[:port] gives the sandbox a placeholder in $NAME that OpenShell swaps for your value only on requests to that host (port 443 unless you give one). --github-write does the same for your GH_TOKEN or GITHUB_TOKEN and api.github.com: the placeholder works there with everything the token may do, in any repository it reaches, and git push over HTTPS (to github.com) is not authenticated. A binding of the harness's model key is the run's model credential, as the banner's Model line says.
  • --env KEY=VALUE is for non-secret settings: the sandbox holds the value in plain text, which the agent can read. A name that looks like a secret (KIRO_API_KEY, *_TOKEN, *_PASSWORD) gets a warning; bind a key with --credential NAME=HOST instead, so the sandbox sees only a placeholder.
  • A sandbox with the egress proxy sets NODE_NO_WARNINGS=1, so Node's [UNDICI-EHPA] EnvHttpProxyAgent is experimental warning, which the proxy settings trigger, does not print above the harness or in the agent's node and npm output. It hides Node's other warnings too, including the one that says TLS certificate checks are off (NODE_TLS_REJECT_UNAUTHORIZED=0); pass --env NODE_NO_WARNINGS=0 to a new sandbox to keep them.
  • Hermes Agent's managed defenseclaw provider reads an OpenAI-compatible endpoint from HERMES_DEFENSECLAW_BASE_URL and its key from HERMES_DEFENSECLAW_API_KEY: for your own endpoint, run defenseclaw sandbox run hermes --llm none --credential HERMES_DEFENSECLAW_API_KEY=HOST[:PORT] --env HERMES_DEFENSECLAW_BASE_URL=https://HOST/v1 -- --provider defenseclaw -m MODEL.
  • --pack, --profile, --copy, --safe, --unmask, --host-port, --no-mcp, --cpu, and --memory go through the sandbox policy. An organization limit can refuse them ("blocked by your organization's DefenseClaw policy") or lower them ("limited by …; running with X instead of Y"), and sandbox policy explain shows the outcome before you run. A flag the organization lowers, --cpu and --memory included, is confirmed before anything is created; answering no creates nothing. When the organization puts the folder on a copy (openshell.admin.require_copy_for, or a required pack that works on copies), run says so before it copies.
  • In a safe run (--safe, or a policy that keeps the prompts), harness arguments that would turn the prompts off are dropped with a warning that names the organization when the policy is its. OmniGent has no skip-permissions switch: its banner shows that its approvals come from its own policies, DefenseClaw's included.
  • A folder that can't be mounted live (a linked git worktree, a git directory outside it) runs on a copy, with one line saying why.
  • A copy larger than openshell.workdir.max_upload_mb (500 MB by default) is refused before a sandbox exists, and the refusal names the setting; on a Mac it also says that a copy is the only way the project runs there.
  • On a gateway whose compute driver mounts no host folders (a Mac's MicroVM driver), --context is refused before anything is copied, --no-snapshot is said not to apply, and --cpu and --memory are said to have no effect: every MicroVM gets the gateway's vcpus and mem_mib, so an organization's limit on the flags is not applied to them. The first start of an image the driver has not prepared yet says it takes about a minute. That start prepares a disk of about the image's size in the driver's image cache: before anything is copied, run refuses it when the volume has less free space than the image's size plus 1 GiB (at least 6 GiB), and warns below twice that (at least 12 GiB), naming what is free and sandbox image prune. The daemon refuses such a create from any client the same way.
FlagTakesDefaultDescription
--bedrock-regiontextAmazon Bedrock region for --llm bedrock (default $AWS_REGION, then $AWS_DEFAULT_REGION, then us-east-1).
--contexttext, repeatableExtra folder mounted read-only (repeatable; mount mode only).
--copyswitchWork on a copy of the folder with secrets held back; bring changes back with pull.
--cputextCPU limit, for example 2 or 500m.
--credentialtext, repeatableNAME=host[:port]: give the sandbox a placeholder for $NAME that works only against that host (repeatable).
-d, --detachswitchRun in the background (needs --prompt); follow with sandbox logs -f.
--envtext, repeatableKEY=VALUE non-secret variable for the sandbox (repeatable).
--github-writeswitchBind your GitHub token (GH_TOKEN or GITHUB_TOKEN) to api.github.com so gh can call the GitHub API (for example to open pull requests) with everything the token may do; git push over HTTPS is not covered.
--host-portport, repeatable or comma-separatedOpen this localhost port on your machine to the sandbox (repeatable).
--keepswitchKeep the sandbox of a headless run (--prompt), which is otherwise deleted at its end when nothing is left in it to bring back or undo.
--llmtextModel credential to share: auto, none, anthropic, claude-oauth, openai, bedrock or gemini (default: openshell.llm, which is auto unless set).
--memorytextMemory limit, for example 4Gi.
--nametextSandbox name, at most 19 lowercase letters, digits and '-' (default <folder>-<random>).
--newswitchStart a new sandbox even when one already holds this folder.
--no-buildswitchFail instead of building a missing harness image.
--no-mcpswitchLeave the harness's MCP servers behind.
--no-snapshotswitchSkip the pre-session snapshot (and so undo).
--packtextSandbox policy pack (open, balanced, strict, or a custom pack).
--profiletextNetwork profile: open, balanced or strict.
-p, --prompttextRun the harness headless with this prompt.
--refreshswitchWhen resuming a copy-mode sandbox, copy the folder again.
--rmswitchDelete the sandbox when the session ends.
--safeswitchKeep the harness's own permission prompts (skip-permissions off).
--unmasktext, repeatableShare a masked secret file or glob with the sandbox (repeatable).
-y, --yesswitchTake the defaults at the end of the session (mount: keep the changes; copy: leave them in the sandbox for pull).

sandbox connect

Resumes a sandbox: it starts the sandbox if it is stopped, then attaches the harness to your terminal, or a login shell in the project folder with --shell. The harness gets the options its run was given after -- first, then the ones you pass now, and the banner shows the run's Model and Secret lines. Both kinds of session end the same way as a run session, with the summary and the keep-or-undo question (a --shell session exits 0). Needs a terminal, except with --prompt TEXT (or the harness's own print flag after --), which runs one prompt headless. A session stops the sandbox at its end only when it started it, and leaves one whose detached run is still going, or that another session is attached to, running. Two sessions can share a sandbox (a second claude in the folder offers to resume it): neither one's end stops it, undoes the folder or deletes it (--rm) under the other, and the one that ends first names the review and undo commands for later. To continue the harness's last conversation, pass its own flag: -- --continue for Claude Code, OpenCode, GitHub Copilot CLI, Hermes Agent and OmniGent, -- resume --last for Codex, -- --resume for Kiro CLI, -- --resume --last for OpenHands and -- -c for Antigravity.

defenseclaw sandbox connect myapp-7f3a
defenseclaw sandbox connect myapp-7f3a -- --continue
defenseclaw sandbox connect fix-tests --refresh -- --model sonnet
defenseclaw sandbox connect myapp-7f3a --shell
FlagTakesDefaultDescription
-p, --prompttextRun the harness headless with this prompt.
--refreshswitchCopy-mode: copy the folder into the sandbox again first.
--rmswitchDelete the sandbox when the session ends.
--shellswitchOpen a shell in the sandbox instead of the harness.
-y, --yesswitchTake the defaults at the end of the session (mount: keep the changes; copy: leave them in the sandbox for pull).

sandbox exec

Runs one command in a running sandbox, in the project folder unless you pass --workdir. The command gets the same environment, including the egress proxy, as the harness. A terminal is allocated when yours is one. The command goes after --.

defenseclaw sandbox exec myapp-7f3a -- npm test
defenseclaw sandbox exec fix-tests --no-tty -- git status --short

What the command starts in the background keeps running after it exits. With a terminal, OpenShell keeps the exec open while such a process runs (a nohup server, say), for about 30 seconds, and then ends it with status 74. DefenseClaw names the process as the command exits. Start a background server with --no-tty to get the prompt back at once.

FlagTakesDefaultDescription
--no-ttyswitchNever allocate a terminal.
--ttyswitchAllocate a terminal even when this one is not.
--workdirtextWorking directory in the sandbox (default: the project).

sandbox logs

Shows the output of a detached run (run --detach) and whether the run is still going, finished (with its exit status) or was interrupted. -f follows the output until the run ends. Once the sandbox is stopped, it shows the last 1 MiB of the log that DefenseClaw kept on this machine as it stopped the sandbox, whoever stopped it (stop, the TUI, the macOS app, undo, a tamper stop). The agent writes this log, so on a terminal its escape sequences (other than colors) and control characters print as � rather than acting on the terminal; piped or redirected to a file, the log is written as it is. A one-prompt run --prompt prints the harness's output the same way.

defenseclaw sandbox logs fix-tests -f
defenseclaw sandbox logs fix-tests --lines 50
FlagTakesDefaultDescription
-f, --followswitchKeep following the output.
-n, --linesnumber200Lines to show.

sandbox list

Lists this DefenseClaw's sandboxes with their harness, phase, workspace mode, profile, uptime, hook traffic, and project folder. The HOOKS column reads tamper! once a tool call ran without a DefenseClaw verdict, unreachable! while the hooks do not reach DefenseClaw, and silent! while the harness works without hook traffic.

defenseclaw sandbox list
FlagTakesDefaultDescription
-o, --outputtexttextOutput format: text or json.

sandbox status

Without a name, shows the sandbox subsystem: whether it is on and reachable, the OpenShell gateway, the listener addresses, the default pack and profile, the organization policy, and counts. With a name, shows that sandbox in detail: the harness and its version, phase, project folder, permissions mode, policy, hook tamper tier, hook traffic, the verdicts per hook event under the harness's own event names ("Hook events PreToolUse 12 · PostToolUse 11 · Stop 2"), the last blocked tool call, the tool calls that ran without a DefenseClaw verdict (hook tamper), egress totals (destinations contacted and blocked, each counted once, as the activity feed names them; the JSON also has blocked_requests), credential endpoints, the undo point, waiting asks, nested repositories, and warnings, including a stopped sandbox the current policy would not start.

defenseclaw sandbox status
defenseclaw sandbox status myapp-7f3a --output json
FlagTakesDefaultDescription
-o, --outputtexttextOutput format: text or json.

sandbox stop

Stops a sandbox. It is kept, so start or connect can resume it. When a detached run is still going, stop asks first on a terminal (--yes skips the question; without a terminal it warns). The stop marks the run interrupted and keeps its log for sandbox logs, as every stop does.

For a copy-mode sandbox that no detached run and no other session is using, stop first looks at its copy. When nothing in it is left to bring back (it is as it was uploaded, or as its last pull took it and that pull went to your folder, a branch or a patch file), delete of the stopped sandbox does not warn about unpulled work, as after a session that brought everything back. When the copy is as its last pull took it, the next pull uses that pull instead of starting the sandbox.

defenseclaw sandbox stop myapp-7f3a
FlagTakesDefaultDescription
-y, --yesswitchStop without asking when a detached run is still going.

sandbox start

Starts a stopped sandbox as a new session. A mounted project gets a fresh snapshot only when nothing would be lost: there is no snapshot yet, the last one was undone, or the folder hasn't changed since it. If an earlier session's changes are still in the folder, the old snapshot is kept, so undo can still revert them, and the activity feed says so. Changes you kept at the end of a session ("Keep changes?", --yes or on_exit: keep) count as accepted: DefenseClaw records it, and the next start takes a fresh snapshot, whether start, connect, the TUI or the macOS app starts it. --new-snapshot accepts the changes and takes a fresh snapshot; --no-snapshot always keeps the old one. start says which undo point applies, and when it kept the old one, that stopping the sandbox and starting it with --new-snapshot accepts the changes instead. A sandbox that is already running is left as it is. Attach to it with connect.

defenseclaw sandbox start myapp-7f3a
FlagTakesDefaultDescription
--new-snapshotswitchTake a new snapshot even if the folder still has an earlier session's changes (undo no longer reverts them).
--no-snapshotswitchKeep the previous session's snapshot instead of taking a new one.

sandbox delete

Deletes sandboxes together with their OpenShell providers and credentials, and their undo point (snapshot) unless you pass --keep-snapshot. Asks once per sandbox unless you pass --yes. A copy-mode sandbox holding work that was never pulled back (or a pull never applied) is named in the question, which then defaults to no; teardown lists such sandboxes before it asks. A stopped copy-mode sandbox is not warned about when what stopped it (a session's end, a pull that started it, or stop) found nothing left in it to bring back. For a copy-mode sandbox whose work came back, delete says where it last went and when, and so does its warning:

  fix-tests's work was last applied to ~/code/myapp at 14:03; nothing newer is left in it
defenseclaw sandbox delete myapp-7f3a
defenseclaw sandbox delete fix-tests docs --yes
FlagTakesDefaultDescription
--keep-snapshotswitchKeep the pre-session snapshot.
-y, --yesswitchDo not ask.

During a session

sandbox activity

Shows the activity feed of every sandbox, or one: destinations reached (✓) and blocked (✗), large uploads, unblocks, asks and their answers, blocked tool calls, findings such as hooks that stopped reaching DefenseClaw, lifecycle changes (a sandbox being created, becoming ready, stopping, or being deleted), and workspace events such as an undo or a copy-mode upload. A destination shows its port unless that is 443 (HTTPS): a plain-HTTP request reads example.com:80, so an HTTPS and an HTTP request refused at the same host are two different lines. An IPv6 address with its port is bracketed: [fd00:ec2::254]:80. A blocked destination that can be unblocked shows the unblock command to run. A large upload is a ⚠ report, made as it crosses the threshold, so it says more than it (⚠ large upload to files.example.net (more than 25 MiB)), or, with the large-upload block on, a ✗ that names the threshold, such as ✗ files.example.net (large upload blocked: this sandbox tried to send more than 10 MiB to a destination it had not contacted before), with its unblock command. -f keeps following the feed. The daemon keeps a bounded buffer of recent events. Each event has a sequence number, and --since starts after one.

defenseclaw sandbox activity -f
defenseclaw sandbox activity --sandbox myapp-7f3a --output json
FlagTakesDefaultDescription
-f, --followswitchKeep following the feed.
-o, --outputtexttextOutput format: text or json.
--sandboxtextOnly this sandbox.
--sincenumber0Start after this event sequence number.

sandbox approvals

Lists the asks waiting for you. Asks are rare: a program in the sandbox tried to connect somewhere directly, around the egress proxy, and the policy would not decide it alone. Typical asks are ports on your machine and private network addresses. --watch keeps the list current.

defenseclaw sandbox approvals
defenseclaw sandbox approvals --sandbox myapp-7f3a --watch
FlagTakesDefaultDescription
-o, --outputtexttextOutput format: text or json.
--sandboxtextOnly this sandbox.
--watchswitchKeep watching for new asks.

sandbox approve

Approves an ask. The approval opens a direct OpenShell rule for the destination. It is applied at the sandbox's next quiet moment, because every OpenShell policy change closes the sandbox's open connections. --always also saves the hosts to openshell.egress.unblocked for future sandboxes. It is refused, and nothing is approved, for the typical asks: a port on your machine, a destination on your private network (a private address, an intranet name such as *.corp, or a name that resolves to a private address), or an ask with allowed IP addresses (allowed_ips). Those open for one sandbox only, so approve them without --always; to open a private destination for every sandbox, add it to openshell.egress.allow. For a port that future sandboxes need, pass --host-port to run or add it to openshell.mcp.host_ports.

defenseclaw sandbox approve myapp-7f3a a1b2c3
defenseclaw sandbox approve myapp-7f3a a1b2c3 --always --reason "team registry"
FlagTakesDefaultDescription
--alwaysswitchKeep the decision for future sandboxes.
--reasontextNote recorded with the decision.

sandbox reject

Rejects an ask. --always also adds the hosts to openshell.egress.block.

defenseclaw sandbox reject myapp-7f3a a1b2c3 --reason "not needed"
FlagTakesDefaultDescription
--alwaysswitchKeep the decision for future sandboxes.
--reasontextNote recorded with the decision.

sandbox unblock

Lifts an egress block for one sandbox (--sandbox NAME) or for every sandbox (--always, saved to openshell.egress.unblocked). Choose exactly one of the two. Only blocks from the blocklist feed or the profile's default can be lifted: IP addresses under open, and hosts off the allowlist under balanced. Your own block list, private networks, this machine, and your organization's lists can't be unblocked, and neither can anything under strict, which runs without the egress proxy. With openshell.admin.allow_unblock: false, nothing can be unblocked. The decision order has the details.

An unblock also stops DefenseClaw's destination rules (such as C2-WEBHOOK-SITE for webhook.site, or C2-NGROK) from flagging or blocking tool calls that name the unblocked host, in the sandboxes the unblock covers: the agent is no longer told a destination is blocked that the proxy lets it reach, and the activity feed shows no finding for it. A call that names a subdomain you did not unblock, that another rule flags too, or that Cisco AI Defense or the LLM judge flags or blocks (even without naming a rule), keeps its verdict.

The agent learns the command from DefenseClaw too. When the proxy refuses an HTTPS destination, the hook of the next shell or web-fetch tool call tells the agent the destination, why it is blocked, and the sandbox unblock command for you to run (Claude Code, Codex, Copilot CLI, Cursor and Devin). Once you unblock the host, the agent is no longer told of that block.

defenseclaw sandbox unblock webhook.site --sandbox myapp-7f3a
defenseclaw sandbox unblock docs.example.com --always
FlagTakesDefaultDescription
--alwaysswitchUnblock for every sandbox from now on.
--sandboxtextUnblock for this sandbox only.

After a session

sandbox review

Reviews a mounted project's changes since the session's snapshot. It prints a summary of the changes and flags the files that can run code on your machine, such as package.json scripts, build and task files, CI workflows, git attributes and submodules, harness and agent config (git-ignored files such as .claude/settings.local.json included), lock files, new or newly executable files, symbolic links, and embedded git repositories, new ones and changes to the git config, hooks or .git pointer of those that were there before. It also flags secret-like files, and lists the review's findings and the nested repositories the session's guard found. --diff prints the unified diff too.

For a copy-mode sandbox (every sandbox on a Mac) review previews what sandbox pull would bring back, the same review and the changed files, and applies nothing; a stopped sandbox is started to read its work and stopped again. -o json then prints the pull's result, as pull -o json does, and sandbox pull NAME --patch-out FILE writes the diff.

defenseclaw sandbox review myapp-7f3a
defenseclaw sandbox review myapp-7f3a --diff
FlagTakesDefaultDescription
--diffswitchPrint the unified diff too.
-o, --outputtexttextOutput format: text or json.

sandbox undo

Restores a mounted project to its undo point (the snapshot a session started from). It shows a preview first, then asks. The sandbox is stopped before the restore, and --restart starts it again afterwards. Branches and tags the session moved are put back too, unless you pass --keep-refs. The session's own state is kept in a commit, and the command undo prints (git -C FOLDER checkout COMMIT -- .) brings its files back. Files git ignores have no copy in the undo point: undo deletes what the session wrote to Python bytecode caches and names the rest, unless openshell.workdir.undo_ignored has the undo point keep a copy of dependency directories such as node_modules and .venv, which undo then restores as the session found them.

For a copy-mode sandbox, which changes your folder only through pull --apply, undo reverts the last pull --apply instead, after the same preview and question. Edits you made since the apply stay; when they overlap the apply, undo changes nothing and names the files. It neither stops nor starts the sandbox, and --restart and --keep-refs don't apply. A branch or patch file the work went to is yours to delete.

defenseclaw sandbox undo myapp-7f3a --preview
defenseclaw sandbox undo myapp-7f3a --yes --restart
FlagTakesDefaultDescription
--keep-refsswitchLeave branches and tags as the session left them.
-o, --outputtexttextOutput format: text or json.
--previewswitchOnly show what undo would change.
--restartswitchStart the sandbox again afterwards.
-y, --yesswitchDo not ask after the preview.

sandbox pull

Brings a copy-mode sandbox's work back, starting the sandbox first if it is stopped (unless its copy has not changed since its last pull, see below). Without a mode it shows the review: the changed files, the ones that can run code on your machine, and files held back from the sandbox. Choose one mode to bring the work back: --apply (a 3-way merge into your working tree), --branch (branch dc/<name>) or --branch-name NAME, or --patch-out FILE. A folder that is not a git repository has no branch mode. When --apply cannot merge in place (a conflict, or git older than 2.38), it leaves your working tree alone and exits 4: the changes go to <name>.patch in your folder and, in a git project, to branch dc/<name>. Changes that can run code on your machine need your confirmation, or --accept-sensitive without a terminal.

Once an --apply (or the apply at a session's end) has put the work in your folder, the next pull, a session's end included, shows only what changed in the sandbox since (2 files changed (+3 −0) since the last apply), flags and asks about only that, and its apply, patch or 3-way merge starts from that point, so a change you took back from your folder since stays taken back. With nothing new it says so and asks nothing, and sandbox delete of the stopped sandbox does not warn about work it holds. sandbox undo of the apply starts the next pull from the uploaded copy again, so the undone work is offered anew. A branch or a patch file does not move that point: your folder does not have those changes. A later pull of the same state still counts as brought back, so delete does not warn about it.

--branch, --branch-name and --patch-out FILE are checked before the sandbox is started or read: a branch that exists and does not hold the last pull's work, a patch file that exists (without --force) or whose folder does not, and a branch for a folder without git are refused at once. A branch that already holds the work is done: nothing to do: branch dc/<name> already has these changes, with nothing asked ("up_to_date": true with -o json). A branch that holds the last pull's work is refused at once too when the stopped sandbox has run since that pull, since only starting it tells whether its work changed (branch dc/<name> already exists: it holds <name>'s pull at 14:03, and <name> has run since, so its work may have changed): pass --branch-name NAME, or --force to move the branch to the new work. A stopped sandbox whose copy has not changed since its last pull read it (the pull, the session's end or the stop that stopped it saw its copy in that state, and it has not run since) is not started: that pull is made again, its changes and review measured anew (fix-tests's copy has not changed since its last pull at 14:03; using that pull instead of starting it). On a Mac this saves booting the MicroVM.

defenseclaw sandbox pull fix-tests
defenseclaw sandbox pull fix-tests --branch
defenseclaw sandbox pull fix-tests --patch-out fix-tests.patch
FlagTakesDefaultDescription
--accept-sensitiveswitchBring back changes that can run code on this machine.
--applyswitchMerge the changes into your working tree (3-way).
--branchswitchPut the changes on branch dc/<name>.
--branch-nametextPut the changes on this branch.
--forceswitchOverride blocking review gates, an existing branch or patch file.
-o, --outputtexttextOutput format: text or json.
--patch-outtextWrite the changes to this patch file.

Policy and packs

sandbox policy

The effective sandbox policy combines the pack, your openshell keys, the run flags, and the organization's openshell.admin limits. See How the effective policy is computed.

defenseclaw sandbox policy show
SubcommandPurpose
sandbox policy allowAdd hosts to openshell.egress.allow (used by the balanced and strict profiles).
sandbox policy blockAdd hosts to openshell.egress.block.
sandbox policy explainShow every resolved setting and where it comes from (pack, config, flag, organization).
sandbox policy showShow the effective sandbox policy.
sandbox policy suggestSuggest an egress allowlist from the destinations sandboxes reached.

sandbox policy show

Shows the effective policy of one sandbox (--sandbox), or the policy a run in this folder with the given flags would get. It lists the pack and its digest, the profile, network and approvals modes, the organization policy, the main settings, and any setting a limit refused.

defenseclaw sandbox policy show
defenseclaw sandbox policy show --harness codex --profile balanced --copy
FlagTakesDefaultDescription
--copyswitchResolve for a copy-mode run.
--harnesstextResolve for this harness.
-o, --outputtexttextOutput format: text or json.
--packtextResolve with this pack.
--profiletextResolve with this profile.
--safeswitchResolve for a --safe run.
--sandboxtextThe policy of this sandbox.
--unmasktext, repeatableResolve with these --unmask globs.

sandbox policy explain

Shows every resolved setting with its value, the layer that set it (pack, user, flag, admin, or default), and its precise origin, such as openshell.admin.min_profile. A clamped setting also shows the value that was asked for.

defenseclaw sandbox policy explain
defenseclaw sandbox policy explain --sandbox myapp-7f3a --output json
FlagTakesDefaultDescription
--copyswitchResolve for a copy-mode run.
--harnesstextResolve for this harness.
-o, --outputtexttextOutput format: text or json.
--packtextResolve with this pack.
--profiletextResolve with this profile.
--safeswitchResolve for a --safe run.
--sandboxtextThe policy of this sandbox.
--unmasktext, repeatableResolve with these --unmask globs.

sandbox policy suggest

Prints an openshell.egress.allow list, as YAML, of the destinations your sandboxes reached in the daemon's activity buffer, with a count for each and the blocked ones listed apart. Use it before you move to the default-deny balanced profile.

defenseclaw sandbox policy suggest
defenseclaw sandbox policy suggest --sandbox myapp-7f3a --output json
FlagTakesDefaultDescription
-o, --outputtexttextOutput format: text or json.
--sandboxtextOnly this sandbox's destinations.

sandbox policy allow

Adds host patterns to openshell.egress.allow in config.yaml, and refuses patterns that cover every host or a whole top-level domain. Allow entries matter under the balanced profile, which reaches only allowed hosts. Under strict, a direct connection to an allowed host is approved without asking. The daemon applies the change to running sandboxes within a few seconds. Under your organization's openshell.admin limits the command refuses an entry, names the limit, and changes nothing: every entry when allow_unblock is false (your own allow entries would be ignored), and a host on egress_block (or a subdomain of a host name there) or outside egress_allow_only.

defenseclaw sandbox policy allow artifacts.example.com "*.docs.example.com"

This command has no flags.

sandbox policy block

Adds host patterns to openshell.egress.block in config.yaml. A host on the block list can't be unblocked; remove the entry to reach it again.

defenseclaw sandbox policy block files.example.net

This command has no flags.

Both commands check each pattern and refuse a change that would make config.yaml invalid. In a managed_enterprise install they are refused, because the administrator owns config.yaml.

sandbox pack

Policy packs set the whole sandbox posture. See Sandbox policy packs.

defenseclaw sandbox pack list
SubcommandPurpose
sandbox pack listList the built-in and custom packs with their sha256 digests.
sandbox pack showPrint a pack and its sha256 digest (for openshell.admin.required_pack_digest).
sandbox pack validateValidate a pack file strictly.

sandbox pack list

Lists the built-in packs and the custom packs under openshell.pack_dir, with their profile and sha256 digest. A custom pack that fails to load shows why.

defenseclaw sandbox pack list
FlagTakesDefaultDescription
-o, --outputtexttextOutput format: text or json.

sandbox pack show

Prints a pack, normalized, with its source and digest. The digest is the one openshell.admin.required_pack_digest pins. It takes the same references as openshell.pack: a built-in name, a custom pack name, or an absolute path.

defenseclaw sandbox pack show balanced
defenseclaw sandbox pack show ~/.defenseclaw/policies/sandbox/team-web
FlagTakesDefaultDescription
-o, --outputtexttextOutput format: text or json.

sandbox pack validate

Loads a custom pack file strictly, with the same checks a run applies, and prints its name, profile, and digest. It takes an absolute path (or one starting with ~/) to a pack.yaml or its directory.

defenseclaw sandbox pack validate ~/.defenseclaw/policies/sandbox/team-web/pack.yaml

This command has no flags.

Legacy cleanup

sandbox legacy-cleanup

Linux only, and the one sandbox command the Python CLI runs itself. It removes a retired openshell-sandbox 0.0.x standalone install and restores the host's OpenClaw networking, ownership, and config. It prints every step with its exact commands and asks once, unless you pass --yes. Progress is recorded in <data_dir>/legacy-sandbox-cleanup.json, so running it again only does what is left. Detect a legacy install says when you need it.

defenseclaw sandbox legacy-cleanup --dry-run
defenseclaw sandbox legacy-cleanup
FlagDescription
--dry-runPrint the cleanup plan and exact commands; change nothing.
-y, --yesApply the plan without asking for confirmation.
--remove-userAlso delete the sandbox user and its home (userdel -r). Refused while it has processes, or until its ownership and ACLs are gone from the OpenClaw home.
--remove-binaryAlso remove /usr/local/bin/openshell-sandbox when it is a legacy 0.0.x build that no package owns.