Sandboxes on Linux

Set up NVIDIA OpenShell sandboxes on Linux with Docker Engine, Landlock and the openshell-gateway systemd user service, check the machine with sandbox doctor, and run your first agent on a live project folder.

On Linux, each sandbox is a container that OpenShell's Docker driver runs. Your project folder is mounted into it live, so you see the agent's edits in your editor as they happen, and a snapshot taken at the start of each session lets defenseclaw sandbox undo put the folder back.

How it fits together

edits
sends everything
hooks :18971 · web :18972
model calls, real key added
creates sandboxes
allowed requests
UntrustedSandbox container (your uid)
TrustedYour machine (your user)
ExternalInternet
Claude Code or Codexskip-permissions on
Your project folderlive at /work/myapp
OpenShell supervisorthe only way out
DefenseClaw daemonchecks hooks, proxies the web
OpenShell gatewaysystemd user service
The internetmodel provider, allowed sites

List view for small screens. Use the expand button to open the drawing.

UntrustedSandbox container (your uid)

  1. Claude Code or Codexskip-permissions on
    • editsYour project folder
    • sends everythingOpenShell supervisor
  2. OpenShell supervisorthe only way out
    • hooks :18971 · web :18972DefenseClaw daemonYour machine (your user)
    • model calls, real key addedThe internetInternet
  3. Your project folderlive at /work/myapp

TrustedYour machine (your user)

  1. DefenseClaw daemonchecks hooks, proxies the web
    • creates sandboxesOpenShell gateway
    • allowed requestsThe internetInternet
  2. OpenShell gatewaysystemd user service

ExternalInternet

  1. The internetmodel provider, allowed sites
A sandbox on Linux. The container has no network of its own: OpenShell's supervisor passes hooks and web traffic to DefenseClaw and model calls to the provider. DefenseClaw's main API (port 18970) is never reachable from a sandbox.

What is mounted, and how:

  • Your folder appears at /work/<folder>, read-write. Secret files such as .env, keys and certificates appear empty inside (--unmask PATH shares one).
  • Git internals .git/hooks, .git/config and .git/config.worktree are read-only, so the agent cannot plant a hook or setting that runs the next time you use git. A new .git the agent creates inside the folder is renamed out of the way while the sandbox runs.
  • Nothing else from your machine is in the sandbox.

For an untrusted repository, defenseclaw sandbox run claude --copy works on a copy instead, as every run on a Mac does. See work on a copy.

Before you start

You needDetails
DefenseClawInstalled and initialized (see the quickstart), with the daemon running (defenseclaw-gateway start). Run everything as your own user, not root.
Linuxamd64 or arm64. WSL2 is not supported.
KernelLandlock ABI 3 or newer (kernel 6.2 or newer), enabled in the kernel's lsm= list. OpenShell refuses to start a sandbox without it.
DockerA rootful Docker Engine 28 or newer that your user can use (a member of the docker group), with the buildx plugin (docker-buildx-plugin from Docker's repository). Rootless Docker is not supported: it keeps containers off the host network, so sandboxes cannot reach DefenseClaw.
systemdA user session with a systemd user manager. The OpenShell gateway runs as the openshell-gateway user service.
DiskAt least 5 GiB free for Docker, 10 GiB recommended. The first image build downloads about 3 GB.

You don't need OpenShell installed first: setup offers to install it.

Check the kernel yourself if you are not sure:

uname -r
cat /sys/kernel/security/lsm

The second command should list landlock.

So that the OpenShell gateway keeps running after you log out, turn on linger for your user. The doctor warns when it is off:

sudo loginctl enable-linger $USER

Set up

Run setup

defenseclaw sandbox setup

Setup checks the machine, then asks before each change. A first run looks like this (the install plan and build lines are shortened):

DefenseClaw sandbox setup
  Checking this machine…  ✓ linux/arm64  ✓ Landlock  ✓ Docker 29.4.0  ✗ OpenShell not installed
Install OpenShell 0.1.1 with NVIDIA's installer? (sudo; sha256 verified) [y/N] y
OpenShell install plan
  Release     v0.1.1 (NVIDIA OpenShell upstream installer)
  SHA-256     … (verified)
  Privileges  the script uses sudo to install the openshell package, then enables and
              starts the openshell-gateway user service (systemd --user)
Run this plan? [y/N] y
  ✓ OpenShell 0.1.1 installed, gateway running
Allow sandboxes to mount the project folder you launch from? (enables bind mounts on your local OpenShell gateway; DefenseClaw only ever mounts the launch folder) [Y/n]
Disable OpenShell's anonymous usage telemetry? (edits ~/.config/openshell/gateway.env and restarts the OpenShell gateway; no sandbox runs on it now) [Y/n]
  OpenShell gateway configuration changes
    edit ~/.config/openshell/gateway.toml (a timestamped backup is kept)
  …
  ✓ gateway configured and restarted
  Harnesses (add another with `defenseclaw sandbox setup --harness NAME`):
    Claude Code (claude)  model credential ANTHROPIC_API_KEY ✓
    Codex (codex)         model credential ~/.codex/auth.json ✓
  ✓ openshell.enabled is on in ~/.defenseclaw/config.yaml
Make `claude` and `codex` run sandboxed automatically? (shell wrapper; undo any time) [y/N]
Build the Claude Code image now? (the first build downloads about 3 GB; otherwise the first `defenseclaw sandbox run claude` builds it) [Y/n]
  Building the Claude Code image (the first build downloads about 3 GB)…
  ✓ Claude Code 2.1.156: defenseclaw/sandbox:claudecode-…-u1000, hooks verified (built in 6m10s)
  …
  ✓ the daemon runs the sandbox subsystem (ingress 127.0.0.1:18971, egress proxy 127.0.0.1:18972)

  ✓ Done →  cd <project> && defenseclaw sandbox run claude

Understand what it changed

  • OpenShell 0.1.1 is installed with NVIDIA's installer, whose script is checked against a pinned SHA-256 before it runs. It uses sudo for the package, then enables and starts the openshell-gateway user service. DefenseClaw drives OpenShell 0.1.1 and later 0.1 releases, not 0.2.
  • The gateway listens on localhost:17670 with mTLS, so only you can call it. Setup turns on project-folder mounts in ~/.config/openshell/gateway.toml, and turns OpenShell's anonymous usage telemetry off in gateway.env, each with a timestamped backup, then restarts the gateway once. Say no to mounts, or pass --no-mounts, and every run works on a copy.
  • DefenseClaw's config gets openshell.enabled: true and the harnesses you set up in openshell.harnesses.
  • The harness images are built in Docker, and each is checked to prove that its hooks fire and block before it is used.

Setup is safe to run again. When sandboxes are running, it asks before it restarts the shared gateway. defenseclaw sandbox teardown undoes it (see tear down).

Check the machine

defenseclaw sandbox doctor
  ✓ Platform                   linux/arm64
  ✓ User                       alice (uid 1000)
  ✓ Landlock                   ABI 6
  ✓ Docker                     Docker 29.4.0 (Ubuntu 24.04 LTS)
  ✓ Docker BuildKit            docker build uses BuildKit (buildx v0.30.1)
  ✓ Docker host networking     Docker Engine shares the host network
  - Docker file sharing        bind mounts come straight from the host filesystem
  ✓ Disk space                 40.0 GiB free under /var/lib/docker
  ✓ systemd linger             enabled for alice
  ✓ Gateway service            openshell-gateway active (running)
  ✓ OpenShell CLI              0.1.1 at /usr/bin/openshell
  ✓ SSH connection sharing     off for the OpenShell sessions DefenseClaw runs (ssh -o ControlMaster=no -o ControlPath=none -o ControlPersist=no); your ssh configuration shares none for host sandbox either
  ✓ Gateway registration       openshell at https://127.0.0.1:17670 (mtls)
  ✓ Gateway mTLS files         private key is owner-only
  ✓ Gateway                    0.1.1 healthy at https://127.0.0.1:17670
  ✓ Gateway compute driver     docker
  ✓ Global policy              none; sandbox policies apply
  ✓ Project bind mounts        enabled for the docker driver
  ✓ OpenShell telemetry        OpenShell usage telemetry is off
  ✓ Sandbox ingress port       127.0.0.1:18971 is served by the DefenseClaw daemon
  ✓ Sandbox egress port        127.0.0.1:18972 is served by the DefenseClaw daemon
  ✓ DefenseClaw daemon         connected to OpenShell 0.1.1 gateway openshell; ingress 127.0.0.1:18971, egress proxy 127.0.0.1:18972
  ✓ Sandbox hooks              no sandbox is running
  ✓ Harness images             hook-verified: claudecode 2.1.156, codex 0.146.0
  ✓ Shell wrappers             none (`defenseclaw sandbox enable claude` makes `claude` run sandboxed)
  ✓ Organization policy        no openshell.admin constraints

  ✓ ready for sandboxes

✓ passed, ⚠ is a warning, ✗ failed and - was skipped, with the reason. Every warning and failure names its fix on the next line (→ …). defenseclaw sandbox doctor --fix applies the fixes that need only your user, such as starting the gateway or turning bind mounts on, after asking. The command exits 1 when a check fails.

Run your first agent

cd ~/code/myapp
defenseclaw sandbox run claude

The banner shows what the sandbox can see before the harness takes over your terminal:

Sandbox myapp-7f3a · Claude Code · skip-permissions ON · network: open + blocklist
  Project   ~/code/myapp → /work/myapp (live)   snapshot taken → `defenseclaw sandbox undo myapp-7f3a` restores it
  Hidden    .env  certs/dev.pem  (secret files appear empty inside; --unmask PATH to share)
  Protected .git/hooks .git/config .git/config.worktree (read-only)
            Not visible: everything else on this machine
  Model     ANTHROPIC_API_KEY → api.anthropic.com only (the sandbox sees a placeholder)

Continue with your first session.

You can run the same setup from the TUI (defenseclaw tui, press 0 for Setup, then choose Sandboxes (OpenShell)). See TUI and macOS app.

Keep or undo

When a session on a mounted folder ends, DefenseClaw summarizes it, flags the changed files that can run code on your machine, and asks:

Keep changes? [Y] keep  [u] undo everything  [d] show diff (then Enter)

Keeping makes the changes the base of the next session. Undo, now or later, restores the snapshot:

defenseclaw sandbox undo myapp-7f3a

In a git project undo puts back the working tree, HEAD, the branch, the staging area and the git files the agent could write. It first saves every tip the session created or moved, so an undo can be undone too. Files git ignores, such as node_modules/ and .venv/, are not in the snapshot: undo names each one it cannot restore. See undo the session.

Only one live mount per folder

undo and the review cover the whole folder, so only one sandbox at a time can mount a folder live. To run agents in parallel on the same project, give each its own copy:

defenseclaw sandbox run claude --copy --name fix-tests
defenseclaw sandbox run codex --copy --name docs

Common problems

You seeDo this
The Landlock check failsBoot a kernel 6.2 or newer with landlock in its lsm= list.
The Docker check says rootless Docker, or that Docker is older than 28Use a rootful Docker Engine 28 or newer and add yourself to the docker group (sudo usermod -aG docker $USER, then log in again). With linger on, logging out does not restart your systemd user manager, so the OpenShell gateway service still lacks the group: see the next row.
The Gateway service check says it runs under a user manager that started before you joined the docker group, or setup's gateway restart fails with the restarted gateway is not healthy and names sudo systemctl restart user@<uid>.serviceYour systemd user manager keeps the groups it started with, and linger keeps it running when you log out. Restart it with sudo systemctl restart user@$(id -u).service (this stops your user services, which then start again), then run setup again.
The Docker BuildKit check failsInstall the docker-buildx-plugin package from Docker's repository.
systemd linger warnssudo loginctl enable-linger $USER, so the gateway keeps running after you log out.
Gateway service fails with openshell-gateway is inactivedefenseclaw sandbox doctor --fix starts it.
Gateway service says openshell-gateway is not installed, yet a gateway answersOpenShell was installed another way than NVIDIA's installer. Setup uses that gateway as it runs, but DefenseClaw cannot restart it: after a gateway change, restart it yourself, the way you started it. The fix line names how to switch to the openshell-gateway user service.
the DefenseClaw daemon is not runningdefenseclaw-gateway start. With the daemon down, sandbox hooks block every tool call.
the DefenseClaw daemon started before you joined the docker group, or a run fails with permission denied on /var/run/docker.sockThe daemon keeps the groups it started with. Restart it so it picks up the docker group: defenseclaw-gateway restart.
refusing to share /home/alice with a sandbox: it is your home directoryRun from a project folder inside your home, not from home itself.

The troubleshooting table in the complete guide covers the rest.