Realtime messaging backend
Realtime channels. Every event, in order.
Public, private and presence channels over WebSockets. Publish from your backend with a signed REST call. Every event gets a sequence number, so a client that reconnects replays exactly what it missed.
const client = new MessageGo("wss://your-host", {
app_id: "demo-app",
token: jwt, // issued by your backend
});
await client.connect();
const room = await client.subscribe("presence:room-1");
room.bind("new_message", ({ seq, data }) => {
console.log(seq, data.body);
});
room.onJoin((joins) => console.log(Object.keys(joins)));curl -X POST https://your-host/apps/demo-app/events \
-H "Content-Type: application/json" \
-H "X-MessageGo-Signature: sha256=<hmac>" \
-H "Idempotency-Key: msg-9f3a" \
-d '{"event":"new_message",
"channel":"presence:room-1",
"data":{"body":"hello"}}'
# HMAC-SHA256 of the raw body, signed with your app secret.{
"event_id": "evt_…",
"seq": 12345,
"event": "new_message",
"data": { "body": "hello" },
"created_at": "2026-07-12T12:00:00Z"
}
// seq increases by 1 per channel. A jump means events were
// missed; the SDK resumes from lastSeq() on reconnect.Built on proven parts, not a black box.
- Elixir
- Phoenix Channels
- PostgreSQL
- WebSocket
- JWT
How it works
Connections drop. Your users shouldn't notice.
Phones change networks and tabs go to sleep. A realtime layer that only delivers what is live leaves a gap each time that happens. MessageGo stores and numbers every event, so a returning client asks for everything after its last sequence number and gets it, in order, before live events resume.
Capabilities
Five properties of the design, not features bolted on.
- 01
Three channel types
public:*, private:* and presence:*. Private and presence channels need a short-lived channel token; presence tracks who joins and leaves.
See the code - 02
Ordered, durable events
Every event gets an event_id, a per-channel seq and a timestamp, and is stored before it is delivered.
See an event - 03
Replay on reconnect
The SDK rejoins with from_seq and missed events arrive in order. At-least-once delivery within the retention window (72 hours by default).
See how it works - 04
Signed publishing
Your backend publishes with an HMAC-SHA256 signature. An Idempotency-Key header stops retries from creating duplicate events.
See a publish call - 05
Any client
A JavaScript SDK, plus a documented wire protocol for building React Native, Flutter, iOS and Android clients.
Join the waitlist
In code
From first connection to resumed stream, in a few lines.
- 01
Authorize
Your backend issues a connection JWT with a user_id and an exp. Private channels trade it for a channel token automatically.
- 02
Subscribe
One call joins a channel and binds handlers. Each payload arrives with its seq.
- 03
Resume
After a long gap, pull history from the last seq you saw, or let the SDK replay it on reconnect.
const sub = await client.subscribe("public:general");
sub.bind("new_message", render);
// The SDK reconnects and replays on its own. To catch up
// manually after a long offline period:
const missed = await client.fetchHistory("public:general", {
afterSeq: sub.lastSeq(),
limit: 50,
});
missed.forEach((e) => render(e.data));
// each e has: event_id, seq, event, data, created_atBe first on MessageGo.
We're finishing the platform. Leave your email and we'll tell you when early access opens.
We'll only use your email to contact you about MessageGo.