History and recovery
Use Protocol V2 stream continuity, replay buffers, channel history, presence history, rewind, and mutable messages.
Sockudo separates connection recovery from durable history. Recovery keeps a connection continuous after a short interruption. History is an application feature for reading and reconstructing past state.
Recovery
Protocol V2 broadcasts carry stream metadata:
{
"event": "order.updated",
"channel": "orders",
"data": { "id": "ord_123" },
"message_id": "msg_01HX",
"stream_id": "orders:main",
"serial": 42
}Clients store recovery positions and provide them during reconnect. Sockudo replays missed messages if continuity can be proven.
For V2 clients, the subscription acknowledgement carries the first recovery position when recovery is enabled. Keep that position even before the channel receives any application event. A reconnect can then resume from the subscribed-but-idle checkpoint and replay messages published while the client was disconnected.
const client = new Sockudo("app-key", {
wsHost: "127.0.0.1",
wsPort: 6001,
forceTLS: false,
protocolVersion: 2,
connectionRecovery: true,
});
client.bind("sockudo:resume_success", (payload) => {
console.log(payload.recovered, payload.failed);
});If a channel emits sockudo:resume_failed with code: "position_expired", the hot replay buffer and durable recovery window could not prove continuity. Resubscribe, then backfill with V2 channel history using until_attach: true so the history page is bounded at the subscription's attach serial.
Recovery is two-tier:
- Hot replay uses the bounded per-channel replay buffer.
- Durable recovery uses
HistoryStorewhenstream_id, serial bounds, and retention prove continuity.
If the durable stream is degraded or reset-required, Sockudo fails closed instead of pretending the stream is continuous.
Rewind
Subscribe-time rewind asks Sockudo to send recent history when a client joins:
const channel = client.subscribe("orders", {
rewind: { seconds: 30 },
});
channel.bind("sockudo:rewind_complete", (payload) => {
console.log(payload.historical_count, payload.complete);
});Durable channel history
Server SDKs expose app-key channel history helpers. Use signed app-key HTTP history for backend jobs, administrative reads, and server SDKs.
page = sockudo.get_channel_history(
"orders",
HistoryParams(limit=50, direction="newest_first"),
)Opaque cursors make storage implementation details private. Store the cursor as a token and pass it back unchanged.
Protocol V2 clients use WebSocket channel_history frames instead of direct HTTP history. Token-authenticated clients need the history capability for the requested channel. The existing app-key HTTP endpoint remains server-only; there is no client-token HTTP history surface in v1.
{
"event": "sockudo:channel_history",
"data": {
"channel": "orders",
"limit": 100,
"direction": "backwards",
"cursor": null,
"start_serial": 1,
"end_serial": 100,
"until_attach": true
}
}The response uses the same history payload shape as app-key HTTP history: items, direction, limit, has_more, next_cursor, bounds, continuity, and stream_state. Limits are capped at 1000 and may be lowered by channel policy. direction defaults to backwards/newest-first. until_attach: true bounds results to history_serial <= attach_serial, while live delivery after subscribe continues above that attach serial. Mutable messages are returned as the latest aggregated V2 message, not raw operation log rows.
until_attach is the late-join gaplessness rule: the history page stops at the serial captured when
the subscription succeeded, and live fanout continues above that serial. Clients should reduce
history first, then live messages.
Presence history
Presence history records joins, leaves, causes, and continuity metadata. It is different from the current presence member list.
const channel = client.subscribe("presence-lobby");
const page = await channel.history({
limit: 50,
direction: "newest_first",
});
const snapshot = await channel.snapshot({ atSerial: 4 });Client helpers call your backend proxy. They do not sign Sockudo REST requests directly.
Mutable messages
Protocol V2 mutable messages use action events:
sockudo:message.updatesockudo:message.deletesockudo:message.append
Clients reduce those events into local state. Server SDKs can fetch the latest visible state or list preserved versions. When a V2 subscription uses rewind, historical rows that point at mutable messages are delivered as the latest visible version, so late joiners see accumulated appended content rather than only the original create payload.
const latest = await channel.getMessage("42");
const versions = await channel.getMessageVersions("42", {
limit: 20,
direction: "oldest_first",
});Push and history
Push notifications should include stable identifiers that let the app fetch authoritative state after the user opens the notification.
{
"title": "Order updated",
"body": "Order ord_123 is now packed",
"data": {
"channel": "orders",
"message_serial": "42",
"order_id": "ord_123"
}
}Do not encode the entire durable state into the push payload. Use push to wake the app, then read the latest message or application API state.