Squad Container Mode¶
Squad Container Mode runs individual squad agents inside isolated containers, providing stronger security boundaries and reproducible execution environments. You can enable container mode per-agent within a squad, so some agents run in-process while others run in containers.
Prerequisites¶
Before enabling container mode, complete the Container Execution Guide:
- Docker, or Apple Container on macOS 26 (see Model Route for how a container reaches its model on each)
- Agent runner image built (
aigo-agent-runner:latest) - Mount allowlist configured
- A model for the agent: its own model, or the Agent default model in Settings > Models, exactly as for an in-process agent
The credential proxy does not need a mapping or any setup for squad tasks. The app starts it when a container task needs it.
How It Works¶
When an agent runs in container mode:
sequenceDiagram
participant Host as Backend.AI GO Host
participant CP as Credential Proxy
participant C as Container (agent runner)
participant R as Continuum Router
participant M as Agent's model
Host->>CP: Start if needed, grant a run token
Host->>C: Start container with workspace mounts, proxy URL, and run token
Host->>C: stdin: one task and the agent's model
C->>CP: POST /router/anthropic/v1/messages (run token)
CP->>R: Same request with the app's router key
R->>M: Route to the agent's model and provider
M-->>R: Answer
R-->>CP: Response
CP-->>C: Response
C->>Host: stdout: one result block
C->>Host: Exit 0 (success) or 1 (error)
Host->>CP: Revoke the run token
Host->>Host: Record the task result or error One container runs one task. The host does not send follow-up messages, and the container exits as soon as it has written its result, so a task never waits for the container timeout unless the agent itself is still working.
Each container receives:
/workspace: the squad's shared workspace directory (read-write for main agents, read-only for others)/workspace/extra/{name}: additional mounts set on the agent through the Management API (see Step 3)/workspace/ipc: the IPC directory for host-container communication.claude/: the agent's SDK session state
Model Route¶
A container agent runs on the model its agent is configured with, resolved the same way as for an in-process agent: the agent's own model, else the Agent default model in Settings > Models. The provider chosen for the agent, the output budget sized to the model's context window, and the temperature are the same too. An agent with no model fails its task with the same message as an in-process agent, and no container is started.
The container reaches that model through the credential proxy's router route, never directly:
| Variable | Value inside the container |
|---|---|
ANTHROPIC_BASE_URL | Docker: http://host.docker.internal:<proxy port>/router/anthropic (port 3001 by default). Apple Container: http://localhost/router/anthropic, where localhost is only the Host header. |
ANTHROPIC_API_KEY | A cpt_ token for this task alone. The proxy accepts it only while the task runs and rejects it once the task ends or is stopped. |
AIGO_INFERENCE_SOCKET | Apple Container only: /tmp/aigo-inference.sock, the proxy's Unix socket relayed into the container. The runner sends every model call over it. |
The proxy checks the token, drops every header except the few a Messages call needs, attaches the app's own router key, and forwards the request to the embedded Continuum Router over the same socket or loopback port the in-process path uses. The router then resolves the model to a local engine or a configured provider. The route forwards only POST /anthropic/v1/messages: any other path, such as the router's model list, Files API, or stored responses, is refused with 404 before the router is contacted, since the app's router key would otherwise open it to the container. The app's router key never enters the container, and neither does any ANTHROPIC_* variable from the app's own environment: only TZ is passed through.
How the container finds the host depends on the runtime:
| Runtime | Route |
|---|---|
| Docker Desktop (macOS, Windows) | host.docker.internal is built in. The proxy listens on 127.0.0.1. |
| Docker on Linux | The container gets --add-host=host.docker.internal:<docker0 address>, and the proxy listens on that same address, so it is reachable from containers but not from the network. |
| Apple Container | The guest has no route to a host listener, so the app relays the proxy's Unix socket into the container at /tmp/aigo-inference.sock (aigo-container-cli run --relay-socket). The socket is credential-proxy-<instance>.sock in the app sockets directory, readable only by your user, and a request on it passes the same token check and path allowlist as one over TCP. If the proxy could not create its socket, the task fails before a container starts with Container mode under Apple Container needs the credential proxy's Unix socket, which this process could not create. |
Docker without a docker0 bridge (rootless Docker, Docker Desktop for Linux) is not supported. It shows up as Container inference route unavailable: <error> or Router unreachable: <error>; see Container Troubleshooting.
Agent Runner Protocol¶
The host and the agent runner image speak a versioned protocol. The bundled aigo-agent-runner:latest image speaks version 2, and a custom image must speak the same version to run squad tasks.
Input¶
The host writes one JSON line to the container's stdin and then closes it:
{
"protocolVersion": 2,
"task": "## Task: Write the config parser\n\nImplement parse_config in src/config.rs.",
"context": ["### Previous task result (task-schema):\nThe schema lives in docs/schema.md."],
"agentName": "Developer",
"systemPrompt": "You are a careful Rust developer.",
"deadlineMs": 1770000,
"model": "qwen3-8b-instruct",
"maxTokens": 2048,
"backend": "provider-openai-a1b2c3d4",
"temperature": 0.7
}
| Field | Required | Description |
|---|---|---|
protocolVersion | Yes | Always 2. The runner rejects a missing or different version. |
task | Yes | The task title and description. Never blank. |
context | No (default []) | Results of the tasks this task depends on, each labeled ### Previous task result (<task id>):. Only the task's own dependencies are included, the same results an in-process agent receives. |
agentName | No (default "") | The agent's name. |
systemPrompt | No (default "") | The agent's system prompt. When empty, the runner uses a generic squad prompt. |
deadlineMs | No | Milliseconds the runner has to write its result. The host sets it a tenth below the container timeout, keeping back at most 30 seconds, so a model call that stalls ends with the runner's own error instead of a killed container. |
model | Yes | The model the host resolved for the agent. Never blank. The runner has no default model of its own. |
maxTokens | Yes | The output budget, already clamped to the model's context window. A positive integer. |
backend | No | The router backend for the provider chosen on the agent. The runner sends it as the x-backend header. |
temperature | No | The sampling temperature. Left out when the model accepts none. |
The input is at most 1 MiB. The host fails a task whose input is larger without starting a container, since the runner would refuse it. Fields the runner does not know are ignored, so an optional field added later does not change the protocol version. Version 2 made model and maxTokens required: a version 1 runner would have ignored them and called its own default model, so it fails with a version error instead.
Output¶
The runner writes exactly one JSON block to stdout between two marker lines, then exits:
---AIGO_OUTPUT_START---
{"protocolVersion":2,"status":"success","result":"Implemented parse_config.","usage":{"promptTokens":2050,"completionTokens":340}}
---AIGO_OUTPUT_END---
| Field | Description |
|---|---|
protocolVersion | Always 2 |
status | success or error |
result | The agent's answer, or null on error |
error | The error message. Present only on error. |
usage | promptTokens (input tokens, including cache writes and cache reads) and completionTokens. Present only on success, and left out when the upstream API reports no token counts. |
Other stdout lines and all stderr lines are kept as container logs.
Exit Codes¶
| Exit code | Meaning |
|---|---|
0 | Success. The task result is the block's result. |
1 | Error. The task fails with Container agent error: <error>. |
The block decides the outcome, not the exit code alone:
- An error block fails the task even when the container exits
0, and its message replaces the bare exit status when the container exits with a non-zero code. - A block with a different
protocolVersionfails the task withContainer agent speaks protocol version <v>; this app speaks 2 (rebuild aigo-agent-runner:latest or update the custom image). - A container that prints no block keeps the earlier behavior: exit
0completes the task with the container's log output as the result, and any other exit code fails it with the exit status.
Images Built for an Earlier Protocol¶
An agent runner image built for an earlier protocol cannot run the input above, so every task assigned to it fails:
- An image built before the protocol existed fails with a message ending in
(the image predates agent runner protocol 2; rebuild aigo-agent-runner:latest or update the custom image). - An image built for protocol 1 fails with
Container agent speaks protocol version 1; this app speaks 2 (rebuild aigo-agent-runner:latest or update the custom image).
Rebuild the image from Settings > Squad > Agent Runner Image → Rebuild Image, or run ./container/build.sh from a source checkout. A custom image has to be changed to read the version 2 input, call the model it is given through ANTHROPIC_BASE_URL, and write the output block. See Container Troubleshooting.
Step 1: Create or Edit a Squad¶
-
Go to Squad in the sidebar and open the Squads tab.
-
Click Create Squad, or choose Edit from an existing squad card's menu. The squad wizard opens.
-
On the wizard's Workspace step, set the Workspace Directory if it is not set yet.
Step 2: Enable Container Mode for an Agent¶
-
On the wizard's Agents step, find the agent you want to run in a container. For a squad you already have open, you can instead click Edit on the agent's card under Agents on the squad's Overview tab: the Edit Agent dialog has the same fields.
-
Set Execution Mode to Container (the default is In-Process).
-
The Container Configuration fields appear:
Field Default Description Image aigo-agent-runner:latestContainer image to use Timeout (seconds) 1800 (30 minutes) Maximum execution time, from 60 to 7200 seconds Idle Timeout (seconds) 300 Saved with the agent, but the container runner does not apply it yet. Only Timeout (seconds) stops a running container. -
Finish the wizard with Create Squad or Save Changes, or click Save in the Edit Agent dialog.
Step 3: Add Mounts Through the Management API (Optional)¶
The agent editor has no field for extra mounts. If the agent needs host paths beyond the workspace, set additionalMounts in the agent's containerConfig with PATCH /api/v1/squads/{id}/agents/{agent_id}. Each entry takes:
hostPath: the path on the host to mount (must be under an allowed root)containerPath: where to mount it inside the container (must be under/workspace/extra/)readOnly:trueto keep the agent from writing to the path
The request replaces the agent's whole containerConfig, so send image and timeout (in milliseconds) along with additionalMounts, or they return to their defaults.
Mount Security
Only paths under an allowed root can be mounted. Paths containing patterns like .ssh, .env, or .aws are always blocked.
Step 4: Configure Group Namespaces¶
Group namespaces organize containers into isolated session groups. Each group gets its own session directory and instruction set.
Create a Group Namespace¶
-
Go to the Workspace step of the Create Squad wizard.
-
Click Create New Group next to Group Namespace.
-
Enter a Group Name (filesystem-safe, e.g.,
dev-team). -
Optionally add a Description.
-
Click Create New Group under the fields.
Set Group Instructions¶
Group-specific instructions (equivalent to CLAUDE.md) are injected into every container session for that group. The app has no editor for them: set them with PUT /api/v1/container/namespaces/{name}/instructions, as shown in Setting Group Instructions.
Global Instructions¶
Global instructions apply to all groups. They are combined with group-specific instructions and merged for each container:
-
Go to Settings > Squad.
-
Find Global Instructions.
-
Enter your global directives.
-
Click Save Instructions.
Step 5: Run the Squad¶
-
Open the squad and go to its Chat tab.
-
Type your task in the message box and send it.
-
The planner creates a task graph and assigns tasks to agents.
-
When a task is assigned to a container-mode agent:
- A session directory is created under
DATA_DIR/sessions/{group}/ - The credential proxy is started if it is not running, and grants the task a router token
- The container is started with the workspace and IPC mounts
- The agent runner receives the task on stdin, writes one result block to stdout, and exits
- The task completes with the block's result, or fails with the runner's own error message or the timeout
- A session directory is created under
Monitoring Container Execution¶
Container Status Panel¶
While a container-mode agent works, the squad's Chat tab shows a status card for its container, and the Overview tab adds a Container Queue section with the running, queued, and stopping counts. The status card shows one of these states (labeled Provisioning, Running, Completed, Failed, and Timed Out):
| Status | Description |
|---|---|
provisioning | Container is starting, image being prepared |
running | Container is active and processing |
completed | Container finished successfully |
failed | Container exited with an error |
timeout | Container exceeded the execution time limit |
Run History¶
Container run history is available only through the Management API. Automations > Execution History lists automation runs, not container runs.
-
Call
GET /api/v1/container/run-history. -
Each entry has the group, start and end times, exit code, and summaries of the input and output.
Audit Log¶
-
Call
GET /api/v1/container/audit-log. -
Browse security events, permission checks, and mount validations.
Troubleshooting Container Mode¶
Agent container does not start¶
- Verify the agent runner image exists: Settings > Squad > Agent Runner Image
- Check the runtime is available:
curl http://localhost:8001/api/v1/container/runtime - Review the audit log for permission errors
Agent cannot write to workspace¶
- Confirm the squad workspace directory is under an allowed root
- Check that the agent is designated as a main agent (only main agents get read-write workspace access)
Container times out¶
- Increase Timeout (seconds) in the agent's container configuration
- Click Show container logs on the agent's status card in the Chat tab and look for slow steps
- Consider splitting the task into smaller subtasks
Model calls fail inside container¶
- Check the credential proxy status endpoint reports
"running": truewhile a task runs. Squad tasks need no credential mapping. - Check that the container's
ANTHROPIC_BASE_URLends in/router/anthropicand itsANTHROPIC_API_KEYis acpt_token - Match the task error against Credential Proxy Issues
API Reference¶
| Endpoint | Method | Description |
|---|---|---|
/api/v1/container/queue/groups | GET | List all container groups |
/api/v1/container/queue/groups/{group} | GET | Get container group state |
/api/v1/container/queue/counts | GET | Get queue counts per group |
/api/v1/container/queue/max-concurrent | GET/PUT | Get or set max concurrent containers |
/api/v1/container/run-history | GET | View container run history |
/api/v1/container/metrics | GET | Current container metrics |
/api/v1/container/analytics | GET | Aggregated container analytics |