# Managed Agents — Self-Hosted Sandboxes With `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. ## Flow ``` 1. Create environment: config: {type: "self_hosted"} → env_... 2. Generate environment key (Console, on the environment page) → sk-ant-oat01-... as ANTHROPIC_ENVIRONMENT_KEY 3. Run a worker: EnvironmentWorker.run() or ant beta:worker poll 4. Sessions reference environment_id=env_... exactly as for cloud ``` ## Create the environment ```python client = anthropic.Anthropic() environment = client.beta.environments.create( name="self-hosted", config={"type": "self_hosted"} ) ``` `{"type": "self_hosted"}` is the entire config — there are no pool, capacity, or networking sub-fields; you control those on your side. ## Run a worker — SDK (primary path) `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). **Python — always-on:** ```python import asyncio import os from anthropic import AsyncAnthropic from anthropic.lib.environments import EnvironmentWorker async def main() -> None: environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"] environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] async with AsyncAnthropic(auth_token=environment_key) as client: await EnvironmentWorker( client, environment_id=environment_id, environment_key=environment_key, workdir="/workspace", ).run() asyncio.run(main()) ``` **TypeScript — always-on:** ```typescript import Anthropic from "@anthropic-ai/sdk"; import { EnvironmentWorker } from "@anthropic-ai/sdk/helpers/beta/environments"; const environmentKey = process.env.ANTHROPIC_ENVIRONMENT_KEY!; const environmentId = process.env.ANTHROPIC_ENVIRONMENT_ID!; const client = new Anthropic({ authToken: environmentKey }); const ctrl = new AbortController(); process.once("SIGTERM", () => ctrl.abort()); await new EnvironmentWorker({ client, environmentId, environmentKey, workdir: "/workspace", signal: ctrl.signal }).run(); ``` **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//` 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. > **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. ## Run a worker — `ant` CLI (fixed tools) The `ant` CLI ships a worker with the fixed built-in toolset (`bash`, `read`, `write`, `edit`, `glob`, `grep`). Install per `shared/anthropic-cli.md`, then: ```sh export ANTHROPIC_ENVIRONMENT_KEY=sk-ant-oat01-... ant beta:worker poll --environment-id env_... --workdir /workspace ``` - `--workdir` is the directory tools operate in (default `.`); tool calls are sandboxed to it. - `--environment-key` overrides the env var. - `--on-work