Deploy agent teams
The deploy/ example describes containers that connect to an existing Cotal broker.
It includes a team container with a manager and a single-agent container.
Use this page to assess its requirements and trust boundary before adapting it.
The documented image build is unsupported
The Dockerfile runs pnpm cotal setup from a source checkout.
The CLI refuses that checkout's attempt to seed operator-global connectors.
The setup step reports refusing to <action> the operator-global seed store at <path> for generation <version> from <origin> (<entry>), with action reconcile and origin a source checkout.
That refusal was reproduced from a built checkout outside Docker; no image was built to confirm the complete Docker path. The example below is not a verified deployment procedure. For supported local operation, use Run a mesh and Define a team.
Inspect the deployment files
The deployment README contains the example's full local and production walkthrough. The image is configured through environment variables, roster files, and persona mounts. Changing the team does not require an image change.
| File | Purpose |
|---|---|
Dockerfile | Bundles the Cotal, Claude Code, and OpenCode CLIs, attempts connector setup, and prepares Claude onboarding for unattended use. |
entrypoint.sh | Waits for the external broker, then runs cotal <cmd> --server $COTAL_SERVERS with the daemon Node flag --max-semi-space-size=1. |
compose.yaml | Defines a team-a manager with a roster and a solo agent. |
roster.example.yaml | Provides a team roster to copy and edit. |
The deployment tree does not run the broker.
It also does not implement the README's later host-side per-agent credential provisioning or stronger sandbox options.
The image bundles Claude Code and OpenCode only.
It does not include uv or hermes-agent, so it cannot run Hermes agents.
Choose the container shape
The container command selects its shape:
supervise --space <s> --roster /workspace/roster.yamlruns a manager and all roster agents in one container with theptyruntime.spawn <name> --foregroundruns one agent from.cotal/agents/<name>.md.
A roster can mix agent: claude and agent: opencode entries.
See Roster files and Agent files for their formats.
Prepare the external services
The example needs Docker, a reachable broker, account signing material, and model credentials. The broker must run nats-server 2.12 or newer; older brokers are refused at connection.
A local broker binds to loopback by default.
For a broker reached by containers, start it with cotal up --host 0.0.0.0 and keep authentication enabled.
Use nats://host.docker.internal:4222 for the host-machine example or a configured tls://broker.host:4222 endpoint for a hosted broker.
The container's COTAL_SERVERS must name an address it can reach.
On the broker host, cotal auth signer --out signer.json exports account signing material without the operator key.
Treat that signer as authority over its entire account, as described below.
Provide a credential for each model provider used by the team's connectors.
Review the example workflow
The example starts with this image build from the repository root. It currently stops at the setup limitation above:
docker build -f deploy/docker/Dockerfile -t cotal-runner .The compose example expects paths relative to deploy/docker/.
Its inputs are signer.json, team-a/roster.yaml, and team-a/agents/*.md:
cp deploy/docker/roster.example.yaml deploy/docker/team-a/roster.yaml # then edit; add agents + signer.json
COTAL_SERVERS=tls://broker.host:4222 \
CLAUDE_CODE_OAUTH_TOKEN=<token> OPENCODE_API_KEY=<key> \
docker compose -f deploy/docker/compose.yaml up team-aThe deployment README also describes a single docker run with explicit mounts and a local-broker variant.
For an adapted deployment that is running, observe the team with cotal console --plain --space <s>.
Supply credentials from the host
Broker authentication and model authentication use separate credentials. Both come from outside the container.
The signer grants authority over its account
Mount the stripped signer.json read-only at /workspace/.cotal/auth/auth.json.
The example mints scoped per-agent credentials into the RAM-backed /workspace/.cotal/auth/creds directory.
The operator root key stays outside the container.
The stripped signer still gives full authority within its NATS account. It can mint admin credentials that read DMs and destructive profiles, not just agent credentials. Its account boundary prevents cross-tenant escalation, not damage within that account.
The supervisor forwards the named credential variables each connector declares, not its whole environment.
For example, a Claude seat receives CLAUDE_CODE_OAUTH_TOKEN because the connector declares it.
That rule does not forward every variable beginning with CLAUDE_CODE_.
| Connector | Model credential |
|---|---|
| Claude Code | CLAUDE_CODE_OAUTH_TOKEN, obtained with claude setup-token on the host. |
| OpenCode | Forwarded without configuration: OPENCODE_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, OPENROUTER_API_KEY, and NOUS_API_KEY. Other provider names require a spawn.env entry. |
A Claude token uses the host account's Claude Pro or Max subscription. Each application reads the provider variables it understands.
Every configured secret can reach every agent in the team container.
The container is the team's trust boundary; it does not isolate secrets between those agents.
For per-agent isolation, use one agent per container, as the solo service does.
Check the mounted files
The working directory is /workspace:
| Path | Mount | Contents |
|---|---|---|
/workspace/.cotal/auth/auth.json | Read-only | Stripped account signer. |
/workspace/.cotal/agents/*.md | Read-only | Personas. |
/workspace/roster.yaml | Read-only | Team roster. |
/workspace/.cotal/auth/creds/ | tmpfs with mode=01777 | Per-agent credentials held in RAM. |
Review the isolation boundary
The example runs as non-root UID 10001, drops all Linux capabilities, and uses an ephemeral writable filesystem.
Host mounts are limited to the read-only inputs above and the credential tmpfs.
Network egress includes the broker and each agent's model API.
A fully read-only root filesystem or a sandbox runtime such as gVisor or Kata is a later hardening option, not part of this example's current guarantees. See Identity and auth for signer and account scoping.
Continue with connector setup
Use Connect Claude Code or Connect OpenCode for the application-specific prerequisites.