Packages: chanreg/, api/. The channel protocol connects external adapter daemons to the router.
Every channel adapter is an HTTP server. The router is an HTTP server. They communicate through three endpoints:
| Direction | Endpoint | Purpose |
|---|---|---|
| Adapter โ Router | POST /v1/channels/register | Register on startup, receive session token |
| Adapter โ Router | POST /v1/messages | Forward inbound platform message to router |
| Router โ Adapter | POST /send | Deliver outbound message to platform |
The router also calls the adapter's GET /health endpoint every 30 seconds.
On startup, each adapter POSTs to /v1/channels/register:
{
"name": "teled",
"callbackURL": "http://teled:8081",
"jidPrefixes": ["telegram:"],
"capabilities": ["send", "send_file", "typing"],
"secret": "<CHANNEL_SECRET>"
}
The router returns a session token. The adapter includes this token as a bearer token on all subsequent POST /v1/messages calls.
receive_only: true in capabilities means the adapter only receives messages from the platform โ the router never calls its /send endpoint. Used by onbod and dashd.
Platform event โ Adapter
โ POST /v1/messages (with session token)
โ api handler โ store.PutMessage
โ gateway poll loop picks up on next tick
The inbound message body (InboundMsg from chanlib):
{
"jid": "telegram:123456789",
"sender": "alice",
"content": "hello",
"replyTo": "optional message ID being replied to",
"mediaURL": "optional media attachment URL",
"verb": "message" // or "reaction", "typing", etc.
}
gateway โ HTTPChannel.Send(jid, text, replyTo)
โ POST <callbackURL>/send
โ { "jid": "...", "text": "...", "replyTo": "..." }
โ 200 = delivered to platform
The router signs outbound requests with the shared CHANNEL_SECRET (same secret used for registration). The adapter verifies the signature before forwarding to the platform.
GET <callbackURL>/health every 30 secondsHTTPChannel.outbox (in-memory)| Call | Auth mechanism |
|---|---|
| Adapter โ Router registration | Shared secret (CHANNEL_SECRET) in request body |
| Adapter โ Router messages | Session token (from registration response) as bearer token |
| Router โ Adapter /send | Shared secret as X-Channel-Secret header |
| Router โ Adapter /health | No auth (read-only liveness check) |
Package: chanlib/. All Go adapter daemons import this for:
RouterClient: wraps the 3-endpoint HTTP calls with session token management and retry logicInboundMsg: canonical inbound message structX-Channel-Secret on inbound /send calls from the routerAdapters can deregister explicitly via POST /v1/channels/deregister (with session token). On reconnection, they re-register and receive a new session token. The outbox queued during downtime drains automatically.
If an adapter process restarts, its registration is lost. The adapter must re-register on startup. The router treats the new registration as a fresh channel instance.