Skip to content

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 protocolVersion fails the task with Container 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 0 completes 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

  1. Go to Squad in the sidebar and open the Squads tab.

  2. Click Create Squad, or choose Edit from an existing squad card's menu. The squad wizard opens.

  3. On the wizard's Workspace step, set the Workspace Directory if it is not set yet.

Step 2: Enable Container Mode for an Agent

  1. 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.

  2. Set Execution Mode to Container (the default is In-Process).

  3. The Container Configuration fields appear:

    Field Default Description
    Image aigo-agent-runner:latest Container 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.
  4. 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: true to 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

  1. Go to the Workspace step of the Create Squad wizard.

  2. Click Create New Group next to Group Namespace.

  3. Enter a Group Name (filesystem-safe, e.g., dev-team).

  4. Optionally add a Description.

  5. 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:

  1. Go to Settings > Squad.

  2. Find Global Instructions.

  3. Enter your global directives.

  4. Click Save Instructions.

Step 5: Run the Squad

  1. Open the squad and go to its Chat tab.

  2. Type your task in the message box and send it.

  3. The planner creates a task graph and assigns tasks to agents.

  4. 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

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.

  1. Call GET /api/v1/container/run-history.

  2. Each entry has the group, start and end times, exit code, and summaries of the input and output.

Audit Log

  1. Call GET /api/v1/container/audit-log.

  2. 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": true while a task runs. Squad tasks need no credential mapping.
  • Check that the container's ANTHROPIC_BASE_URL ends in /router/anthropic and its ANTHROPIC_API_KEY is a cpt_ 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