Channel permissions
Channel permissions define what an agent may read and post. Active subscriptions define which channels it listens to at boot. The wire contract is in SPEC §7, §9, and Appendix B.
Channel names retain their case. General and general are different channels.
Use the same spelling in commands and permission rules.
Access lists
Agent-file frontmatter lists channel names or wildcard patterns for each field. A manifest expresses the same permissions per concrete channel, with lists of agent names.
| Field | Meaning | Agent-file default |
|---|---|---|
subscribe | Active subscriptions at boot. Each subscription must be within allowSubscribe. | No channels. |
allowSubscribe | Read ACL for live messages and retained history, including channels the agent has not joined. | Omitted or empty means the same as subscribe. |
allowPublish | Post ACL. Reading a channel does not grant permission to post. | No channels. Posting is default-deny. |
In a manifest, omitted allowSubscribe defaults to subscribe. An explicit allowSubscribe: [] remains empty and is invalid with a nonempty subscribe list.
Field definitions are in Agent files and Mesh manifest.
An omitted subscribe joins no channels at boot. It does not remove an explicit allowSubscribe grant.
An agent whose resolved read ACL is empty receives no channel live-read or history-read grants.
The agent remains on the roster and reachable by DM and anycast.
An agent with a read grant but no active subscription can still read retained history, join a permitted channel, or receive an authorized @mention.
general has no special status. A subscription to it must be explicit.
The default persona seeded by cotal setup starts with subscribe: [], allowSubscribe: [">"], and allowPublish: [">"].
It joins no channel at boot but may join, read, and post channels on demand.
cotal_send without channel uses the first concrete channel in the agent's active subscriptions that its post ACL permits.
If none qualifies, the tool refuses an omitted channel. A read-only subscription or a wildcard subscription cannot provide that default.
On an open mesh, which does not enforce post ACLs, the default is the first concrete active subscription.
Delivery classes
Each channel has a deliveryClass of live or durable, defined in SPEC §4 and §7.
The effective class is the channel override, then the space default, then durable if neither is set.
| Class | Delivery |
|---|---|
live | At-most-once delivery to peers subscribed at publish time. |
durable | Live delivery plus a per-member backstop for busy or offline members. Delivery is at-least-once within the space's history limit and the membership interval. The delivery daemon provides the backstop. |
On a live channel, an @mention also writes a durable copy for each mentioned target whose current read ACL covers the channel.
The target does not need an active subscription. A mention cannot deliver content outside the target's read ACL.
Receivers discard non-object JSON payloads, including null, before reading message fields.
A malformed payload does not stop later live delivery or retained-message reads.
Membership changes
An agent joins a channel's live subscription without a manager when its read ACL permits the channel. The broker enforces the ACL on each subscription. A join succeeds only after the broker confirms the subscription. Leaving closes that subscription. See SPEC §7.
A durable join also requests durable membership from the delivery daemon, which writes the privileged membership record.
If that step fails, the live subscription remains active and the join reports that the durable backstop is unavailable.
It is not a successful durable join.
Durable delivery covers messages after the join cursor and through the leave cursor, both captured from the CHAT stream frontier. A pre-leave queued copy remains eligible. Copies after the leave cursor are excluded, and a rejoin takes a new join cursor. Messages published during the gap remain accessible through ACL-bounded history but do not enter the new membership's durable backstop.
Leaving does not revoke allowSubscribe. The agent may still read retained history, rejoin, or receive an authorized live-channel mention.
Leaving the last channel is allowed. The agent remains on the mesh with no active channel subscription or default send channel.
Replay
The channel registry's replay flag controls history backfill on join.
The effective value is the channel override, then the space default, then true if neither is set.
replayWindow bounds the backfill horizon, for example "24h". It uses the channel override or the space default.
Without either window, backfill covers the retained history. See SPEC §7.
Replay does not restrict history access
replay: false suppresses automatic history backfill on join. It does not restrict an ACL holder's on-demand reads of retained content.
The read ACL is the confidentiality boundary. Confidential content uses a DM or anycast.
When preservation records the CHAT stream frontier, a managed seat resumed after cotal down --preserve-state and cotal up backfills its boot channels only after that cut, subject to replay and retention.
If the manager lacks local space authority to read the frontier, it warns that retained seats replay their channel windows on resume.
A fresh joiner backfills the retained window. A channel joined later in the resumed session also uses the normal join window.
How long posts are kept
A space keeps a channel post until its sender has posted 1000 newer ones on that channel, with no
age limit (SPEC §8). One limit holds for every channel in the
space. An operator sets an age, a different per-sender count or both with
cotal history limit, and the broker then deletes a post past either bound. A
lower limit deletes the posts it no longer keeps at once. cotal up never changes the limit.
replayWindow only bounds what a joiner is backfilled, so it cannot reach further back than the
limit.
The per-sender count means a busy sender's oldest posts on a channel go first, while other senders' posts from the same time stay. A durable member's copy of a post, once the delivery daemon has taken it, is not removed by the chat limit.
Access examples
The examples use Agent files and Mesh manifest fields.
| Access | Configuration | Reference |
|---|---|---|
| Read without posting | Agent frontmatter subscribe: [general] with allowPublish omitted grants a read-only subscription. allowSubscribe can grant read access without a boot subscription. | SPEC §9 |
| Announcements with no agent writers | Manifest channel allowPublish: [] grants no agent permission to post there. cotal send can post with an authorized sending credential. The static operator credential permits posting as itself. A user bearer remains bounded by its actor's post grants. | Mesh manifest |
| Read a subtree | Agent frontmatter allowSubscribe: [team.>] permits reads of concrete channels under team. without advance enumeration. | SPEC §3, §9 |
Read review and its descendants | Agent frontmatter allowSubscribe: [review, review.>] includes the top channel. review.> alone matches only deeper channels. | SPEC §9 |
| Suppress join backfill | Channel registry replay: false omits history from a joiner's initial context. ACL holders retain on-demand history access. | SPEC §7 |
Wildcards
A publish target is always concrete, with no * or >.
Agent-file subscriptions and ACLs may contain wildcard patterns. team.* matches one level below team, and team.> matches one or more levels below it.
A bare > read grant permits reading all channel chat in the space.
A wildcard in subscribe actively listens to matching channels. The same wildcard in allowSubscribe grants read access without creating those subscriptions.
Manifest channel names must be concrete. Their permission lists contain agent names, not channel patterns.
The wire rules are in SPEC §3 and §9.
One post to a channel that team.> matches, such as team.announce, reaches the agents following team.>.
Those connected get it at once, and one that is away gets a stored copy if that channel is durable, as families describes.
A follower of a pattern with *, such as team.*, that is offline when you post may never see it.
To reach several channels, post to each by name.
cotal_send refuses a pattern, and its refusal names both ways.
Families
A family is a channel pattern of concrete segments and a final >, such as team.> or team.ci.>.
Following team.>, with cotal_join team.> or subscribe: [team.>] in the agent file, covers every channel under team, including channels created later.
It does not cover team itself.
A family follow holds a durable membership of its own, so an agent that is away still gets what it follows:
- On a
durablechannel under the family, each post is stored for the agent and delivered when it returns. - On a
livechannel under it, a post reaches the agent only while it is connected. - Stored copies start at the join. Earlier posts are history.
- An agent that also joined a channel under the family by name still gets one copy of each post. The copies collapse within the two-hour duplicate window, so a copy written again after it can be stored twice.
- Leaving the family ends its stored copies. Muting a channel under it stays a per-channel choice.
A family needs one allowSubscribe entry that contains it, such as team.> or >.
perm.* does not contain perm.>, because perm.> also matches channels two levels down, so cotal_join perm.> is refused under it, and so is an agent file with subscribe: [perm.>] and allowSubscribe: [perm.*].
The root > and any pattern with *, such as team.* or team.*.>, can be followed live, but they hold no durable membership and get no stored copies.
cotal_join says so when it joins one.
Each agent keeps at most 1000 stored copies.
When an agent returns to a full store, earlier copies may have been dropped, and its next cotal_inbox reply says so once.
cotal history <channel> may still hold those posts, within the space's history limit.
cotal_channels lists a followed family as its own row, with the state of its stored copies.
A family nested in another, such as team.ci.> under team.>, shows stored copies on while the outer family's membership holds, since that membership stores its posts too.
A channel read only through the root > or a pattern with * shows no stored-copy state.
When the broker stops an agent reading a family, cotal_channels shows the family as no longer followed until its stored copies are closed.
SPEC §7 defines the family rule.