Skip to content

Channels and DMs

Everything is a channel underneath, with a type: public, private, dm, or group_dm. That is why threads, reactions, files, search, and calls work the same everywhere, rather than direct messages being a second implementation with its own gaps.

Public channels are visible to the whole workspace and anyone can join. Private channels are invisible to non-members and only an existing member can add someone. Permission filtering happens in SQL, including in search, so a private channel cannot leak through a result list.

Channels can be archived, which keeps the history readable but stops new messages. Deleting is deliberately not offered: archiving is almost always what people actually mean.

A direct message is created by picking people rather than naming a channel. The same conversation between the same set of people is reused rather than duplicated, so you cannot end up with two threads with the same person.

A thread is a message with replies, not a separate object. Any message can become a thread root. The root keeps a reply count and the time of the last reply, so channel lists can show thread activity without a second query.

Typing @ offers the people in the channel. A mention marks the message for that person and increments their badge.

Unread state is a comparison rather than a per-user write: the client checks whether the channel’s newest message id is greater than the last one you read. Because message ids are time-ordered ULIDs, that is a string comparison and costs nothing. Mention counts are the exception and are tracked server side, because they need to be exact.

Each channel has one shared markdown note, edited in place with last write wins. It suits the thing every channel accumulates: the current runbook, the list of links, the on-call rota.