arizuko

arizukoconcepts › addressing

jid

The Slack pane was the last adapter; the tour ends one layer beneath all of them, where it began — routing. The JID is the cross-cutting addressing substrate every adapter shares. Telegram chats, web chats, voice notes, webhooks: every adapter you’ve met names its chats and senders the same way, and this is that name. Every address has the shape <platform>:<rest>, so a single routing rule like chat_jid=telegram:group/* matches every Telegram group and nothing else. One address shape lets routes and grants ignore each platform’s quirks.

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 platform has its own odd IDs.

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 routd 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 match with Go’s path.Match. * grabs any run of characters but stops at /, so each slash-separated segment stays separate; ? 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.

in Go

Inside the codebase a JID is just a string. Two helpers in core/types.go split it: core.JidPlatform(jid) returns everything before the first colon, and core.JidRoom(jid) returns everything after. Matching is Go’s path.Match against the route’s glob — the same call the router runs in router.RouteMatches. There is no parse step and no separate chat-vs-sender type; both are addresses routd only ever glob-matches.

per-platform shapes belong to adapters

routd treats everything after platform: as an opaque string and only ever glob-matches it. Each adapter (teled/, 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; routd and the routes table don’t move.

colons in the rest

core.JidPlatform splits on the first colon and hands back everything before it; the rest stays untouched. So an ID that itself contains a colon doesn’t break the platform/rest split. A Bluesky DID is a did:plc:… string — the adapter percent-encodes its colons (bluesky:user/did%3Aplc%3A…) so a glob never trips on them.

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.