Docs

How Foony Realtime works

The architecture behind ordering, exactly once delivery, and reconnect recovery.

This page explains the design that backs the guarantees the SDKs advertise: per-channel ordering, effectively exactly once delivery, and gap-free reconnects. You don’t need any of it to build on Foony, but it’s the honest answer to “why can I trust this”.

The path of a message

Stateless edge servers terminate WebSocket and REST traffic. A publish flows from the edge to a durable log backed by NATS JetStream, and fans out from there to every edge server that has a subscriber on the channel, which writes it to its local sockets. Edges hold no channel state of their own, so any client can connect to any edge and pods can come and go without draining channels.

Ordering

Every durable message on a channel gets a serial, a per-channel counter with no gaps. Serials are assigned by exactly one server at a time per group of channels, under a short-lived lease, and the write to the log is fenced so a server that lost its lease during a failover can’t slip a stale write in. One writer per channel is what makes the ordering guarantee unconditional rather than probabilistic: subscribers receive messages in serial order because there’s no second author to race.

Exactly once delivery

Exactly once falls out of two halves:

  • Into the log, exactly once. Every publish carries a stable client-assigned message id, and the log deduplicates on it. When an SDK resends a publish because the connection dropped before the ack arrived, the resend matches the original record and returns its serial instead of appending twice.
  • Out to the app, exactly once. Deliveries are at-least-once on the wire, and the SDK deduplicates by message id and checks serials for contiguity, so your listeners see each message once, in order.

The exception is ephemeral messages, which skip the log entirely and are at-most-once by design.

Reconnects and the two storage tiers

When a client reconnects, each of its channels resumes from the last serial it saw, and the service replays exactly the gap. What’s replayable depends on the channel’s rule:

  • Live-only channels keep a couple of minutes of messages, enough to heal ordinary network blips.
  • Stored channels keep a hot window in the log and continuously seal older segments into object storage, where history and deep backfills read from for up to the retention period. Retention works by archiving first and purging after, so a slow archiver grows the hot window instead of losing messages.

A client that was away longer than its channel retains messages gets an explicit discontinuity signal (resumed: false) rather than a silent gap, as described in Channels.

Presence

Presence membership lives in a replicated key-value store with per-entry expiry, so a member whose server dies is removed by TTL even though no leave was ever sent. Short disconnects hold membership for a grace window, which is why other members don’t see a leave-and-rejoin flicker on every network blip, per Presence.

What this means for your design

  • Order within one channel is guaranteed. Order across channels is not, so state that must be ordered belongs on one channel.
  • Publishes are safe to retry, and the SDKs already do. Building your own retry on top costs nothing but is unnecessary.
  • Retention is the recovery budget. Pick channel rules so that “how long can a client be offline and recover seamlessly” matches your product’s answer.