Mesh manifest (`cotal.yaml`)
Reference: every field of the mesh manifest. · For: operators · Walkthrough: Define a team · ACL semantics: SPEC §9
A manifest (cotal.yaml, kind: Mesh) describes a whole team (its channels, its agents,
and who may read and post where) in one 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. The manifest is a convenience over the CLI;
it adds no wire concepts. Today it is single-space (one space: per file).
The lifecycle (cotal topology view -f / up -f / spawn -f / down -f), ownership,
and teardown behavior are in the guide: Define a team.
Top level
Section titled “Top level”| Key | Required | Meaning |
|---|---|---|
apiVersion |
yes | Must be cotal/v1. |
kind |
yes | Must be Mesh. |
space |
yes | The space name (one per file; spaces: is not supported in v1). A space’s auth is bound to one root; to run a non-default space in a checkout that already ran cotal up (which sets up main), use a fresh directory. |
broker |
no | servers (comma-separated broker URLs: this sets the address/port; default nats://127.0.0.1:4222; no embedded creds), host (bind interface only, no scheme; does not set the port), auth (the auth mode: unset/true/"static" = per-agent JWT creds, the default; false = an open dev mesh; "user" = per-user auth, where people cotal login and every connect is authorized against the actor ledger; pair with idp), idp (with auth: "user": the IdP auth base URL to pin on first enable). The port comes from servers/--server, never host/--host. |
runtime |
no | Registered manager runtime name. pty is built in; optional providers such as tmux, cmux, orca, and herdr are installed with cotal ext add. |
agent |
no | Default harness (claude / opencode / hermes) for agents that don’t set their own. There is no silent default; an agent needs this or its own agent:. |
personaPermissions |
no | reject (default): the manifest is the whole truth. include: a persona’s own channel grants are inherited for channels the manifest doesn’t declare. |
defaults |
no | Channel defaults applied unless a channel overrides: replay, replayWindow, deliveryClass (live / durable). Semantics in SPEC §7. |
agents |
no | name → persona (a channels-first manifest can seed rooms now and add agents later). |
channels |
yes | name → channel (below). |
Unknown keys are rejected (no silent ignore), and every error is reported with its file and line.
Agent forms
Section titled “Agent forms”agents: planner: ./agents/planner.md # 1) bare path: reuse a persona file as-is builder: # 2) a persona file + overrides (manifest wins) persona: ./agents/builder.md model: sonnet cwd: repos/backend # relative to the manager workspace role: implementer instructions: Prefer the smallest change that works. lead: # 3) inline (no file): needs at least model or instructions model: opus role: lead capabilities: [spawn] # may spawn helpers instructions: Coordinate the team. prompt: Introduce yourself in #general and assign the first task.Per-agent keys: persona, agent (harness override), cwd, continuity, model, variant, role,
description, instructions, prompt, capabilities (spawn,
what it grants; on a per-user-auth mesh also role:<r>, so the
agent may delegate that role when spawning; admin is never accepted from a manifest),
personaPermissions (override the top-level policy). Model strings and variants pass to
the harness as-is: for Claude use the short form (opus, sonnet) or the full id; for
OpenCode use provider/model plus an optional variant (cotal models --agent opencode
lists both). Persona file format: agent files.
cwd is the agent’s working directory on the manager host. A relative path resolves
against the manager workspace, matching cotal spawn --cwd; an absolute path is used
as supplied. Omitting it keeps the manager workspace as the default. It is never resolved
against the manifest or persona directory on the deploying machine. Changing cwd marks
an already-deployed agent stale and requires a restart. Empty paths and NUL bytes are
rejected. This field controls the directory only; continuity restores a harness session.
continuity says whether an agent keeps its harness session across launches. none, the
default, starts a new session every time. exact reopens the session the manager last
bound to this agent name, so after cotal down and cotal up -f the agent comes back
with its previous context. The manager owns the session id: on the first launch it
records the session the connector proves over its authenticated control endpoint in
<manager workspace>/.cotal/continuity/<name>.json. A connector that offers no such proof
fails the launch, and nothing is recorded. The record follows the agent: crash recovery
updates it when it rebinds a session, and a stop or a preservation cut records the
session the agent last ran, including one it switched to itself. Every later launch, and
a preserved resume of the session that cut retained, reopens the session under the same
proof and fails if the connector reports a different one. A resume that fails this proof
stops the seat it started and frees it once its exit is proved; a seat whose stop cannot be
proved stays managed, and the resume’s error says so. A reopen also fails when the
harness no longer has that session, for example after its transcript was deleted or when
the agent never answered and so nothing was stored; the manager never starts an empty
session under the old id, and a failed launch names the file to remove to start a new
one. The manifest never names a session id, and the imperative cotal spawn --resume
fork stays separate. A reopened session does not get the kickoff
prompt again. The manager refuses to reopen a recorded session whose space, connector
or resolved cwd differs from the declaration, and names the file to remove to start a
new session. A connector that cannot reopen an existing session refuses the manifest at
preflight (today only pi can). Switching continuity marks an already-deployed agent
stale.
instructions and prompt differ in kind: instructions become the session’s system
prompt (who the agent is), while prompt is a kickoff message auto-submitted once
the session is up (what to do right now). This is the declarative form of cotal spawn --prompt.
It is submitted on first boot and again on a stale-restart (it is part of the launch form,
so changing it marks a running agent stale like any other launch field); a manager
reclaiming a still-live session does not re-submit it. A connector that cannot deliver a
kickoff prompt refuses the manifest at preflight (today: hermes), the same way an
unsupported variant: is refused.
Channel grants
Section titled “Channel grants”A channel carries its registry card (description, instructions, replay, …;
SPEC §7) plus three lists of agent names, the same verbs Cotal
uses everywhere (channels & permissions):
| Verb | ACL | Meaning |
|---|---|---|
subscribe |
none | Auto-listen at boot. A subscriber is implicitly allowed to read. |
allowSubscribe |
read | May read the channel. Omitted ⇒ defaults to subscribe. Must be a superset of subscribe. |
allowPublish |
post | May post. Default-deny: an empty or omitted list means nobody posts. |
A read-only channel (no agent posts, e.g. an operator writes the record by hand with
cotal send, which is a CLI action outside agent ACLs):
channels: decisions: description: The durable record of what we decided. subscribe: [lead] allowPublish: [] # read-only for agentsEvery name under a channel must be declared in agents:. Channel names must be concrete
(no wildcards in v1).
How access is resolved
Section titled “How access is resolved”You declare membership per channel; Cotal inverts it into each agent’s minted creds:
- Read comes from
allowSubscribe(orsubscribewhenallowSubscribeis omitted). - Post comes from
allowPublish, and is default-deny: an agent you don’t list cannot post, even to a channel it reads. subscribeonly sets what an agent auto-listens to at boot; it never widens read.
With personaPermissions: reject (the default) the manifest is the complete picture; a
persona file’s own channel grants are ignored, so the file you read is what each
agent can do. Set include (top level or per agent) to also inherit a persona’s own
grants for channels the manifest doesn’t mention. cotal topology view -f always prints
the resolved graph, inherited scopes included.
For implementers: the channel-centric → per-agent inversion lives in
resolve.ts; the spawn -f
classification and teardown in
spawn-plan.ts and
down-manifest.ts.