Skip to content

Sandboxing — Concepts

Sandboxing exists so an agent can run code you don’t fully trust — without that code harming your machine, reading your secrets, or leaving you in a broken state. This page explains the model. For step-by-step configuration, see Run Code in Sandbox.

Every setting on the Sandbox page serves one of five goals:

  1. Run untrusted / agent-generated code — the agent writes and runs a script you didn’t review line by line. The worst case is a discarded container, not a wiped home directory.
  2. Process untrusted external inputs — a downloaded CSV, a scraped page, a user-supplied file. A malicious payload can’t escape the container.
  3. Reproducible per-language environments — “run in Python 3.12 / Node 20” gives the same result on any host, independent of what’s installed locally.
  4. Resource-capped, timeboxed execution — a runaway loop or fork bomb is killed by hard CPU/memory/time limits (kernel cgroups), not left to melt the host.
  5. Network control — turn outbound network off entirely (Strict), so downloaded or generated code can’t call home.

The UI is organised around that intent — three profiles — not around fifteen individual fields.

Snippbot has two distinct isolation mechanisms. They are not merged; the profile you pick composes them.

OS-level fenceContainer engine
WhatA permission cage around a process running on the hostThe command runs inside a container with its own filesystem, namespaces, and cgroups
HowmacOS Seatbelt (sandbox-exec) / Linux bwrap / firejailDocker / Podman / a process fallback
StrengthWeaker boundary; shares the host kernel & toolchainStrong isolation, reproducible, disposable
Cost~0 ms, no runtime neededA running runtime + ~200 ms cold start
GovernsThe agent’s own commands — loop checks, MCP serversUntrusted code Snippbot runs — run_code, workflow tool steps (installed tools: follow-up)

The Sandbox settings page configures the container engine. The OS fence is configured separately (it governs loops and MCP), and the page’s “Where commands run” panel always tells you which system applies to each context.

Pick a profile; it sets the underlying fields for you. Advanced settings can fine-tune any profile (which then shows as Custom).

ProfileBehaviourUse when
OffAll commands run on the host.Fully trusted local dev.
Safe (default)Host by default; risky commands auto-containerize based on a risk score. Arbitrary code (run_code) is always containerized.Everyday agent work.
StrictEvery command Snippbot runs — run_code, bash from API-model and claude-native agents (with the default contain gate), workflow tool steps — is containerized, with no network.Untrusted code, shared or production machines.

The profile is derived, not stored: it’s read back by comparing four fields — mode, scope, workspace_access, and whether the network is enabled — to the profile bundles. Change any of those four and you get Custom; tuning resources, GPU, or mounts keeps the profile label.

In Safe mode each shell command is scored 0–1 by an operation detector (does it delete files, modify the system, reach the network, run recursively?). Commands scoring at or above the risk threshold (default 0.5) run in a container; everything else runs on the host. Two guarantees:

  • Arbitrary code is always contained. A regex can’t reliably judge a Python or JavaScript snippet, so run_code is containerized in both Safe and Strict.
  • Fail-safe. If the scorer is unavailable, the command is treated as high-risk and containerized — Safe never silently drops to the host because of a scoring error.

The file model: how code touches your files

Section titled “The file model: how code touches your files”

This is the most misunderstood area.

  • Bind mounts, not copies. Your workspace is exposed to the container as a bind mount at /workspace. It’s one set of files on disk; a write to /workspace/x is a write to the host path — no sync step.
  • workspace_access is the mount choice:
    • none — the project is not mounted (clean room).
    • ro — bind-mounted read-only (“analyze my code/data”).
    • rw — bind-mounted read-write (“edit my project / emit files”).
  • System files are never mounted. The container sees the image’s OS plus only what’s bind-mounted — your project, any custom mounts, and a tmpfs. ~/.ssh, other projects, /etc, cloud credentials are simply absent from the container’s namespace.

Where the agent writes, and how files come back

Section titled “Where the agent writes, and how files come back”

When the workspace is read-only or unmounted, the agent still needs somewhere to write. Its working directory is a writable scratch tmpfs (/work), separate from the read-only project at /workspace. Files the agent creates there are copied back to the conversation’s files and render inline in chat — your project is never modified unless you choose rw.

Detection avoids a full-workspace diff: the execute path drops a marker before the command and copies out files newer than it (or explicit declared outputs), returning their host paths on the result’s artifacts.

  • A linked project → the project directory.
  • Chat with no project → a per-conversation scratch directory (the same store chat uses for agent files) — never your home directory.
  • workspace_access = none → nothing is mounted.

Where commands run (execution-context matrix)

Section titled “Where commands run (execution-context matrix)”

The container profile governs the paths Snippbot’s own tool executor runs. Other contexts have their own rules — the Sandbox page shows this live:

ContextGoverned byBehaviour
Agent-generated code (run_code)Container engineAlways containerized in Safe & Strict — the core “untrusted code” case.
Agent bash — API-model agentsContainer engineRouted through Snippbot’s executor; honours the profile (Off → host, Safe → risk-based, Strict → container).
Agent bash — claude-native agentsPer claude_native_bash_gatecontain (default) → same as API-model bash (container per profile); gate → allowed/denied on the host per profile; off → host (legacy).
Installed marketplace tools (custom_python)Container profileBlocked under any non-Off profile (untrusted code); runs on the host only when sandboxing is Off. Containerized execution is a follow-up.
Workflow tool stepsContainer engineHonours the profile (a step may opt out with sandbox_enabled=False).
Loop verifiable commandOS fenceA lightweight pass/fail check — separate from the container profile.
MCP tool serversOS fenceThird-party tool processes run under the fence.
Project modeHost (no sandbox)Intentionally host-only — project work needs to write to your workspace and run at host speed, so the profile does not apply.
CapabilitymacOSLinuxWindows
Container engineDocker/Podman Desktop (Linux VM)native docker/podmanDocker/Podman via WSL2 only
OS fencesandbox-execbwrap / firejailUnsupported (loops/MCP fencing fail closed)
process fallbackSeatbelt-backedbwrap/firejail-backed”basic” — minimal isolation
GPU passthroughNot supportedNVIDIA + toolkitvia WSL2

The Sandbox page shows a page-level banner for the current runtime state:

  • Live — Docker/Podman is up; commands containerize per the profile.
  • Safe/Strict but no runtime — a surfaced degradation: commands run unsandboxed (or are blocked, if you set fail-closed), and the banner says so. This is never silent — the engine emits a sandbox.degraded event.
  • Off + no runtime — expected; commands run on the host by design.