Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build LLM-powered apps with the Anthropic Claude API or SDK across Python, TypeScript, Java, Go, Ruby, C#, and PHP.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
shared/managed-agents-self-hosted-sandboxes.md
1# Managed Agents — Self-Hosted Sandboxes23With `config.type: "self_hosted"`, the **agent loop stays on Anthropic's orchestration layer** but **tool execution moves to infrastructure you control** — bash, file ops, and code run inside your container, so filesystem contents and network egress never leave your environment. Contrast with `config.type: "cloud"`, where Anthropic runs the container. Connectivity is **outbound-only**: your worker long-polls Anthropic's work queue; Anthropic never dials into your network.45## Flow67```81. Create environment: config: {type: "self_hosted"} → env_...92. Generate environment key (Console, on the environment page) → sk-ant-oat01-... as ANTHROPIC_ENVIRONMENT_KEY103. Run a worker: EnvironmentWorker.run() or ant beta:worker poll114. Sessions reference environment_id=env_... exactly as for cloud12```1314## Create the environment1516```python17client = anthropic.Anthropic()1819environment = client.beta.environments.create(20name="self-hosted", config={"type": "self_hosted"}21)22```2324`{"type": "self_hosted"}` is the entire config — there are no pool, capacity, or networking sub-fields; you control those on your side.2526## Run a worker — SDK (primary path)2728`EnvironmentWorker` wraps the poll → dispatch → tool-execute loop. `.run()` is the always-on loop; `.run_one()` / `.runOne()` handles one work item (for webhook-driven wake).2930**Python — always-on:**3132```python33import asyncio34import os35from anthropic import AsyncAnthropic36from anthropic.lib.environments import EnvironmentWorker373839async def main() -> None:40environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]41environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]42async with AsyncAnthropic(auth_token=environment_key) as client:43await EnvironmentWorker(44client,45environment_id=environment_id,46environment_key=environment_key,47workdir="/workspace",48).run()495051asyncio.run(main())52```5354**TypeScript — always-on:**5556```typescript57import Anthropic from "@anthropic-ai/sdk";58import { EnvironmentWorker } from "@anthropic-ai/sdk/helpers/beta/environments";5960const environmentKey = process.env.ANTHROPIC_ENVIRONMENT_KEY!;61const environmentId = process.env.ANTHROPIC_ENVIRONMENT_ID!;62const client = new Anthropic({ authToken: environmentKey });63const ctrl = new AbortController();64process.once("SIGTERM", () => ctrl.abort());6566await new EnvironmentWorker({67client,68environmentId,69environmentKey,70workdir: "/workspace",71signal: ctrl.signal72}).run();73```7475**Customizing tools.** `EnvironmentWorker` runs the built-in toolset by default. To add or replace tools, use `AgentToolContext(workdir=, client=, session_id=)` with `beta_agent_toolset(env)` / `betaAgentToolset(env)` and pass the resulting tools to the lower-level `tool_runner()`. Skills attached to the agent are downloaded into `{workdir}/skills/<name>/` before tool calls begin (`AgentToolContext` handles this when given `client` and `session_id`). Downloaded skill files are marked executable automatically by the CLI and SDK; if you implement skills download yourself, you set permissions.7677> **Runtime deps:** the SDK helpers require `/bin/bash` at that exact path. The TypeScript SDK additionally requires `unzip`, `tar`, and Node.js 22+. These are resolved at fixed paths and do **not** respect `PATH` overrides.7879## Run a worker — `ant` CLI (fixed tools)8081The `ant` CLI ships a worker with the fixed built-in toolset (`bash`, `read`, `write`, `edit`, `glob`, `grep`). Install per `shared/anthropic-cli.md`, then:8283```sh84export ANTHROPIC_ENVIRONMENT_KEY=sk-ant-oat01-...85ant beta:worker poll --environment-id env_... --workdir /workspace86```8788- `--workdir` is the directory tools operate in (default `.`); tool calls are sandboxed to it.89- `--environment-key` overrides the env var.90- `--on-work <script>` runs your script per work item (e.g. to spin a fresh container per session — see Container orchestration below).91- `--unrestricted-paths`, `--max-idle` (default `60s`), `--log-format` — see `ant beta:worker poll --help`.92- Flags fall back to env vars (`ANTHROPIC_ENVIRONMENT_ID`, `ANTHROPIC_ENVIRONMENT_KEY`).93- Exits cleanly on SIGTERM/SIGINT after draining in-flight work.94- **Fixed toolset** — for custom tools, use the SDK worker above.9596Inside an `--on-work` container, run `ant beta:worker run --workdir <dir>` as the entrypoint.9798## Webhook-driven wake (instead of always-on)99100Register a webhook for `session.status_run_started` (see `shared/managed-agents-webhooks.md`), verify the delivery, then drain one work item with `.run_one()`:101102```python103import os104import anthropic105from anthropic.lib.environments import EnvironmentWorker106107environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]108environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]109client = anthropic.AsyncAnthropic(110auth_token=environment_key,111) # reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env for webhooks.unwrap()112113114async def handle(raw: bytes, headers: dict[str, str]) -> dict:115event = client.beta.webhooks.unwrap(raw.decode(), headers=headers)116if event.data.type != "session.status_run_started":117return {"status": "ignored"}118await EnvironmentWorker(119client,120environment_id=environment_id,121environment_key=environment_key,122workdir="/workspace",123).run_one()124return {"status": "ok"}125```126127TypeScript: same shape with `client.beta.webhooks.unwrap(body, {headers})` and `new EnvironmentWorker({...}).runOne()`.128129## Container orchestration (mid-level)130131`EnvironmentWorker.run()` polls and executes tools in the same process. To run each session in its **own** container, use the mid-level poller in a thin orchestrator — Python `client.beta.environments.work.poller(environment_id=, environment_key=, drain=, block_ms=, reclaim_older_than_ms=, auto_stop=)`; TypeScript `new WorkPoller({client, environmentId, environmentKey, autoStop})` from `@anthropic-ai/sdk/helpers/beta/environments` — and, for each yielded `work` item, start a fresh container with these env vars injected, whose entrypoint runs `ant beta:worker run` or an `EnvironmentWorker(...).run_one()`. `block_ms` is 1–999 (or `None` for non-blocking); `reclaim_older_than_ms` re-claims items leased to a dead worker; `drain` stops once the queue is empty; `auto_stop` posts a stop signal after the iterator exits (set `False` when the launched container owns the stop call). **Go's poller has no `auto_stop` opt-out** — it calls `work.Stop` when the handler returns, so block in the handler until the session completes rather than detaching.132133| Env var | Value |134|---|---|135| `ANTHROPIC_SESSION_ID` | `work.data.id` |136| `ANTHROPIC_WORK_ID` | `work.id` |137| `ANTHROPIC_ENVIRONMENT_ID` | `work.environment_id` |138| `ANTHROPIC_ENVIRONMENT_KEY` | pass through |139| `ANTHROPIC_BASE_URL` | pass through |140141Skip items where `work.data.type != "session"`.142143## Monitoring & control144145These are **control-plane** calls — authenticate with `x-api-key` (not the environment key); `managed-agents-2026-04-01` beta header. **Call them from outside the worker host** — setting `ANTHROPIC_API_KEY` on the worker host exposes an organization-scoped credential to agent tool calls.146147| SDK (`client.beta.environments.work.*`) | REST | CLI | Returns |148|---|---|---|---|149| `stats(environment_id)` | `GET /v1/environments/{id}/work/stats` | `ant beta:environments:work stats` | `{type:"work_queue_stats", depth, pending, oldest_queued_at, workers_polling}` |150| `stop(work_id, environment_id=)` | `POST /v1/environments/{id}/work/{work_id}/stop` | `ant beta:environments:work stop` | `work.state` |151152## What changes vs `cloud`153154| Concern | `cloud` | `self_hosted` |155|---|---|---|156| Container lifecycle, hardening, networking | Anthropic | **You** — run non-root, read-only rootfs, drop caps; egress is whatever your VPC/firewall allows |157| `file` / `github_repository` resource mounting | Anthropic mounts into the container | **You** — pass pointers via `sessions.create(metadata={...})` and have your orchestrator fetch/clone before dispatch |158| `memory_store` resources | Supported | **Not yet supported** |159| Vault `environment_variable` credentials | Supported (substituted at Anthropic-managed egress) | **Not yet supported** — egress is yours, so there's nowhere to substitute the secret. Use MCP credentials or a host-side custom tool (`shared/managed-agents-client-patterns.md` Pattern 9) |160| Built-in tools | Via `agent_toolset_20260401` | Supplied by your worker (`EnvironmentWorker` default / `beta_agent_toolset(env)` / `ant` CLI fixed set) |161| Skills download | Automatic | `EnvironmentWorker` / `AgentToolContext` fetch into `{workdir}/skills/` (needs `client` + `session_id`) |162| Claude Platform on AWS | Supported | **Not available** |163| SDK worker helpers | All SDKs | **Python, TypeScript, Go only** (`EnvironmentWorker` / poller not in Java, Ruby, PHP, or C#) — use one of those three or the `ant` CLI |164165## Credentials166167| Credential | Format | Scope |168|---|---|---|169| `ANTHROPIC_ENVIRONMENT_KEY` | `sk-ant-oat01-...` | One environment's work queue. Generate in Console ("Generate environment key"). Pass as `auth_token=` / `authToken` on the client **and** as `environment_key=` / `environmentKey` on `EnvironmentWorker`. Store in a secrets manager; rotate on exposure. |170| `ANTHROPIC_WEBHOOK_SIGNING_KEY` | `whsec_...` | Webhook signature verification (if using webhook-driven wake). The SDK reads this env var automatically for `client.beta.webhooks.unwrap()`. |171172## Security — what you own173174Container hardening; egress restriction (there is no default); `ANTHROPIC_ENVIRONMENT_KEY` custody and rotation; one workspace + environment per trust boundary when running untrusted code; least-privilege for the tool process; log retention and redaction. **Anthropic cannot**: fast-revoke a leaked environment key, verify your image or supply chain, sandbox tool execution inside your container, or enforce retention after tool output reaches your infrastructure. See the Self-Hosted Sandboxes Security page in `shared/live-sources.md` for the full checklist.175