mirror of
https://github.com/Piebald-AI/claude-code-system-prompts.git
synced 2026-05-30 05:35:24 +08:00
179 lines
10 KiB
Markdown
179 lines
10 KiB
Markdown
<!--
|
||
name: 'Data: Managed Agents self-hosted sandboxes'
|
||
description: Reference documentation for running Managed Agents tool execution in self-hosted infrastructure, including environment setup, workers, webhook-driven wake, orchestration, monitoring, credentials, and security responsibilities
|
||
ccVersion: 2.1.145
|
||
-->
|
||
# 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/<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.
|
||
|
||
> **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 <script>` runs your script per work item (e.g. to spin a fresh container per session — see Container orchestration below).
|
||
- `--unrestricted-paths`, `--max-idle` (default `60s`), `--log-format` — see `ant beta:worker poll --help`.
|
||
- Flags fall back to env vars (`ANTHROPIC_ENVIRONMENT_ID`, `ANTHROPIC_ENVIRONMENT_KEY`).
|
||
- Exits cleanly on SIGTERM/SIGINT after draining in-flight work.
|
||
- **Fixed toolset** — for custom tools, use the SDK worker above.
|
||
|
||
Inside an `--on-work` container, run `ant beta:worker run --workdir <dir>` as the entrypoint.
|
||
|
||
## Webhook-driven wake (instead of always-on)
|
||
|
||
Register a webhook for `session.status_run_started` (see `shared/managed-agents-webhooks.md`), verify the delivery, then drain one work item with `.run_one()`:
|
||
|
||
```python
|
||
import os
|
||
import anthropic
|
||
from anthropic.lib.environments import EnvironmentWorker
|
||
|
||
environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
|
||
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
|
||
client = anthropic.AsyncAnthropic(
|
||
auth_token=environment_key,
|
||
) # reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env for webhooks.unwrap()
|
||
|
||
|
||
async def handle(raw: bytes, headers: dict[str, str]) -> dict:
|
||
event = client.beta.webhooks.unwrap(raw.decode(), headers=headers)
|
||
if event.data.type != "session.status_run_started":
|
||
return {"status": "ignored"}
|
||
await EnvironmentWorker(
|
||
client,
|
||
environment_id=environment_id,
|
||
environment_key=environment_key,
|
||
workdir="/workspace",
|
||
).run_one()
|
||
return {"status": "ok"}
|
||
```
|
||
|
||
TypeScript: same shape with `client.beta.webhooks.unwrap(body, {headers})` and `new EnvironmentWorker({...}).runOne()`.
|
||
|
||
## Container orchestration (mid-level)
|
||
|
||
`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.
|
||
|
||
| Env var | Value |
|
||
|---|---|
|
||
| `ANTHROPIC_SESSION_ID` | `work.data.id` |
|
||
| `ANTHROPIC_WORK_ID` | `work.id` |
|
||
| `ANTHROPIC_ENVIRONMENT_ID` | `work.environment_id` |
|
||
| `ANTHROPIC_ENVIRONMENT_KEY` | pass through |
|
||
| `ANTHROPIC_BASE_URL` | pass through |
|
||
|
||
Skip items where `work.data.type != "session"`.
|
||
|
||
## Monitoring & control
|
||
|
||
These 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.
|
||
|
||
| SDK (`client.beta.environments.work.*`) | REST | CLI | Returns |
|
||
|---|---|---|---|
|
||
| `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}` |
|
||
| `stop(work_id, environment_id=)` | `POST /v1/environments/{id}/work/{work_id}/stop` | `ant beta:environments:work stop` | `work.state` |
|
||
|
||
## What changes vs `cloud`
|
||
|
||
| Concern | `cloud` | `self_hosted` |
|
||
|---|---|---|
|
||
| Container lifecycle, hardening, networking | Anthropic | **You** — run non-root, read-only rootfs, drop caps; egress is whatever your VPC/firewall allows |
|
||
| `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 |
|
||
| `memory_store` resources | Supported | **Not yet supported** |
|
||
| Built-in tools | Via `agent_toolset_20260401` | Supplied by your worker (`EnvironmentWorker` default / `beta_agent_toolset(env)` / `ant` CLI fixed set) |
|
||
| Skills download | Automatic | `EnvironmentWorker` / `AgentToolContext` fetch into `{workdir}/skills/` (needs `client` + `session_id`) |
|
||
| Claude Platform on AWS | Supported | **Not available** |
|
||
| 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 |
|
||
|
||
## Credentials
|
||
|
||
| Credential | Format | Scope |
|
||
|---|---|---|
|
||
| `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. |
|
||
| `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()`. |
|
||
|
||
## Security — what you own
|
||
|
||
Container 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.
|