jid

addresses for chats and senders · ← concepts

Every chat and every sender has an address that looks the same across every platform: <platform>:<rest>. A routing rule like chat_jid=telegram:group/* matches every Telegram group, and nothing else.

the shape

Each platform names things differently. Telegram chat IDs are signed integers, where a negative number means group. WhatsApp suffixes a server name like @g.us for groups and @s.whatsapp.net for DMs. Reddit uses prefixes like t1_, t2_, t3_ for comments, users, and submissions. Each format is its own little puzzle.

arizuko writes one shape over all of them. The platform goes before the colon. Everything after the colon is the platform’s business, kept as an opaque string the gateway only matches with globs:

telegram:user/123              # telegram DM with user 123
telegram:group/-100456         # telegram group, supergroup, or channel
discord:dm/456                 # discord DM channel
whatsapp:789@g.us              # whatsapp group
reddit:comment/abc             # reddit comment
mastodon:account/42            # mastodon account
web:<folder>[/<suffix>]        # browser chat at folder; URL token resolves to this JID
hook:<folder>/<source>[/...]   # webhook ingest: folder is the destination, source is the agent-chosen label

matching with globs

Routes use Go’s path.Match across / boundaries. So * grabs any run of characters except /, ? grabs one character, and [0-9] grabs a digit. The same syntax works on every field:

match='chat_jid=telegram:group/*'    # all telegram groups
match='chat_jid=discord:dm/*'        # all discord DMs
match='chat_jid=whatsapp:*@g.us'     # all whatsapp groups
match='sender=telegram:user/[0-9]*'  # any numeric telegram user

Because the platform sits in its own segment, you can write platform=telegram to mean “every Telegram chat, regardless of kind” without thinking about Telegram’s sign-bit trick.

typed in Go

Inside the codebase, core.ChatJID and core.UserJID are different types built on *url.URL. So if a function asks for a chat address and you hand it a sender address, the compiler refuses. Before this split they were both string and a swap silently routed messages to the wrong place.

per-platform shapes belong to adapters

The gateway treats everything after platform: as an opaque string and only ever glob-matches it. Each adapter (tgd/, discd/, whapd/, …) parses and constructs its own paths. Adding a new platform — or a new kind inside an existing platform — is a one-place change in that adapter; the gateway and the routes table don’t move.

URI-clean

The address is built on net/url, so an ID with reserved characters — a Bluesky DID contains a colon, for example — encodes safely without breaking the platform/rest split.

go deeper

Per-platform schemas, code types, migration history, and design rules: specs/5/S-jid-format.md. For how these addresses get matched into folders, see routing.