Concept · sandboxes

Sandboxes — bubblewrap or Docker, picked per workload.

Every code-executing tool call runs inside a sandbox scoped to the tenant. Lightweight on Linux, full container on demand.

The default: bubblewrap

Bubblewrap is the same sandboxing primitive Flatpak uses. It's a small setuid-free binary that builds a Linux user-namespace + mount-namespace sandbox using only kernel features. No daemon, no Docker, no root.

Cold start: ~30 ms on a modern x86 box. That's per tool call cheap enough — you can spawn a fresh sandbox for every shell command your agent issues. Each invocation gets a clean tmpfs, a frozen view of the tenant root, and a tight seccomp filter.

approximate bubblewrap invocation
bwrap \
  --unshare-all \
  --share-net=false \
  --die-with-parent \
  --ro-bind /usr /usr \
  --ro-bind /lib /lib \
  --ro-bind /bin /bin \
  --bind /tenants/acme/sandbox /work \
  --chdir /work \
  --tmpfs /tmp \
  --proc /proc \
  --dev /dev \
  --seccomp 11 \
  -- /bin/bash -c "$AGENT_CMD"

The escape hatch: Docker

When the workload is genuinely untrusted — user-submitted scripts in a multi-tenant SaaS, plugins from an unverified ClawHub publisher, code paths your security team flagged — switch to Docker.

Cold start: 200–500 ms (one-time per warm pool) but you get the full container-isolation suite: seccomp default-deny, cgroups for memory and CPU caps, dropped capabilities (no CAP_NET_ADMIN, no CAP_SYS_ADMIN), an unprivileged user inside, and optional gVisor for kernel-level isolation.

~/.openclaw/tenants/acme/config.yaml
sandbox:
  mode: docker            # or "bwrap"
  image: openclaw/sandbox:base
  runtime: runsc          # gVisor (optional, requires runsc on host)
  memory_limit_mb: 512
  cpu_quota: 0.5          # half a vCPU
  network: none
  read_only: true
  drop_caps: [ALL]
  add_caps: []
  seccomp_profile: /etc/openclaw/seccomp-default.json

Configuration: pick per workload

The sandbox mode can be set:

  • Globally in the gateway config (default).
  • Per tenant in the tenant's config overlay.
  • Per agent run by passing --sandbox docker on the CLI or sandbox: "docker" in the JSON-RPC params.
  • Per tool in the tool's metadata.

Most deployments end up using bubblewrap as the default and Docker only for the small set of tools that touch external code.

What the sandbox sees

  • Filesystem: read-only host system, writable scratch tmpfs, writable tenant work directory.
  • Network: off by default; opt-in per tool via an allow-list of hostnames + ports.
  • Process tree: only the sandboxed process; parent gateway PID is hidden.
  • Devices: /dev/null, /dev/urandom; no other devices.
  • Capabilities: none. Even when Docker is the runtime, the container runs with --cap-drop=ALL.
  • Seccomp: default-deny filter; common syscalls allow-listed (read/write/openat/close/exec/fork…).

The web terminal piggy-backs on this

The browser-based web terminal (xterm.js in the UI, node-pty server side) spawns the shell inside the tenant's sandbox. So a user logging into https://your-gateway.example.com/terminal and running rm -rf / deletes nothing outside their own tenant work directory.

EXFOLIATE!

Run your own gateway today.

Apache-2.0, self-hosted, no SaaS layer between you and your users. Install the CLI, create your first tenant, mint a token — you're routing traffic in 60 seconds.