Rightshold™ Sign up

Answers · agent safety

How do I sandbox an AI agent to one folder plus a network allowlist?

Last updated

TL;DR: Use a boundary the operating system enforces, and set both halves: what the agent can read and write on disk, and which hosts it can reach. Either half alone leaves a gap; Claude Code's docs warn that without network isolation a compromised agent could exfiltrate files such as SSH keys, and without filesystem isolation it could backdoor system resources to gain network access. The catch most people miss is reads. Claude Code's Bash sandbox (off by default) limits writes to the working directory, a per-user temp directory and directories you add, but by default commands can read most of the machine, including ~/.ssh and ~/.aws/credentials, until you add denyRead (for example deny ~/ and allow the project) or turn on blockReadsOutsideWorkingDirectories. Codex's workspace-write mode limits writes to the workspace (the current directory plus temp directories), keeps .git, .codex, .agents and .aws inside it read-only, and starts with network off. For a boundary that also covers the agent's own file tools, MCP servers and hooks, run the whole agent in a container that mounts only the project folder and has no network or an egress firewall. Then test both halves from inside the agent.

What should I check, step by step?

1. Pick the folder: a fresh checkout or worktree with only what the task needs, and no .env with production secrets in it.

  1. Writes: Claude Code sandbox writes default to the working directory, per-user temp and added directories; keep additionalDirectories and filesystem.allowWrite empty unless needed. Codex: workspace-write, with sandbox_workspace_write.writable_roots left empty; exclude_slash_tmp and exclude_tmpdir_env_var remove /tmp and $TMPDIR if you want only the folder.
  2. Reads: in Claude Code add sandbox.filesystem.denyRead ["~/"] with allowRead ["."] in the project's .claude/settings.json (or set permissions.blockReadsOutsideWorkingDirectories), and list credential files under sandbox.credentials with mode deny. The built-in Read tool follows permission rules, not denyRead, so add Read deny rules too.
  3. Network: start empty and allow only the hosts the task needs (package registry, git host, your API). In Claude Code set strictAllowlist so unlisted hosts are refused, not prompted; in Codex enable network_access only together with features.network_proxy and domain allow rules, since network on without the proxy is unrestricted.
  4. Close the escape hatches: Claude Code allowUnsandboxedCommands false and failIfUnavailable true; keep excludedCommands short, because an excluded command runs with your full access. In Codex avoid danger-full-access and --yolo outside a container you trust as the boundary.
  5. Cover what the tool's sandbox doesn't: Claude Code's file and web tools, local MCP servers and hooks run outside its sandbox, and Codex's proxy doesn't filter MCP connections, web search or connector calls. Give those their own limits, or put the whole agent in a container.
  6. Container option: mount only the project (docker run -v "$PWD":/work -w /work), add :ro for anything it should only read, use --network none when no network is needed, or an egress firewall such as Anthropic's dev container init-firewall.sh when it is. Don't mount the Docker socket or your home directory.
  7. Test from inside the agent: ask it to run touch ~/sandbox-probe (should fail) and, once reads are restricted, ls ~/.ssh (should fail too), and curl to a host not on the list (Claude Code's docs suggest curl --noproxy '*' https://example.com). Commands you type yourself at a ! prompt usually run outside the sandbox, so they don't test it.

What each layer actually limits

Claude Code Bash sandbox: Bash, PowerShell and Monitor commands and their child processes, enforced with Seatbelt on macOS and bubblewrap on Linux and WSL2. Writes default to the working directory; reads default to most of the machine; network goes through a local proxy whose allowed domains start empty. Inside writable folders it still denies writes to .claude settings, .mcp.json, shell startup files, .gitconfig and .git hooks and config, so a command can't widen its own permissions.

Gaps to plan for

Hostname allowlists decide from the requested name. Claude Code's docs say its proxy doesn't inspect TLS by default, so domain fronting could reach hosts outside the list, and broad entries such as github.com are exfiltration paths. Codex says its DNS check reduces DNS rebinding risk but doesn't eliminate it.

How do I ask Rightshold about this (free, no key)?

Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Write down this agent's boundary for the job (the folder it may touch and the hosts it may reach) as a declared intent, so its sub-agents can only narrow it and anyone checking later can see what it was allowed.”

One-click setup

{
  "tool": "declare_intent",
  "arguments": {
    "agent_id": "<one of your agents>",
    "purpose": "other",
    "description": "Update dependencies in ~/work/billing-refactor and check their licenses; no other folders",
    "operations": [
      "check_license"
    ],
    "ttl_hours": 8,
    "targets": {
      "domains": [
        "registry.npmjs.org",
        "github.com"
      ],
      "resources": [
        "folder:~/work/billing-refactor"
      ]
    }
  }
}

Rightshold records declared intents and checks MCP servers you ask about. It does not run, configure or monitor your agent's sandbox, container or network. Not legal advice.

What are the sources?

Related

Not legal advice. JSON · Markdown · All answers

Get a free API key in one call

No card and no email needed. 100 free credits; the license and policy checks stay free without a key.

curl -s -X POST https://www.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_...   # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
  -H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'