realtime-transport-contracts

Installation
SKILL.md

Realtime transport contracts

A realtime client's happy path (connect, receive, render) always works; the defects live in the seams around a dropped connection — reconnect, resume, dedupe, liveness, backpressure, and re-auth. Treat the connection as unreliable and every delta as potentially duplicated, reordered, or missing, and make each recovery step an explicit contract rather than a framework default.

Checklist (lead with the trap; details in references/)

transport-resilience

  1. Reconnect with capped exponential backoff + jitter, and stop on non-retryable closes. A fixed-interval (or un-jittered) retry turns a server restart into a synchronized thundering herd; AWS's measurements show full jitter — sleep = random(0, min(cap, base * 2**attempt)) — cuts both contention and total recovery time. Reset the attempt counter only after a connection stays up past a stability window, or a flapping server resets your backoff every few seconds. Do not blindly retry every close: retry transient codes (1006 abnormal, 1011 server error, 1012 restart, 1013 try-again-later); do not loop on auth/policy (1008, app-level 4xxx) or protocol errors (1002/1003). EventSource reconnects on its own, so the bug is the opposite — call .close() when you mean to stop, and know a 204 response tells the browser to stop retrying.
  2. Resume the stream, do not restart it. After reconnect you must re-subscribe to channels and resume from a position. SSE sends the Last-Event-ID header automatically — but only if the server emitted id: lines and you did not reset the id, and only if the server actually honors the header and replays from it. For WebSocket you own resume: send your last-applied server cursor/sequence on reconnect. Without resume you either gap (dropped events) or blindly re-request a fresh snapshot and double-count.
  3. Fold deltas defensively: dedupe, order, detect gaps. Deltas arrive duplicated (replay after reconnect), out of order, or gapped. Key every delta by a monotonic server sequence/version: drop seq <= lastApplied (idempotent apply), hold seq > expected in a small reorder buffer until the gap fills, and treat an unfillable gap as "resnapshot", never "interpolate". A Map/last-writer-wins merge that ignores sequence silently applies a stale delta over a newer one.
  4. Reconcile the initial snapshot against the live stream on one cursor. The snapshot (REST/SSR) and the stream must share a version axis — apply only deltas newer than the snapshot version and backfill the gap between them, or you double-apply or drop the overlap. (For the SSR/first-render side of this seam see ssr-hydration-mismatch.)
  5. Add heartbeat + liveness; an OPEN socket can be dead. readyState === OPEN (and even a live TCP socket) can be a zombie after a network drop or a proxy idle-timeout — the classic silent 1006. Browser JS cannot send protocol-level ping/pong (RFC 6455 frames are not exposed to the WebSocket API), so run an application-level ping and expect a pong within a timeout; on miss, close and reconnect. Gate reconnects on navigator.onLine/offline and Page Visibility so a backgrounded or offline tab does not spin.
  6. Bound the buffers; the send path has no automatic backpressure. WebSocket.bufferedAmount climbs when you send() faster than the socket drains; per the WHATWG spec a full send buffer forces the browser to close the connection. Poll bufferedAmount and throttle/coalesce outgoing messages above a threshold; bound the inbound queue too and coalesce or shed rather than growing memory unbounded (or adopt a stream-backpressured transport where supported).
  7. Refresh auth on a long-lived socket. Authentication at the handshake establishes identity only at that instant; hours later the token may be expired or revoked while the socket keeps processing messages. Refresh before expiry — in-band (push a fresh token over the open socket, server re-validates, no drop) or reconnect-with-new-token — and define an explicit reauth-required signal, since the protocol has no built-in "auth expired" response. (Refresh/rotation mechanics belong to frontend-auth-flow-contracts; EventSource cannot set an Authorization header, so tokens often ride in the URL/query and get logged — that placement is frontend-security-baseline.)
  8. Test the failure sequence, not the happy path. Assert: reconnect after a drop resumes with no gap and no duplicate; backoff carries jitter and caps; an auth/policy close is not retried; a missed heartbeat triggers reconnect; duplicated/out-of-order/gapped deltas converge to the same state; an expired token is refreshed without tearing the stream down.

Quick probes

Installs
1
GitHub Stars
1
First Seen
3 days ago
realtime-transport-contracts — voidmatcha/frontend-niche-skills