What is Converger?
Converger is a self-hosted, multi-tenant channel hub written in Elixir on Phoenix, Ecto/Postgres and Oban. It connects communication channels (WebSocket clients, generic HTTP webhooks, WhatsApp through Meta or Infobip, and more to come) to each other. A message that enters through any channel is stored once as an activity in a conversation, given a strictly increasing per-conversation sequence number, and delivered to every channel that should receive it. Each delivery is tracked, retried and, when retries run out, dead-lettered.
The model in one paragraph
A tenant owns channels. Each channel has a type (its adapter: webhook, websocket, whatsapp_meta, whatsapp_infobip, slack, telegram, email, sms_twilio, sms_vonage, echo) and a mode (inbound, outbound or duplex). Messages arrive through the REST API, the WebSocket sockets or a channel's inbound webhook. They are written as activities in a conversation that belongs to one channel. Inbound messages from providers that never send a Converger conversation id are matched to a conversation through the external participant, such as a WhatsApp phone number or a Slack thread. Each activity gets a seq (1, 2, 3, ...) that is allocated under a row lock on the conversation. In the same database transaction Converger enqueues one delivery job per target channel: the conversation's own channel, if it can send, plus the targets of any enabled routing rules. Each delivery passes through the target channel's middleware chain (transformations) and is then handed to the adapter. It ends as sent, delivered or read, or as failed once its retries run out.
See Concepts for every entity, and Architecture for how the processes fit together.
High-level flow
Design goals
WebSocket-first, with its own protocol
WebSocket is meant to be Converger's primary, first-class channel. Two socket stacks exist today:
/socket/converger(ConvergerSocket, topicsconverger:conversation:<id>and, for agent consoles,converger:channel:<id>), the client API inspired by Bot Framework Direct Line, authenticated with a Converger token. On join it replays activities after an opaque watermark, clients send activities on it withpostActivityand acknowledge deliveries withack. It is the single client socket stack (#23)./socket(UserSocket, topicconversation:<id>), authenticated with a conversation token. Deprecated: new clients use/socket/converger; see migrating from the legacy surfaces.
Both receive the same canonical activity payload as REST (see ADR-0004). The direction is to merge them into one documented wire protocol, the Converger Protocol v1 (spec in progress, #21, #63). It will be wire-compatible with mekik/1 (ADR-0024) and will add client message ids with server acks, receipts, typing and presence. That work is tracked in the v3.0 epic #58. The websocket channel type is a duplex channel adapter: clients send over the socket through the same inbound path as webhooks, and deliveries to the channel are tracked, can be routing rule targets and wait for a client while none is connected (#22, ADR-0033).
Zero data loss
An acknowledged message must never be lost. These mechanisms are in place:
| Mechanism | What it guarantees | Where |
|---|---|---|
| Transactional outbox | The activity row, its seq and its Oban delivery jobs commit in one database transaction. A crash cannot leave a committed activity without its deliveries. If enqueueing fails, the activity is rolled back and the client gets 503. | ADR-0001, activities.ex |
| Single delivery path | Every external delivery, whatever its origin (REST, WebSocket, inbound), goes through the pipeline. That means middleware, the adapter and a delivery record. | ADR-0003 |
| Idempotency | An x-idempotency-key header (REST) or the provider message id (inbound) is unique per conversation. A retried request returns the existing activity instead of creating a second one. Inbound batches are idempotent per message. | ADR-0015 |
Per-conversation seq | Strict, gap-free ordering that does not depend on clocks, and opaque watermarks for resuming. | ADR-0006 |
| Retries with backoff | Per-channel retry policy (max_attempts, backoff, base_ms, max_ms, timeout_ms). A provider Retry-After is honored. Oban Lifeline rescues jobs orphaned by a crashed node. | ADR-0019 |
| Dead letters | A delivery that runs out of attempts, gets a permanent provider error, or is halted by middleware is marked failed with last_error, and telemetry fires. | Deliveries |
Broadway (Converger.Pipeline.Broadway) and inline (Converger.Pipeline.Inline) pipeline backends also exist, for throughput experiments and for tests. Only the default Oban backend is durable (ADR-0002).
What Converger is
- A message router and store for conversations that span channels, with per-tenant isolation (API keys, channel secrets encrypted at rest, per-tenant rate limits).
- A delivery tracker: every outbound message has a delivery record with attempts, errors and provider receipts (
sent,delivered,read). - An integration surface: a server-to-server REST API (tenant API key), a client API with short-lived tokens and WebSocket streaming, inbound webhooks for providers, an admin panel (
/admin) and a tenant portal (/portal). - Operable: an Elixir release with separate migrations, Prometheus metrics, OpenTelemetry traces, audit logs and channel health checks.
What Converger is not
- Not a bot framework or AI agent runtime. It carries messages to and from bots. It does not decide what to answer. The
echochannel is a test helper, not a bot. - Not a chat UI. The admin and portal show transcripts for operators. End-user widgets connect over the WebSocket or REST client API.
- Not a general-purpose message broker. Activities are conversation-scoped and stored in Postgres. Kafka, RabbitMQ, NATS JetStream, Redis Streams and MQTT can be broker channels: integration endpoints, not the storage of conversations.
- Not finished. A management API (#51) and the bot/agent SDKs (#67) are on the roadmap. The headless TypeScript client
@converger/client(#46) and the server-side SDKs for Python, Go and Elixir (Client SDKs, #47) exist.
Where to go next
- Getting started: run Converger with docker compose or locally, and send a first message end to end.
- Concepts overview: tenants, channels, conversations, participants, activities, deliveries, routing rules and middleware.
- Architecture overview: processes, supervision, the activity flow and the delivery pipeline.
- Architecture decision records: why things are built the way they are.
- Roadmap: what is done and what is next.
License
Converger is open source under the MIT License (ADR-0073). Contributions are welcome: see Contributing.