Agents

Run Claude Code in a sandbox

Claude Code is a coding agent that edits files, runs shell commands, and executes whatever it decides your task needs. That is exactly the kind of program you want inside a hardware-isolated VM rather than on your own machine or CI runner: give it a disposable sandbox, let it work with full autonomy, and read back the result. The sandbox is the blast radius.

What you need
An orkestr API token with the sandbox scopes (quickstart) and an Anthropic API key for Claude Code itself. The whole flow below runs on the default restricted egress allowlist - npm, GitHub, and api.anthropic.com are already on it.

1. Create a sandbox

Use the node-22 base and restricted networking. Pass the Anthropic key as a sandbox env var - it lives in the VM's memory only and is never persisted.

create
from orkestr import Sandbox

# node-22 ships Node and npm. restricted egress already covers
# registry.npmjs.org, github.com and api.anthropic.com.
sbx = Sandbox.create(
    template="node-22",
    network="restricted",
    timeout_seconds=3600,
    env={"ANTHROPIC_API_KEY": "sk-ant-..."},
)

2. Install Claude Code

A plain npm install lands in the writable workspace and takes a few seconds. Skip this step entirely by baking the install into a custom template (step 6).

install
# A local install lands in the writable workspace - about 4 seconds.
sbx.exec("npm install @anthropic-ai/claude-code", timeout_seconds=120)
print(sbx.exec("npx claude --version").stdout)   # 2.x.x (Claude Code)

3. Get a repo in

clone
# github.com is on the default restricted allowlist.
sbx.exec("git clone --depth 1 https://github.com/you/your-repo.git repo")

# Private repo? Put a fine-grained token in the URL - it stays inside
# the sandbox and dies with it:
# sbx.exec(f"git clone https://x-access-token:{token}@github.com/you/private.git repo")

4. Run it headless

-p runs Claude Code non-interactively: one prompt in, the agent works (edits files, runs commands, iterates), one JSON result out. Inside a disposable sandbox, --dangerously-skip-permissions is the point rather than a risk - there is nothing of yours here to protect, so the agent never blocks on approval prompts.

run
import json

result = sbx.exec(
    'npx claude -p "Run the test suite and fix the first failing test" '
    "--output-format json --dangerously-skip-permissions",
    cwd="/workspace/repo",
    timeout_seconds=1800,
)
run = json.loads(result.stdout)
print(run["result"])          # Claude Code's final answer
print(run["total_cost_usd"])  # what the run cost
session_id = run["session_id"]  # keep it - resumes the conversation later

5. Multi-turn sessions

Two layers of state compose here. Claude Code's own conversation resumes with --resume <session_id>, and the sandbox around it can be paused between turns so you stop paying for compute while the user thinks - resume brings back the repo, the installed packages, and the agent's session files exactly as they were.

sessions
# Same Claude Code session, next instruction:
sbx.exec(
    f'npx claude -p "Now update the changelog" --resume {session_id} '
    "--output-format json --dangerously-skip-permissions",
    cwd="/workspace/repo",
    timeout_seconds=1800,
)

# Park the whole environment between turns - repo, node_modules,
# Claude Code session state, everything:
sandbox_id = sbx.pause()
# ... later, possibly from another process:
sbx = Sandbox.resume(sandbox_id)

6. Make boots instant with a template

If you launch Claude Code sandboxes often, bake the install into a custom template once. Template recipes run as root in a build VM with a writable root filesystem, so a global install works there and every sandbox booted from the template has claude ready in ~300 ms.

terminal
curl -X POST https://api.orkestr.eu/v1/templates \
  -H "Authorization: Bearer $ORKESTR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "claude-code",
    "base_template": "node-22",
    "recipe": ["npm install -g @anthropic-ai/claude-code"],
    "network": "restricted"
  }'

# Once ready, boot from it - claude is preinstalled, ~300ms to a working agent:
#   Sandbox.create(template="tmpl_...", network="restricted", env={...})

Production notes

  • Read files back, not just stdout. The JSON result carries the agent's answer; the actual work is in the filesystem. Use sbx.files.read or git diff to collect it, or push a branch from inside the sandbox.
  • Budget with timeouts. Give the sandbox a lifetime that matches the task and each exec its own ceiling - agent runs hang more often than scripts do.
  • Scope both credentials. A sandbox-scoped orkestr token can't touch the rest of your account, and the Anthropic key you inject should be one you can revoke without pain.
  • Fan out for parallel work. Each sandbox is fully isolated - run one Claude Code per candidate fix and keep the branch that passes the tests. See the cookbook.
Reading with an agent? This page is also plain markdown at /docs/sandboxes/claude-code.md, and the full docs index lives at /docs/llms.txt.