Skip to content
C.W.K.
Stream
Lesson 02 of 05 · published

Sandboxing: cwd, env, and Network

~14 min · sandbox, cwd, environment, network

Level 0Observer
0 XP0/64 lessons0/13 achievements
0/150 XP to next level150 XP to go0% complete

The agent only knows what you let it see

Three knobs scope the agent's blast radius. cwd sets the working directory — Read/Write/Edit/Bash all run from there. env adds environment variables on top of the ones the CLI inherits from your process. Network access can be gated at the OS layer (firewall, VPN) or via your own MCP servers' policies.

Set cwd to the smallest viable directory

If the agent only needs to edit files under /srv/myapp, do not set cwd to /. The model will see paths relative to cwd, and the SDK will refuse paths that escape it (with default settings). Narrow cwd is half the sandbox.

Strip env at the source

Bash inherits the environment the CLI was given, and the CLI is given your worker's own environment with options.env merged on top. options.env can add and override; it cannot remove. If the worker runs with your full shell environment, the agent has access to every credential variable, whatever you pass in env. So start the worker with a minimal environment (env -i, a service unit, a container) and add only what tools genuinely need. Leak protection is mostly env protection.

Principle: Sandboxing is not 'install Docker'. It is choosing what cwd, env, and tool list the agent gets. Each is a minimum viable scope decision.

Code

Tight sandbox options·python
# options.env is merged OVER the environment this worker process already has:
# it adds and overrides, it never removes. So start the worker itself minimal,
#   env -i PATH=/usr/local/bin:/usr/bin:/bin HOME=/srv/agent-home python worker.py
# (or a service unit / container with an explicit environment), then add only
# what this task needs here.
from claude_agent_sdk import ClaudeAgentOptions

options = ClaudeAgentOptions(
    cwd="/srv/agent-workspace",  # NOT /
    env={
        "LANG": "en_US.UTF-8",
        # ONLY the keys this task needs, fetched here rather than inherited:
        "WEATHER_API_KEY": load_secret("weather-api-key"),
    },
    tools=["Read", "Bash"],
    permission_mode="default",
)
Containerized sandbox for untrusted runs·yaml
# docker-compose.yml — run the agent inside a container with a read-only mount
services:
  agent:
    image: claude-agent:latest
    read_only: true
    tmpfs:
      - /tmp
      - /var/run
    volumes:
      - ./workspace:/workspace
      - ./input:/input:ro
    networks:
      - egress-allowlisted
    environment:
      - PATH=/usr/local/bin:/usr/bin:/bin
      # Inject only task-specific secrets
networks:
  egress-allowlisted:
    driver: bridge
    internal: false
    # outbound rules enforced by external firewall / Calico / similar

External links

Exercise

For one Agent SDK process in your project, write down the cwd, env keys, and tool list. Justify each in one phrase. Anything you cannot justify gets removed.
Hint
If your worker inherits everything in os.environ, the agent has every secret you do, and options.env cannot take any of it away. Filter the worker's own environment by allow-list, never by deny-list.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.