Skip to main content

Message delivery

peer.send() gives you durable, at-least-once delivery with de-duplication. Here's what happens to a message.

The life of a message​

  1. Stored. send() writes the message to the peer's outbox (in its store) and resolves with its id. If you get an id back, the message is safely queued.
  2. Sent. The SDK opens a session with the recipient if needed and sends the message.
  3. Acknowledged. The recipient emits message, then replies with an acknowledgement. When it arrives, the message leaves the outbox and you get a delivered event.
const { id } = await peer.send(bobDid, { text: "See you at 3" });

peer.on("delivered", (receipt) => {
if (receipt.id === id) markAsDelivered(id);
});

When the recipient is offline​

If the recipient can't be reached, the message stays in the outbox and delivery is retried:

  • On a timer, with exponential backoff: 2 s, 4 s, 8 s… capped at 5 minutes. Configure it with retry: { initialMs, maxMs }.
  • Whenever a session with the recipient opens, from either side. If Bob comes back and connects to you, everything you queued for him goes out immediately.
  • When your peer restarts. With a persistent store, createPeer() resumes delivery of anything left in the outbox.
Make returning peers say hello

Connecting to your contacts when your app starts (peer.connect(did) for each) shows who's online. It also lets each of them deliver what they queued for you, without waiting for their next retry.

Duplicates​

If an acknowledgement is lost, say because the connection dropped right after the message arrived, the sender sends the message again. Receivers remember the ids of recent messages (the last 2,000) and ignore repeats, while still acknowledging them. So you get each message once in normal use.

If your app needs exactly-once semantics across restarts, de-duplicate on message.id in your own storage too.

What the SDK doesn't keep​

The SDK stores undelivered outgoing messages only. It doesn't keep a conversation history: when a message arrives, it's handed to you through the message event, and storing it is up to your app. That keeps the SDK small and lets you choose your own retention, search and sync.

Messages vs. protocols​

peer.send()peer.protocol(name).send()
Stored and retriedYesNo
Delivery receiptYesNo
Works while offlineQueuedThrows UNREACHABLE
Best forChat messages, commands, anything that must arrivePresence, cursors, typing indicators, signaling

See Custom protocols for the second kind.