Concept (informative threat model) · For: operators and security reviewers · Normative: SPEC §9. This page is the threat model SPEC §9 references; where the two disagree, the spec wins.
Cotal v0 provides containment and sender authenticity for peers sharing one trusted NATS broker. It is not an end-to-end encrypted or untrusted-relay protocol. The enforcement mechanics (profiles, ACLs, consumer confinement) are defined in SPEC §9 and Appendix B, explained informally in identity & auth; this page covers who the adversaries are and what is (not) defended.
- One Cotal space maps to one NATS account.
- The broker, operator, account signing key holder, and any
admincredential are trusted. - On a per-user-auth mesh, ledger scope
adminis the same trust grade as anadmincredential: it unlocks the elevated views (the whole-space read tap, history and channel purges, channel-registry writes, cross-owner control), so grant it as operator authority, not as a convenience (identity & auth). - Agents are not trusted to self-report sender identity, channel permissions, or DM access.
Each adversary, what it can attempt, and what stops it (or why it is out of scope).
- Compromised or malicious peer agent (authenticated, in-space): the primary adversary.
It cannot forge another agent's
from.id(the subject sender, anowner.actorprincipal, is pinned to its connection by NATS permissions; not another owner, and not a sibling actor under its own owner), cannot publish to channels outside its declared allow-list, and cannot read another agent's DMs or another role's work queue (SPEC §9). It still can send well-formed hostile content to channels it is allowed on (see Prompt-facing data) and flood within its limits (see availability under What v0 does not protect). - Buggy or lazy receiver: sender authenticity depends on the receiver enforcing the
from.id-equals-subject-sender check; a client that skips it accepts spoofed senders. The check is therefore normative: receivers MUST reject on mismatch (SPEC §5, §12). - On-path network attacker (between an agent and the broker): defeated only when the join
link uses
cotals://(TLS required). Plaincotal://is cleartext on the wire, for trusted networks and dev only. - Content author targeting a reading model: any writer of channel
description/instructions, presenceactivity, message bodies, or free-form metadata can attempt prompt injection against an agent that reads it. See Prompt-facing data. - Untrusted broker, relay, operator, or admin: out of scope by definition. The broker and
any
admincredential can read, drop, replay, or alter all plaintext traffic. v0 makes no claim against a hostile broker; signed envelopes and untrusted-relay bindings are reserved for a later version (roadmap).
The guarantees, at a glance, each enforced by the broker per SPEC §9:
- Sender authenticity: the sender id is encoded in the subject and enforced by NATS
permissions; receivers reject payloads whose
from.idmismatches. - Space containment: account boundaries isolate one space's subjects, streams, and KV buckets from another.
- Channel publish scope: posting only as self, only to declared
allowPublishchannels (default-deny). - Channel read scope, reads bounded to the
allowSubscribeACL: live joins are broker-refused outside it, and history reads ride server-pinned single-channel consumers.- Known metadata leak (not content): agents hold
STREAM.INFOon the chat stream, so asubjects_filterquery can enumerate retained chat subjects (channel names, sender ids, per-subject counts) including channels outsideallowSubscribe. This is metadata, never message content, and channel names are already public via the registry. Hiding even the existence/volume of other channels requires the per-channel-stream model and is deferred strict-containment work (roadmap).
- Known metadata leak (not content): agents hold
- DM / task peer confidentiality: per-identity inbox prefixes plus provisioner-created bind-only consumers, so an agent cannot read someone else's inbox or steal another role's work; durable-channel backstop reads are re-authorized by a trusted reader (delivery daemon).
- Transport secrecy (optional):
cotals://enforces TLS for the hop to the broker. It protects that hop, not the broker itself.
- Untrusted broker or relay: the broker can read, drop, replay, or alter plaintext traffic. Signed envelopes are reserved for a later version.
- End-to-end secrecy: DMs are plaintext to the broker and to
admin. Cotal v0 deliberately does not add end-to-end encryption, trading secrecy for a single trusted broker. - Non-repudiation: sender authenticity is broker-enforced, not portable proof. (A2A signs every message for this; here it is reserved as signed envelopes.)
- Availability: an authenticated peer can flood any channel or inbox it may write to. v0 relies on coarse NATS account limits (connections, subscriptions, payload and storage caps) and adds no per-agent application-level rate limiting.
- Replay by a peer: a peer may re-send its own prior messages; v0 defines no protocol-level nonce or idempotency key. It cannot replay as another agent (subject binding still holds).
- Static agent credential revocation: on a static-auth mesh, a minted agent cred is long-lived unless the signing key is rotated; despawn cuts a session, not a credential. The machinery is bounded (one-shot command creds expire in minutes, standing daemon creds in 24h with renewal), and a per-user-auth mesh closes the gap entirely: short-lived bearers, ledger revocation that bites at the next connect, and live-connection eviction (identity & auth). A copied signing seed still stays valid until rotation on either kind of mesh.
- Manager compromise: the operator side is split into narrow, single-purpose profiles (there
is no allow-all cred); the long-lived supervisor serves control and touches
presence/its lease but cannot read a DM, create a consumer, or delete a stream; the destructive
verbs (
STREAM.DELETE/PURGE, cross-agent stop, per-agent provisioning) ride ephemeral per-command creds (teardown / control-caller-admin / deployer / provisioner). What stays hot on a static-auth mesh is the account signing key on the mint/manager box (a compromise there can still mint fresh creds); on a per-user-auth mesh it is confined to the auth service (the callout stage, shipped for user mode; identity & auth). spawnis host-launch authority: launch options are a raw passthrough (no allow/deny list), so a persona holdingcapabilities: [spawn]can drive the connector's full launch surface on the manager host (Claude--mcp-config,--add-dir, permission flags; OpenCode agent-config keys). The boundary is who may spawn (the authenticated caller, gated by the capability), not which flags they pass. Grantspawnas host-launch authority, not a narrow "add a teammate" permission (run a mesh).
Channel description and instructions, presence activity, message bodies, and free-form
metadata may reach models. Writers that can set channel registry text are privileged, and
registry text is length-bounded, but clients MUST still render all of it as attributed,
advisory data, never as trusted system instruction. This is the indirect-prompt-injection
surface common to agent protocols (MCP tool descriptions, A2A agent cards): Cotal's position is
that the reading client, not the wire, is the trust boundary for model-facing text.
Report a suspected vulnerability privately to the maintainers rather than in a public issue.