Skip to content

Define a team

Guide (informative) · For: operators · Prereqs: Quickstart

The Quickstart gives you one agent. To run a specific team (your own channels, your own agents, and the channel access for each agent), describe it once in a cotal.yaml and launch it with a single command.

A manifest (cotal.yaml, kind: Mesh) is the declarative form of what you’d otherwise do by hand: start a broker, seed channels, spawn agents, and mint each agent creds scoped to the channels it may use. It is a convenience over the CLI and adds no wire concepts. Today it is single-space (one space: per file).

It is channel-centric: you list the channels, and under each one name the agents that may read and post. Cotal inverts that into one least-privilege credential per agent, so the file reads the way you think about a team (“who’s in #review?”), while each agent only gets the access you granted.

A complete, runnable manifest (two agents, two channels, no separate files):

apiVersion: cotal/v1
kind: Mesh
space: main # the default space, runnable fresh or right after `cotal up`
agent: claude # the harness that runs each agent
agents: # inline personas (no external files needed)
planner:
instructions: Break the work into steps and post the plan.
builder:
instructions: Implement the smallest change that works.
channels:
general:
subscribe: [planner, builder] # auto-listen at boot
allowPublish: [planner, builder] # may post: default-deny, so list everyone who posts
review:
subscribe: [planner] # only planner auto-listens
allowSubscribe: [planner, builder] # builder MAY read #review, but isn't auto-subscribed
allowPublish: [planner, builder]

Save it as cotal.yaml and launch:

Terminal window
cotal topology view -f cotal.yaml # validate + render the access graph (no broker needed)
cotal up -f cotal.yaml # broker + channels + agents, all fresh
cotal ps --space main # see the agents the manager booted
cotal web --space main # ...or watch it in the browser
cotal down # stop the whole mesh

The manifest introduces no access model of its own; the three verbs are the same ones Cotal uses everywhere: subscribe (auto-listen at boot, and implicitly may read), allowSubscribe (read; defaults to subscribe, must be a superset of it), and allowPublish (post; default-deny: an empty or omitted list means nobody posts). Above, builder may read #review but doesn’t auto-listen to it. Every top-level key, the three agents: forms, channel cards, and the resolution rules are in the manifest reference.

Command What it does
cotal topology view -f <file> Validate the file and render its access graph. Read-only: needs no broker, mutates nothing. Run it before you launch.
cotal up -f <file> Bring up a fresh mesh: broker + seeded channels + booted agents.
cotal spawn -f <file> Deploy a manifest additively onto a mesh that is already running.
cotal down [-f <file>] Tear down (see “Tearing down” below).

up -f and spawn -f accept --dry-run (preview the plan, change nothing). up -f also takes --server / --host / --space / --runtime / --open to override the file for one run.

If a Cotal mesh is already running at the manifest’s broker address (e.g. the default 127.0.0.1:4222 from cotal up), up -f refuses; it never re-seeds a live broker. The check is on the address, not the space: name. Run cotal down first, point the manifest at another address (broker: { servers: nats://127.0.0.1:14999 }, or --server), or use cotal spawn -f to deploy onto the running mesh.

Tearing down. A fresh mesh from up -f is torn down with plain cotal down: it owns the whole space. An additive deploy from spawn -f is torn down with cotal down -f <file> (or cotal down -f <file> --run <id>), which removes only that run’s agents and channels.

The rule: up -f owns the whole space; spawn -f owns only what it created. Cotal only ever tears down what it owns; foreign actors on a shared mesh are never touched.

  • A fresh mesh from up -fcotal down stops all of it.
  • An additive deploy from spawn -f records a creation-only ledger (.cotal/manifests/<runId>.json) of the channels and agents it added; cotal down -f removes only those. The run id is printed by spawn -f and is the filename under .cotal/manifests/; pass it to down -f --run <id> when the file has changed since the deploy (an edited file no longer matches its ledger) or to finish a teardown that was retained.

down -f is deliberately conservative; it treats the ledger as untrusted and validates before deleting: an owned agent is stopped only when the live agent’s recorded name and id match; an owned channel is removed only when no other members remain; and if the broker is unreachable or anything is uncertain, nothing remote is removed and the ledger is retained for a later down -f --run <id>. It is local-only: run it from the checkout that created the run.

(.cotal/ holds creds, the ledger, and runtime artifacts: add it to your .gitignore; commit your cotal.yaml and persona files, not what’s under it.)

spawn -f is additive and never adopts or mutates anything it didn’t create. It classifies each declared item against the live mesh:

Item Classification Behaviour
Channel, brand-new created + owned Seeded and recorded in the ledger.
Channel, already present exists-unmanaged Left untouched: card not mutated; the desired card is shown against the live one.
Agent, not yet created will-create Booted and recorded.
Agent, already created, unchanged already-owned No-op.
Agent, already created, policy changed stale Exits non-zero unless --allow-stale <names> (then it restarts).

Security. If an unmanaged actor already has read access to a channel you declare, spawn -f prints a warning: an isolation conflict on a shared mesh. It is an explicit lower bound (presence plus the broker membership feed), not a guarantee that no other access exists.

Deploying to a remote manager. The mesh’s manager may live on another machine (or another checkout): spawn -f detects that from the manager lease and pushes the resolved launch spec inline over the control plane. The manager validates it as untrusted input and persists it under its own .cotal/run/ before launching, so nothing changes downstream. Run the deploy from the checkout the mesh is registered to on your machine (that’s where the ledger lands), and run down -f from that same checkout; it stops remote agents over the control plane and treats a locally-absent cred file as proven-absent. One residual: the agents’ cred files minted on the manager’s host stay there until the mesh’s own cleanup, the same way it would after a crash.

Every mesh-touching command resolves the broker from the mesh registry, so --space <name> is enough; send, channels, console, web, the manifest verbs, and the manager control commands (cotal ps / stop / attach, plus cotal spawn --detach) all reach a manifest mesh on any port with no --server:

Terminal window
cotal ps --space research-team # finds research-team's broker via the registry

--server remains an explicit override for an off-registry broker.


See manifest.md for the complete field reference and the resolution rules, channels and permissions for the access model, and agent files for the persona format the agents: entries point at.