NodeOps
KR

Run Claude Managed Agents on CreateOS Sandbox

With Claude Managed Agents, Anthropic runs the agent loop: the model, the context, and the choice of which tool to call next. Anthropic does not run the tool calls themselves. You supply the machine that does.

CreateOS Sandbox is that machine. It boots in about a second, holds state for as long as you keep it alive, and lets you control its network egress from outside. Anthropic keeps the orchestration. You keep the execution boundary: the filesystem, the network, and the logs.

Two ways to wire it up

CreateOS supports both wirings below. They differ in where the agent's shell runs and which credentials you put inside the sandbox, so read the table before you pick.

Self-hosted environmentSandbox as a tool
Who runs the agent's basha worker inside your sandboxyour orchestrator, in a sandbox it owns
Anthropic environment typeself_hostedcloud
Agent's toolsthe built-in agent toolset (bash, file edit, …)one custom tool you define
Credentials on your hostorg key + environment keyorg key only
Who reaches out to whomyour worker polls Anthropic's work queueAnthropic streams you a tool call; you answer it
Best forfull agentic coding, where the agent needs a real filesystem across a sessiona narrow, auditable execution surface: the agent runs this, and nothing else

Both keep the agent's execution inside CreateOS. They differ in how much of the agent's world lives there.

Self-hosted environment

The agent's own toolset runs in your sandbox. Anthropic hands work to a queue. A worker process inside the sandbox claims it, runs the tool call, and posts the result back. The agent gets a real machine: a shell, a network, and a filesystem it can build up over a session. None of that state leaves the sandbox.

Claude Managed Agent (Anthropic)   ──▶  work queue  ◀── worker polls
                                                          │
                                          ┌───────────────┴───────────────┐
                                          │  CreateOS sandbox             │
                                          │  ant beta:worker poll         │
                                          │  → the agent's bash runs here │
                                          └───────────────────────────────┘

Anthropic's self-hosted sandboxes guide describes this topology.

Sandbox as a tool

Anthropic hosts the agent loop (a cloud environment), and CreateOS becomes a discrete tool the agent calls. The agent emits a run_command tool call. Your orchestrator runs that command in a sandbox it owns and hands the output back.

Claude Managed Agent (Anthropic)
        │  agent.custom_tool_use  { command: "…" }
        ▼
your orchestrator  (holds the CreateOS API key — the agent never sees it)
        │  sandbox.runCommand("bash", ["-lc", command])
        ▼
CreateOS sandbox   ──── output ────▶  user.custom_tool_result

Give the agent only the custom tool and omit the built-in agent toolset. CreateOS then becomes its single execution path, with no second shell to fall back on. The control-plane key stays on your host, and every command the agent runs passes through code you wrote, so you can log it, rewrite it, or refuse it.

Prerequisites

  • A CreateOS Sandbox API key and control-plane URL.
  • An Anthropic organization API key with the Managed Agents beta enabled (Console → API keys).
  • For a self-hosted environment only: an environment and an environment key (Console → Environments → Generate environment key). The environment key authenticates the worker to its queue. It is a separate credential from the org key and uses a different auth scheme. A cloud environment needs neither, since nothing of yours polls the queue.

Set up a self-hosted environment

On the Anthropic side. Create a self_hosted environment and generate an environment key for it.

TypeScript
1const environment = await anthropic.beta.environments.create({
2 name: "createos-workers",
3 config: { type: "self_hosted" },
4});

On the CreateOS side. Boot a sandbox with the environment credentials in its environment, install the ant CLI, and start the worker as a daemon. It polls for work until you tear the sandbox down.

TypeScript
1const sandbox = await Sandbox.create({
2 shape: "s-4vcpu-4gb",
3 rootfs: "devbox:1",
4 envs: {
5 ANTHROPIC_BASE_URL: "https://api.anthropic.com",
6 ANTHROPIC_ENVIRONMENT_ID: environment.id,
7 ANTHROPIC_ENVIRONMENT_KEY: environmentKey, // never the org key
8 },
9});
10
11await sandbox.sh(`curl -fsSL "$ANT_RELEASE_URL" | tar -xz -C /usr/local/bin ant`);
12await sandbox.sh(
13 "nohup setsid ant beta:worker poll --workdir /workspace > /tmp/worker.log 2>&1 &",
14);

Every session you point at that environment now executes inside the sandbox.

TypeScript
1const agent = await anthropic.beta.agents.create({
2 name: "createos-worker",
3 model: "claude-haiku-4-5",
4 tools: [{ type: "agent_toolset_20260401" }],
5});
6const session = await anthropic.beta.sessions.create({
7 agent: agent.id,
8 environment_id: environment.id,
9});

Only the environment key sits inside the sandbox. The org key stays on your host, so a prompt injection that compromises the agent still cannot spend your organization's credits or read your other sessions.

Set up sandbox-as-a-tool

Create a cloud environment and an agent whose only tool is yours.

TypeScript
1const environment = await anthropic.beta.environments.create({
2 name: "createos-tool",
3 config: { type: "cloud", networking: { type: "unrestricted" } },
4});
5
6const agent = await anthropic.beta.agents.create({
7 name: "createos-tool-agent",
8 model: "claude-haiku-4-5",
9 system:
10 "Your only way to run anything is the run_command tool, which runs a shell " +
11 "command on a remote Linux machine. Report its output verbatim.",
12 // No agent_toolset: the agent has no bash of its own, so every command it
13 // wants to run must go through CreateOS.
14 tools: [
15 {
16 type: "custom",
17 name: "run_command",
18 description: "Run a shell command on the remote Linux machine.",
19 input_schema: {
20 type: "object",
21 properties: { command: { type: "string" } },
22 required: ["command"],
23 },
24 },
25 ],
26});

Then run the loop. Open the event stream before you send the task, since any event emitted in between is lost, and answer every tool call.

TypeScript
1const session = await anthropic.beta.sessions.create({
2 agent: agent.id,
3 environment_id: environment.id,
4});
5
6const stream = await anthropic.beta.sessions.events.stream(session.id);
7await anthropic.beta.sessions.events.send(session.id, {
8 events: [{ type: "user.message", content: [{ type: "text", text: task }] }],
9});
10
11for await (const event of stream) {
12 if (event.type === "agent.custom_tool_use") {
13 const output = await runInSandbox(String(event.input.command));
14 await anthropic.beta.sessions.events.send(session.id, {
15 events: [
16 {
17 type: "user.custom_tool_result",
18 custom_tool_use_id: event.id,
19 content: [{ type: "text", text: output }],
20 },
21 ],
22 });
23 } else if (event.type === "session.status_idle") {
24 // `requires_action` means the agent is idle *waiting on your tool result* —
25 // the run is not over. Only a real stop reason ends it.
26 if (event.stop_reason?.type === "requires_action") continue;
27 break;
28 }
29}

Sandbox lifecycle: per session, or per call

runInSandbox is where you choose what the agent's world looks like.

One sandbox per session. Create it before the session starts, reuse it for every tool call, then destroy it at the end. State persists: a file the agent writes in one call is there in the next, so the agent can build something up over a conversation.

A fresh sandbox per call. Create, run, destroy, every time. Nothing carries between calls, so one command cannot contaminate the next, and any command's blast radius is a sandbox you have already thrown away. You pay a cold start per call, which on CreateOS measures 0.7–1.0 s end to end (create, run, destroy).

Pick per-call for untrusted or one-shot work, per-session for anything that needs to accumulate.

Why CreateOS

  • Egress you control from outside the sandbox. setEgress locks a sandbox to an allowlist, and the host enforces those rules in-kernel, so code inside the sandbox cannot bypass them. The agent can reach your private service and still have no route to the public internet.
  • Hardware isolation. Each sandbox runs its own kernel rather than sharing one across containers, which puts a hardware boundary between a language model's output and your infrastructure.
  • ~1 s cold start. A fresh sandbox per tool call costs 0.7–1.0 s, which keeps per-call disposal practical.
  • State when you want it. Pause and resume a sandbox, snapshot it, or reattach a disk, so a session survives an idle user without burning compute.
  • Your keys stay yours. In both topologies the CreateOS API key lives on your host. In the sandbox-as-a-tool topology every command the agent runs passes through your code first.

Gotchas

An empty tool result is rejected. Managed Agents rejects a tool result with no content. Send something, even (no output).

Report command failures, do not throw them. Sandbox.sh throws on a non-zero exit, which breaks the run when you call it inside a tool body. The agent needs to see a failing command as a result it can reason about. Use runCommand and hand the exit code back with the output.

TypeScript
1const { result } = await sandbox.runCommand("bash", ["-lc", command]);
2const output = `${result.stdout}${result.stderr}`.trim() || "(no output)";
3return result.exit_code === 0 ? output : `exit ${result.exit_code}\n${output}`;

Hold the stream open. In the sandbox-as-a-tool topology the agent blocks on your tool result. If your orchestrator drops the stream mid-call, the session strands. A single-shot script works as written. A long-lived service needs reconnect handling.

session.status_idle does not mean the run is over. It fires with stop_reason.type: "requires_action" while the agent waits on you. Break only on a real stop reason.

Anthropic does not stage files for you. A self-hosted sandbox starts empty, with no repo and no session files. Pass what the agent needs through session.metadata and have your worker fetch it.

Working examples

All five run end to end against a live control plane.

ExampleTopologyWhat it shows
36: self-hosted agent workerself-hostedone persistent sandbox running the worker
37: sandbox per sessionself-hosteda fresh sandbox and worker per session
49: egress-locked workerself-hostedthe agent reaches a private service but cannot exfiltrate
50: sandbox as a tool, per sessionsandbox-as-a-toolone sandbox for the session; state survives between tool calls
51: sandbox as a tool, per callsandbox-as-a-toola disposable sandbox per call; the same task, and the state is gone

Examples 50 and 51 run the same two-step task, writing a file and then reading it back in a separate tool call, so you can watch the two lifecycles diverge.

100,000명 이상의 빌더. 하나의 워크스페이스.

제품 업데이트, 빌더 스토리, 더 빠르게 출시할 수 있는 기능에 대한 얼리 액세스를 받아보세요.

NodeOps is the agentic operating system for production AI. CreateOS is its flagship product.