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
- Stored.
send()writes the message to the peer's outbox (in its store) and resolves with itsid. If you get an id back, the message is safely queued. - Sent. The SDK opens a session with the recipient if needed and sends the message.
- Acknowledged. The recipient emits
message, then replies with an acknowledgement. When it arrives, the message leaves the outbox and you get adeliveredevent.
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.
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 retried | Yes | No |
| Delivery receipt | Yes | No |
| Works while offline | Queued | Throws UNREACHABLE |
| Best for | Chat messages, commands, anything that must arrive | Presence, cursors, typing indicators, signaling |
See Custom protocols for the second kind.