Claude Skill

sandbox

Fence an autonomous or long-running agent loop: the built-in sandbox with network off, or a container with the worktree mounted. Use before any unattended loop, before `execute` autonomy on an unfamiliar repo, and whenever a task pulls untrusted input.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download jakeselby-agent-harness-primitives_skills_sandbox-0c8664f.zip · 2 KB
Part of jakeselby/agent-harness — 14 skills

Install

skills CLI npx skills add https://github.com/JakeSelby/agent-harness/tree/main/primitives/skills/sandbox
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install jakeselby-agent-harness@llmmart
Git git clone https://github.com/JakeSelby/agent-harness.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole jakeselby/agent-harness collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Fence the loop

A permission mode decides whether a call runs. A sandbox decides what a command can reach once it is running, and the OS enforces that on every child process. An unattended loop needs the second kind: nobody is at the prompt to answer for the first.

Runtime scope

The configuration and container example below are Claude Code-specific. Do not copy those settings into Codex. For Codex, use its native sandbox and approval controls as documented in the configuration reference. A read-only sandbox constrains filesystem writes; approval policy is a separate control. Native hooks are not a replacement for OS confinement. Custom Codex role defaults can be superseded by the parent turn's permissions; see docs/runtime-controls.md before delegating work that requires a hard boundary. Client qualification remains in the compatibility catalog.

The Claude Code sandbox

It "runs on macOS, Linux, and WSL2. Native Windows is not supported"; Linux and WSL2 need bubblewrap and socat installed first. Put this in ~/.claude/settings.json to cover every project; the /sandbox panel writes enabled to .claude/settings.local.json for one project.

{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false,
    "network": { "allowedDomains": [], "strictAllowlist": true },
    "filesystem": { "denyRead": ["~/.ssh", "~/.aws", "~/.config/gh"] }
  }
}

strictAllowlist over an empty allowedDomains is network off: Claude Code then "denies sandboxed commands access to any host outside the allowlist instead of prompting". Only user, managed and --settings settings set it; a repository's own file cannot. The deny entries are load-bearing — the default read policy covers the whole disk, and "this default still allows reading credential files such as ~/.aws/credentials and ~/.ssh/." Add sandbox.credentials.envVars entries with "mode": "deny" to unset tokens for sandboxed commands too. failIfUnavailable makes a missing dependency a hard stop rather than a silent unsandboxed fallback, and allowUnsandboxedCommands: false removes the retry-outside escape hatch. Subagents inherit the session's sandbox; commands you type at the ! prompt do not.

For one session, writing no file: claude --settings '{"sandbox":{"enabled":true}}'. Confirm with /sandbox: the Config tab shows the resolved settings, and a Dependencies tab appearing means a package is missing. A prompt titled "Bash command (unsandboxed)" is the signal a command left the boundary. Keys and defaults: https://code.claude.com/docs/en/sandboxing

A container

Harder boundary, coarser tooling. Mount the worktree and nothing else, stay non-root, and let the container be the isolation — do not nest the built-in sandbox inside it.

docker run --rm -it --network none \
  --user "$(id -u):$(id -g)" \
  -v "$PWD:/work" -w /work \
  -v "$HOME/.claude:/config:ro" -e CLAUDE_CONFIG_DIR=/config -e ANTHROPIC_API_KEY \
  <image-with-the-cli> claude --dangerously-skip-permissions -p "<the loop prompt>"

podman substitutes unchanged. What breaks, in order: --network none cuts the model API too, so as written this runs only against a local model — for a loop that must reach the API, allow that one host and nothing else and the shape holds. Then web search and fetch, every MCP server reached over the network, and every package install; a read-only config mount blocks session state and credential writes, so auth arrives by environment variable. Losing all of it is the point when the task parses input you did not write: a path the loop lacks cannot be talked into opening.

Which one

Built-in sandbox for daily work: a settings change, every tool still works, the OS still enforces the boundary. Container for a loop that runs while you sleep, a repo whose build scripts you have not read, or anything handling untrusted content. Neither isolates branches — run inside a worktree as well, per the worktree-per-agent skill.

Files (agent-harness)
  • SKILL.md 4.3 KB
    ---
    name: sandbox
    description: Fence an autonomous or long-running agent loop: the built-in sandbox with network off, or a container with the worktree mounted. Use before any unattended loop, before `execute` autonomy on an unfamiliar repo, and whenever a task pulls untrusted input.
    ---
    
    # Fence the loop
    
    A permission mode decides whether a call runs. A sandbox decides what a command can reach once it
    is running, and the OS enforces that on every child process. An unattended loop needs the second
    kind: nobody is at the prompt to answer for the first.
    
    ## Runtime scope
    
    The configuration and container example below are Claude Code-specific. Do not copy those
    settings into Codex. For Codex, use its native sandbox and approval controls as documented in
    [the configuration reference](https://learn.chatgpt.com/docs/config-file/config-reference).
    A read-only sandbox constrains filesystem writes; approval policy is a separate control.
    Native hooks are not a replacement for OS confinement. Custom Codex role defaults can be
    superseded by the parent turn's permissions; see `docs/runtime-controls.md` before delegating
    work that requires a hard boundary. Client qualification remains in the compatibility catalog.
    
    ## The Claude Code sandbox
    
    It "runs on macOS, Linux, and WSL2. Native Windows is not supported"; Linux and WSL2 need
    `bubblewrap` and `socat` installed first. Put this in `~/.claude/settings.json` to cover every
    project; the `/sandbox` panel writes `enabled` to `.claude/settings.local.json` for one project.
    
    ```json
    {
      "sandbox": {
        "enabled": true,
        "failIfUnavailable": true,
        "allowUnsandboxedCommands": false,
        "network": { "allowedDomains": [], "strictAllowlist": true },
        "filesystem": { "denyRead": ["~/.ssh", "~/.aws", "~/.config/gh"] }
      }
    }
    ```
    
    `strictAllowlist` over an empty `allowedDomains` is network off: Claude Code then "denies sandboxed
    commands access to any host outside the allowlist instead of prompting". Only user, managed and
    `--settings` settings set it; a repository's own file cannot. The deny entries are load-bearing —
    the default read policy covers the whole disk, and "this default still allows reading credential
    files such as `~/.aws/credentials` and `~/.ssh/`." Add `sandbox.credentials.envVars` entries with
    `"mode": "deny"` to unset tokens for sandboxed commands too. `failIfUnavailable` makes a missing
    dependency a hard stop rather than a silent unsandboxed fallback, and `allowUnsandboxedCommands:
    false` removes the retry-outside escape hatch. Subagents inherit the session's sandbox; commands
    you type at the `!` prompt do not.
    
    For one session, writing no file: `claude --settings '{"sandbox":{"enabled":true}}'`. Confirm with
    `/sandbox`: the **Config** tab shows the resolved settings, and a **Dependencies** tab appearing
    means a package is missing. A prompt titled "Bash command (unsandboxed)" is the signal a command
    left the boundary. Keys and defaults: https://code.claude.com/docs/en/sandboxing
    
    ## A container
    
    Harder boundary, coarser tooling. Mount the worktree and nothing else, stay non-root, and let the
    container be the isolation — do not nest the built-in sandbox inside it.
    
    ```bash
    docker run --rm -it --network none \
      --user "$(id -u):$(id -g)" \
      -v "$PWD:/work" -w /work \
      -v "$HOME/.claude:/config:ro" -e CLAUDE_CONFIG_DIR=/config -e ANTHROPIC_API_KEY \
      <image-with-the-cli> claude --dangerously-skip-permissions -p "<the loop prompt>"
    ```
    
    `podman` substitutes unchanged. What breaks, in order: `--network none` cuts the model API too, so
    as written this runs only against a local model — for a loop that must reach the API, allow that
    one host and nothing else and the shape holds. Then web search and fetch, every MCP server reached
    over the network, and every package install; a read-only config mount blocks session state and
    credential writes, so auth arrives by environment variable. Losing all of it is the point when the
    task parses input you did not write: a path the loop lacks cannot be talked into opening.
    
    ## Which one
    
    Built-in sandbox for daily work: a settings change, every tool still works, the OS still enforces
    the boundary. Container for a loop that runs while you sleep, a repo whose build scripts you have
    not read, or anything handling untrusted content. Neither isolates branches — run inside a
    worktree as well, per the `worktree-per-agent` skill.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related