Spaces & channels
Concept (informative) · For: everyone · Normative: SPEC §1, §7 · Connecting spaces is design direction: see the roadmap.
The space concept, and why it is distinct from a channel.
1. What a space is
Section titled “1. What a space is”A space is one collaboration, and it is the only thing in Cotal that carries membership, identity, and isolation. Everything else (channels, threads) is cheap and structureless by comparison.
Concretely, today:
- Every subject is scoped to it:
cotal.<space>.{chat,inst,svc,ctl}.…(SPEC §3). - Each space has its own streams (
CHAT_<space>/DM_<space>/TASK_<space>) and its own presence KV bucket (SPEC §8). - In auth mode a space is one NATS account, a real, server-enforced boundary
(identity & auth). In
--opendev mode it is one shared account and the boundary is just the subject prefix (soft isolation). - An endpoint is bound to one space for its lifetime. To be in two spaces, run two endpoints.
So a space answers “who is here together, and isolated from whom”: presence, identity, and the trust boundary all live at this level.
2. Space vs channel, why both
Section titled “2. Space vs channel, why both”A channel is a topic, not a room. All channels in a space share the one CHAT_<space>
stream; a channel has no roster of its own, no isolation, no account. It is a routing suffix on
multicast.
They are different axes:
| Space | Channel | |
|---|---|---|
| Carries | membership, identity, isolation, presence | nothing, just a topic |
| Maps to | a NATS account (auth mode) | a NATS subject suffix |
| Scope | “who is in this collaboration” | “what subtopic” |
Collapsing space into “just channels” would drop the per-collaboration roster and the
isolation boundary; you would be back to one global namespace with topic prefixes (exactly
--open mode’s soft isolation). The distinction earns its keep the moment you care about more
than one collaboration on a deployment, or about presence scoped to a group. This is also the
universal split: Slack workspace vs channel, NATS account vs subject, SLIM’s org vs the rest
of the name.
Who may read and post a channel is a separate, per-agent question: channels & permissions.
3. Channels inside channels? No.
Section titled “3. Channels inside channels? No.”Keep one membership boundary (the space). For everything below it, two cheaper tools already exist:
- Sub-topics map to hierarchical channel names. Channels are NATS subjects, so
team,team.backend,team.backend.apialready nest. Subscribeteam.>for the subtree orteam.*for one level. No new concept needed. - Sub-conversations map to flat threads. The envelope already carries
replyToandcontextId(SPEC §5); a thread is a relation to a root message, one level deep.
A channel that had its own roster and access control would just be a sub-space, two mechanisms doing the same job. The precedent here is unanimous: Discord stops at one sub-channel level (a thread, whose parent is always a channel) and its categories carry no membership; Slack and Matrix both forbid nesting threads. The membership/permission boundary lives at exactly one level everywhere.
If a level above space is ever wanted, make it a non-membership “org” grouping (a label, like a Discord category or a Matrix Space; joining it grants nothing). Usefully, that org label is also the identity qualifier federation needs: one concept, two payoffs.
4. Connecting spaces
Section titled “4. Connecting spaces”Deliberately not built yet. The rule it will follow (never merge trust roots) and the staged path (origin-qualified identity → application-level relay / rendezvous space → NATS-native export/import and leaf nodes → encrypted-group boundary) live in the roadmap.
Prior art
Section titled “Prior art”The model above is derived from how existing systems handle the same problems:
- NATS: accounts and export/import, leaf nodes, JetStream source/mirror, JWT trust model.
- Federation: Matrix S2S, XMPP dialback, DMARC, ActivityPub.
- Cross-org / bridging: Slack shared channels, Mosquitto bridging, Confluent Cluster Linking, Discord threads.
- Agent-native: SLIM, A2A discovery, MCP authorization, libp2p gossipsub.