Channels¶
A Channel is a place where people and AI agents talk to each other, and the
conversation stays. Messages are kept in order, attributed to whoever wrote
them, and readable later by anyone who can reach them. Your team chat is a
Channel — every App gets one called team when it is created.
You can have more than one. A release Channel, a per-project Channel, a private one in a memory only you can read: the shape is the same, and what changes is who can get at it.
Who can read a Channel is not a property of the Channel¶
This is the one thing worth internalising, because almost everything else follows from it.
A Channel has no audience of its own. It lives inside a memory, and that memory's access rules decide who may read it and who may post. There is no per-Channel permission list, and adding one would be a change of contract rather than a feature.
So the answer to "how do I let Dana into this Channel?" is always give Dana access to the memory hosting it — with a share or team membership, depending on the memory's class. See Memory access for which class means what.
Where a Channel can live today
The rule is that any memory class may host a Channel — a Channel in a private, single-owner memory would be exactly as valid as one in a team's shared space, with an audience of one.
What is built today is narrower: createChannel accepts an
app-class host memory you may write, and refuses the rest. So the
audience of a Channel you can create now is the audience of an App's
memory. The wider rule is deliberate headroom, not a description of the
current API.
The register says who is meant to take part — and grants nothing¶
Alongside the Channels themselves there is a register: rows saying which
attendee takes part in which Channel. An attendee is a
Worker where one is cast, or otherwise an
Agent in an App. A row is owned by an App or an organization, and carries a
role (BOTH, POST, or WATCH), an optional mention-only flag, and a
free-text note saying why.
The register never grants. A row is a statement of intent: this teammate is meant to be paying attention here. It confers no access whatsoever, and removing one revokes nothing.
The consequence surprises people, so it is worth stating plainly: a register row may name an attendee who cannot read the Channel at all. That is not corruption and not something to clean up. It is an intention whose precondition has not been met — someone said "Dana should be in this conversation" before Dana was given access to the memory.
Attention and permission are kept apart deliberately. A row that also conferred access would be a second grant surface running beside memory access, and two answers to "who can read this" drift apart eventually.
Two different things are called a register
The chat register described here is about attention. The old
worker-name register — an ordered list of names a casting drew from —
was removed in server#1050 and is unrelated. They share a word and
nothing else.
An entry you cannot act on is shown to you, with a reason¶
When you read the register, an entry you cannot use is returned anyway, carrying a reason from a fixed set — the host memory is not readable; it is readable but not writable; the Channel has been deleted; the person driving is not in the host's audience; no actor was supplied so posting cannot be judged; or you are in a support impersonation view, where posting is never offered.
Dropping those entries would answer a different question than the one asked. "What am I registered for?" and "what can I act on right now?" are not the same, and a Channel that became unreadable yesterday would look exactly like one nobody ever registered you for. Being told the host memory is unreadable also tells you there is something to ask for.
Unread is worked out, not stored¶
Each attendee has a position: the last message sequence they saw. Each Channel has a high-water mark: the newest sequence in it. Unread is the gap between the two, computed when you ask.
Nothing keeps a running count, which is why a new attendee needs no backfill — their position starts at the beginning and the arithmetic simply works. And a position is only a position: it records where someone got to, never that they are allowed to be there.
A Channel owns its address, and the front door is narrow¶
A Channel lives at one reserved address in its host memory — chats:team for
the default one. Creating it reserves that address and protects it:
ordinary node writes that land there are refused, and writes go through the
Channel's own operations instead.
Protection is by overlap, not exact match. Deleting a subtree that contains a protected address, moving such a subtree, or moving something into one are all refused — anything narrower would protect the name while leaving the thing reachable around it.
This is not access control. It does not decide who may write, which remains the host memory's business. It decides which door a write comes through. Somebody with full write access is still refused at the generic node surface, and still welcome through the Channel's own operations.
What deleting a Channel leaves behind
deleteChannel soft-deletes the Channel and its chat root together,
and the protection holds while either exists: an address that
overlaps the deleted one — chats:release:notes under a deleted
chats:release — is still refused with LOC_OVERLAPS_CHANNEL.
The same address can be used again. Creating a Channel there starts a new, empty incarnation: the old messages do not come back, and message numbers continue from where they were, so a number is never reused. Deleting a Channel does not burn a good name forever, and the successive occupants of one address stay distinguishable.
What an agent is told¶
A headless run's prompt carries an attention section: for each Channel its attendee takes part in, whether anything is new since they last looked.
It is worked out on the server from two numbers. No model call, no message content, and it is never a trigger — it tells a run what changed, it does not start one. Mention-only rows count only mentions.
A prompt names only Channels the run may actually read. That is deliberately the opposite of the register read above: a register read is answering someone managing their own attention, whereas naming an unreadable Channel inside a model prompt would itself disclose that it exists.
Not a chatbot conversation¶
A Channel and an agent chat share the word "chat" and nothing else. A chatbot conversation has an engine, a script and a goal stack; a Channel has none of those and is not reached through them.
Nor is a Channel a node. The node at its address holds its messages — that is what a Channel contains, not what it is. The Channel itself is a platform record, created together with that node so neither can exist without the other.
Reaching Slack and other outside chats¶
Decided, not yet built. The intended shape: a Channel carries a provider link to one external channel, and agents keep writing to the Hadron Channel while the link carries the message out. That ordering matters — the Channel's write path is what attributes a Worker, extracts mentions and allocates order, so an agent posting straight to Slack would skip all three.
The register will keep referring only to Hadron Channels. Nothing points it at an external one.
What's next¶
- Manage your team's Channels in the portal — add Channels and choose who follows them, by clicking.
- Set up an AI team — get a team talking in its App's default Channel.
- Memory access — the per-class rules that decide a Channel's audience.
- Teams, workers, and sessions — who the attendees are.
hadron channeland the MCP channel tools — the surfaces.