# How to Sandbox an AI Agent: Files, Network and Secrets

> How to sandbox an AI agent from zero: what a sandbox contains and what it misses, from files and network to credentials, in Claude Code, Docker and K8s.

URL: https://www.matthewswong.com/en/learn/ai-agents-mcp/run-an-ai-agent-in-a-sandbox/

AI agents and MCP · Lesson 4 of 4 · Reviewed on 17 October 2026 · Checked against MCP specification 2026-07-28 · By Matthews Wong (https://www.matthewswong.com/en/about/)

> A sandbox is a boundary the operating system or a virtual machine enforces around an AI agent, so it holds whatever the model decides. Check three doors: which files it can write and read, which hosts it can reach, and which credentials are inside. Then check what runs outside it, because Claude Code's sandbox wraps shell commands only.

## You will be able to

- Describe a sandbox by its three doors, files, network and credentials, and know the defaults of Claude Code's.
- Name what runs outside a sandbox, and choose a wider boundary when that matters.
- Prove a sandbox works with two probes instead of trusting a setting.

## The three doors of an agent sandbox

A sandboxed shell command may write only to the project, can read most files until you deny them, reaches the network only through an allowlist proxy, while the agent's MCP servers run outside the boundary.

1. **Meet the boundary.** Claude Code's sandbox wraps the shell commands an agent runs and the processes they start. The agent itself, its file tools and local MCP servers sit outside the line.
2. **Writes stay inside.** By default a sandboxed command may write to the project folder, a per-user temp directory and folders you add. A write anywhere else, such as your home directory, fails.

   ```
   touch ~/sandbox-probe → Operation not permitted
   ```

   *Predict:* Can that same sandboxed command read ~/.ssh by default? (No, reads are confined too / Yes, reads are open until you deny them)
3. **Reads are wide open.** Reads are not confined by default: a sandboxed command can read most of the machine, including ~/.ssh and cloud credential files, and it inherits your environment variables.

   ```
   cat ~/.ssh/known_hosts → works
   ```
4. **Deny the secrets.** List credential files and environment variables in sandbox.credentials. Denied files cannot be read inside the sandbox, and denied variables are unset before each command runs.

   ```
   credentials · files: ~/.ssh mode deny · envVars: GITHUB_TOKEN mode deny
   ```
5. **No route out.** Network traffic must pass a proxy that checks each host against your allowed domains, which start empty. A command that tries to skip the proxy cannot resolve anything.

   ```
   curl --noproxy '*' https://example.com → Could not resolve host
   ```

   *Predict:* The agent's local MCP server needs the network. Does this sandbox limit it? (Yes, it shares the sandbox / No, it runs outside it)
6. **What stays outside.** File tools, hooks and local MCP servers run with your full access, outside the shell sandbox. To fence those in, run the whole agent in a container, VM or microVM.

   ```
   sandbox runtime · dev container · VM or microVM
   ```

## What does a sandbox contain, and what does it not?

A sandbox is a boundary enforced outside the model, by the operating system or a virtual machine, around what an agent can touch. It matters because it holds whatever the model decides: the Claude Code docs say sandbox restrictions still apply even if a prompt injection bypasses Claude's decision-making. Describe any sandbox by three doors: files, network and credentials.

Claude Code's built-in sandbox, off by default and turned on with /sandbox, shows how specific those doors are. Writes go to the working directory, a per-user temp directory and folders you add. Reads cover most of the machine, including ~/.ssh and ~/.aws/credentials. Network traffic goes through a local proxy whose allowed domains start empty. Environment variables, secrets included, are inherited.

So the default stops a command writing outside the project or phoning home, but not reading your keys. The docs say there is no built-in credential deny list: only the files and variables you list under sandbox.credentials are protected, by blocking reads of those files and unsetting those variables before each sandboxed command.

## What runs outside the sandbox?

Claude Code's sandbox wraps shell commands and the processes they start, nothing else. Its file tools, such as Read, Edit and Write, follow permission rules instead; hooks, local MCP servers, plugin monitors and helpers such as the status line run with your full access. A denyRead entry does not stop the Read tool.

Some shell commands leave it too. A command you type at the ! prompt usually runs unsandboxed, commands that match excludedCommands run outside it, and Claude can ask to retry a failed command without the sandbox. Setting allowUnsandboxedCommands to false removes that retry, and failIfUnavailable makes Claude Code exit rather than run commands unsandboxed when the sandbox cannot start.

Other sandboxes draw the same line in other places. Docker's security page says local MCP servers run on the host, outside sandbox isolation, and should be treated as trusted host integrations. Whichever sandbox you use, ask what sits outside it, because that is where an injected instruction will try to go.

## When do you need a bigger boundary?

When the agent runs unattended or touches untrusted code, put the whole agent inside the boundary, not just its shell. The Claude Code docs say the built-in sandbox alone is not sufficient for fully unattended runs, list the sandbox runtime, a dev container, a custom container and a virtual machine as wider options, and recommend a dedicated VM for an untrusted repository.

Docker Sandboxes run each agent in a microVM with its own kernel and its own Docker engine, deny outbound TCP by default, and inject API keys from a host-side proxy so the raw values never enter the VM. What they do not protect is the workspace you share: a direct mount is read-write, so the agent edits your working tree in place, git hooks and CI config included.

On Kubernetes, the agent-sandbox project manages one stateful pod per sandbox, but its threat model says it does not implement isolation itself. It relies on a runtime such as gVisor or Kata Containers chosen through a RuntimeClass, a default-deny NetworkPolicy, and service account tokens that are not mounted by default when a SandboxTemplate is used.

Whatever the boundary, an allowed host is a capability grant, not just a destination. Anthropic describes a case where hidden instructions in a workspace file made Claude upload files through an allowed API host into an attacker's account. For each host you allow, ask what a hostile process could do with its own account there.

## The real config behind this lesson

`app/components/blogs/AnthropicContainClaudeSandboxApprovalFatigue.tsx:81-93`

```json
  "sandbox": {
    "enabled": true,
    "credentials": {
      "files": [
        { "path": "~/.aws/credentials", "mode": "deny" },
        { "path": "~/.ssh", "mode": "deny" }
      ],
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" },
        { "name": "NPM_TOKEN", "mode": "deny" }
      ]
    }
  }
```

This repository does not turn the sandbox on in its own .claude/settings.json. This block is from the site's article on Anthropic's containment write-up, and it matches the example in the Claude Code docs. It closes the read door the default leaves open: the two files are denied for reads inside the sandbox, and the two variables are unset before each sandboxed command.

`app/components/blogs/AnthropicContainClaudeSandboxApprovalFatigue.tsx:100-105`

```bash
touch ~/sandbox-probe
# Sandboxed: "Operation not permitted" on macOS, "Read-only file system" on Linux/WSL2.
# If it succeeds and home is not a writable sandbox path, the sandbox is off: delete the file.

curl --noproxy '*' https://example.com
# Sandboxed: "Could not resolve host", because there is no route around the proxy.
```

The proof step from the same article, which follows the docs' own check: one probe for the write door and one for the network door. Ask Claude to run both lines. Commands you type yourself at the ! prompt usually run outside the sandbox, so typing them proves nothing.

## What breaks

- **Assuming reads are confined.** The default sandbox lets commands read ~/.ssh and cloud credential files, and passes your environment variables through. List them under sandbox.credentials, or a hijacked command can still read them.
- **Forgetting the file tools.** sandbox.credentials and denyRead apply to shell commands only. The Read tool follows permission rules, so add a matching deny rule such as Read(~/.ssh/**) to cover it.
- **A sandbox that is silently not there.** If the sandbox cannot start, for example because bubblewrap or socat is missing on Linux, Claude Code runs commands unsandboxed by default. Set failIfUnavailable to true, then run the two probes.
- **An allowlist entry wider than it looks.** Allowing a broad domain such as github.com lets a command reach anything an account there can do, and the proxy does not inspect TLS by default. Add hosts one at a time, and ask what a hostile account could do on each.

### Where the boundary sits, and what stays outside it

| Approach | What is inside | What stays outside |
| --- | --- | --- |
| Claude Code sandboxed Bash | Shell commands and the processes they start | File tools, hooks and local MCP servers |
| Sandbox runtime around Claude Code | The whole Claude Code process, MCP servers and hooks included | Whatever you allow it to write or reach |
| Docker Sandboxes (microVM) | The agent with its own kernel and Docker engine | Your shared workspace and local MCP servers |
| Kubernetes agent-sandbox | A pod under gVisor or Kata, if you configure it | Isolation itself: the project relies on the runtime |

## Try it yourself

You need Claude Code on macOS, Linux or WSL2, and a scratch folder. You will turn the sandbox on, prove it, then watch the read door before and after you close it. Nothing here touches your real keys.

1. Make a fake secret file and a fake token, then start Claude Code in a scratch folder.

   ```bash
   echo not-a-real-key > ~/fake-creds.txt
   export FAKE_TOKEN=not-a-real-token
   mkdir -p ~/sandbox-practice && cd ~/sandbox-practice && claude
   ```
2. Run /sandbox and choose a mode. Then ask Claude to run the two probes. Both should fail; decline if Claude asks to retry them outside the sandbox.

   ```bash
   /sandbox
   touch ~/sandbox-probe
   curl --noproxy '*' https://example.com
   ```
3. Ask Claude to run these two commands. Both work, because reads and environment variables are open by default.

   ```bash
   cat ~/fake-creds.txt
   echo $FAKE_TOKEN
   ```
4. Close the read door. Add these keys to .claude/settings.local.json, where /sandbox saved your mode, then restart Claude Code.

   ```bash
   {
     "sandbox": {
       "enabled": true,
       "credentials": {
         "files": [{ "path": "~/fake-creds.txt", "mode": "deny" }],
         "envVars": [{ "name": "FAKE_TOKEN", "mode": "deny" }]
       }
     },
     "permissions": {
       "deny": ["Read(~/fake-creds.txt)"]
     }
   }
   ```
5. Ask for the same two commands again: the file read fails and the variable prints empty. Then ask Claude to open the file with its Read tool; the permission deny rule, not the sandbox, is what refuses that.

   ```bash
   cat ~/fake-creds.txt
   echo $FAKE_TOKEN
   ```
6. Clean up the practice files when you are done.

   ```bash
   rm -f ~/fake-creds.txt ~/sandbox-probe
   unset FAKE_TOKEN
   ```

## Quick quiz

1. **With Claude Code's sandbox on and no extra settings, what can a sandboxed command do?**
   A. Write to your home directory
   B. Read ~/.ssh
   C. Connect to any host directly
   Answer: B. Writes are limited to the project, temp and added folders, and network goes through a proxy whose allowlist starts empty. Reads cover most of the machine, keys included, until you deny them.

2. **Which of these runs outside Claude Code's sandbox?**
   A. npm test started by Claude
   B. A child process of a sandboxed command
   C. A local MCP server
   Answer: C. The sandbox wraps shell commands and their children. Local MCP servers, hooks and file tools run with your full access.

3. **You typed touch ~/sandbox-probe at the ! prompt and it worked. What did you learn?**
   A. The sandbox is off
   B. Nothing: commands you type at ! usually run outside the sandbox
   C. Your home directory is an allowed write path
   Answer: B. The docs say to ask Claude to run the probes, because shell-mode commands you type yourself usually run unsandboxed and prove nothing.

## Think it through

- **Why does a sandbox protect you from prompt injection when a permission prompt might not?** A prompt depends on a person reading each request carefully. The sandbox is enforced by the operating system on the running process, so it holds even when the model has been talked into running something.
- **Your agent needs to read issues from one internal API host. What do you add, and what do you ask first?** Add that one host to allowedDomains and nothing broader. First ask what a hostile process could do with an account on that host, because an allowed host is a set of capabilities, not just a destination.
- **When is Claude Code's built-in shell sandbox not enough on its own?** When the agent runs fully unattended, or when MCP servers, hooks or file tools you do not fully trust are in play. Then the whole agent belongs inside a container, a VM or the sandbox runtime.

## Go deeper

- [AI Coding Agent Sandbox: Fixing Approval Fatigue in CI](https://www.matthewswong.com/en/blog/anthropic-how-we-contain-claude-sandbox-approval-fatigue/)
- [Sandboxing AI Agents and the Lethal Trifecta](https://www.matthewswong.com/en/blog/sandboxing-ai-agents-lethal-trifecta/)
- [Claude Code Sandboxing: Run Bash Safely](https://www.matthewswong.com/en/blog/claude-code-sandboxing-bash-guide/)
- [Docker Sandboxes sbx: Run Claude Code in Cloud MicroVMs](https://www.matthewswong.com/en/blog/docker-sandboxes-cloud-sandboxes-sbx-guide/)

## Sources

- [Claude Code docs: configure the sandboxed Bash tool](https://code.claude.com/docs/en/sandboxing)
- [Claude Code docs: choose a sandbox environment](https://code.claude.com/docs/en/sandbox-environments)
- [Docker docs: Docker Sandboxes security model](https://docs.docker.com/ai/sandboxes/security/)
- [kubernetes-sigs/agent-sandbox: threat model](https://github.com/kubernetes-sigs/agent-sandbox/blob/main/docs/security/threat_model.md)
- [Anthropic Engineering: how we contain Claude](https://www.anthropic.com/engineering/how-we-contain-claude)
- [MCP specification 2026-07-28: local MCP server compromise and sandboxing](https://modelcontextprotocol.io/specification/2026-07-28/basic/security_best_practices)

---

- HTML version: https://www.matthewswong.com/en/learn/ai-agents-mcp/run-an-ai-agent-in-a-sandbox/
- Bahasa Indonesia: https://www.matthewswong.com/id/learn/ai-agents-mcp/run-an-ai-agent-in-a-sandbox/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
