websocket-expert
Builds resilient WebSocket systems - heartbeat and dead-connection detection, reconnect with exponential backoff and jitter, message queuing with sequence numbers and backpressure limits, auth on connect, and pub/sub backplanes for horizontal scale. Use when someone asks "my WebSocket connections keep dropping", "how do I handle reconnects", "how do I scale WebSockets across servers", "clients miss messages after reconnecting", or is building chat, live dashboards, or collaborative editing. Do NOT use for one-way server push where SSE would do, for webhook ingestion - use webhook-receiver-hardener instead - or for REST/HTTP API shape - use api-design instead.
- License 许可
- MIT
- 最近更新
- 2026-10-14
- Stars
- ★ 25
- Forks
- 3
01 前言 Front Matter
- name
- websocket-expert
- metadata
- { "title": "WebSocket Expert" }
- description
- Builds resilient WebSocket systems - heartbeat and dead-connection detection, reconnect with exponential backoff and jitter, message queuing with sequence numbers and backpressure limits, auth on connect, and pub/sub backplanes for horizontal scale. Use when someone asks "my WebSocket connections keep dropping", "how do I handle reconnects", "how do I scale WebSockets across servers", "clients miss messages after reconnecting", or is building chat, live dashboards, or collaborative editing. Do NOT use for one-way server push where SSE would do, for webhook ingestion - use webhook-receiver-hardener instead - or for REST/HTTP API shape - use api-design instead.
03 SKILL.md 正文
WebSocket Expert
A WebSocket system's real workload is not messaging - it is surviving the network: connections die without firing close, mobile clients vanish and return, and servers restart under thousands of live sockets. The costly mistake this skill prevents is treating the happy path as the design: no heartbeat (dead connections accumulate until memory or fd limits blow), naive reconnect (a server blip triggers a thundering herd that finishes the outage), and no sequence numbers (clients silently miss messages forever after every reconnect).
Operating procedure
Step 1: Gather inputs
- Message pattern: server-push only (consider SSE first - simpler, proxy-friendly, auto-reconnecting), bidirectional, or fan-out to rooms.
- Delivery requirement: fire-and-forget (cursors, typing indicators) vs at-least-once (chat, orders). This decides whether you need acks and replay.
- Scale: expected concurrent connections and messages/sec per connection. Under ~10k connections, one node suffices; beyond it, plan the backplane from day one.
- Clients: browsers only, or mobile (aggressive OS-level connection killing changes reconnect design).
- Infrastructure between client and server: load balancers and proxies, with their idle-timeout values (label unknown timeouts as guesses and test).
Step 2: Secure the connection handshake
wss://always; plainws://over the internet is unacceptable.- Auth token in the first message after connect or in a subprotocol header - never in the URL query string, because URLs land in proxy and server logs.
- Authenticate on connect, and re-check authorization per sensitive action; a connection authorized an hour ago may belong to a since-revoked session.
- Validate and size-limit every inbound frame (set a hard max like 64KB-1MB per your payloads) and never
evalor trust payload contents.
Step 3: Implement heartbeats - both sides
TCP does not tell you a peer is gone; a pulled cable fires no close event.
- Client: send a ping every 20-30 seconds; if no pong within 5-10 seconds, force-close and enter reconnect. Keep the interval under the lowest proxy/LB idle timeout (commonly 60s) so intermediaries never reap "idle" connections.
- Server: track last-activity per socket; terminate anything silent past 2 missed intervals (~60-90s) to reclaim memory and file descriptors.
Step 4: Reconnect with capped exponential backoff and jitter
let attempt = 0;
function connect() {
const ws = new WebSocket(url);
ws.onopen = () => { attempt = 0; authenticate(ws); resync(ws); flushQueue(ws); };
ws.onclose = () => {
const delay = Math.min(30000, 2 ** attempt * 500) + Math.random() * 1000;
attempt++;
setTimeout(connect, delay);
};
}
- Base 500ms, doubling, capped at 30s, plus 0-1s random jitter. The jitter is not optional: after a server restart, 50k clients with identical backoff reconnect in synchronized waves and take the server down again.
- Reset the attempt counter only after a successful open (and ideally after a stable period, so a connect-crash loop doesn't hammer at base delay).
- Mobile: expect constant background/foreground disconnects; reconnect eagerly on foreground, still with jitter.
Step 5: Handle gaps and delivery - sequence numbers are the backbone
- Stamp every server-to-client message with a monotonic per-channel sequence number. On reconnect, the client sends its last seen seq; the server replays from a short retained buffer (size it to cover a typical reconnect window, e.g. 1-5 minutes of messages) or instructs a full resync when the gap exceeds the buffer.
- At-least-once delivery: client acks; server resends unacked after a timeout; handlers are idempotent (dedupe by message id) because resends guarantee duplicates.
- Queue outbound messages while disconnected - with a cap. Choose the overflow policy per data type: drop-oldest for state updates where only the latest matters (cursor positions, ticker prices - better yet, coalesce to latest-only), or surface an error for must-deliver messages.
Step 6: Manage backpressure on the server
A slow consumer on a fast stream buffers unboundedly inside your process.
- Monitor per-socket send-buffer depth (
bufferedAmountin browsers;socket.bufferedAmount/write-queue size server-side). - Thresholds: stop sending non-essential messages past ~1MB buffered; disconnect the client past ~5MB or 30s without drain - a client that can't keep up is better served by reconnect-and-resync than by an OOM on your node.
- For broadcast-heavy systems, coalesce: send state snapshots at a tick rate (e.g. 10-20Hz) instead of every mutation.
Step 7: Scale horizontally with a backplane
- One node holds its connections in memory; to route a message to a subscriber on another node, publish through a pub/sub backplane (Redis pub/sub, NATS). Every node subscribes to the channels its sockets care about.
- Rooms/channels are server-side constructs; never iterate all sockets to broadcast.
- Sticky sessions (or a shared session store) if any per-connection state lives outside the socket itself; design connection state to be rebuildable from auth + resync so any node can adopt a reconnecting client.
- Drain gracefully on deploy: stop accepting, send a "reconnect" close code, and let clients' backoff+jitter spread the reconnection across the fleet.
Worked artifact: connection policy block
Copy, fill, and keep next to the server code:
WEBSOCKET CONNECTION POLICY - [FILL: service]
Transport: wss only; auth via first-message token; max frame [FILL: 256]KB
Heartbeat: client ping [FILL: 25]s, pong timeout [FILL: 8]s
server reaps after [FILL: 2] missed intervals
Proxy idle limit: [FILL: 60]s ([FILL: source or GUESS - verify])
Reconnect: 500ms * 2^n, cap 30s, + rand(0-1000)ms jitter
Sequencing: per-channel monotonic seq; replay buffer [FILL: 2] min
gap > buffer -> full resync via [FILL: REST endpoint]
Delivery: [FILL per message type: fire-and-forget | ack+idempotent]
Offline queue: cap [FILL: 100] msgs; overflow: [FILL: drop-oldest | error]
Backpressure: pause non-essential at [FILL: 1]MB; disconnect at [FILL: 5]MB
Scale: [FILL: single node | Redis/NATS backplane]; drain on deploy
Deliverable
Produce the filled connection policy, the client reconnect/heartbeat implementation, the server-side reaping and backpressure thresholds, and the resync protocol (seq numbers, replay buffer size, full-resync fallback).
Do NOT
- Do not skip heartbeats because
oncloseexists - half-dead connections never fire it. - Do not reconnect without jitter; synchronized retries turn a blip into an outage.
- Do not put auth tokens in the URL - they end up in logs across every intermediary.
- Do not assume delivery: no sequence numbers means every reconnect silently loses messages.
- Do not buffer unboundedly for slow consumers; cap, coalesce, or cut them loose.
- Do not broadcast by looping all sockets, or scale past one node without a backplane.
- Do not use WebSockets where SSE or plain polling meets the need - you inherit this entire checklist for nothing.
Quality bar
- Pulling the network cable is detected within 2 heartbeat intervals on both sides.
- A 60-second server outage produces jittered, capped reconnects and zero message loss within the replay window.
- A slow consumer triggers the backpressure policy, not node memory growth.
- Every inbound frame is size-limited and validated; auth is never in a URL.
- With two server nodes, a message published on node A reaches a subscriber on node B.
Escalation
For inbound event ingestion over HTTP, use webhook-receiver-hardener. Mobile offline-first data sync beyond live messaging goes to mobile-offline-sync. Push notifications as the fallback channel pair with push-notification-wirer; broker-based fan-out architecture with kafka-pipelines.
04 同仓库更多 Skills
有氧运动计划
zone-2-cardio-plan
当有人要求制定稳态有氧训练计划、进行有氧基础训练或达到第二区间、安排轻松或耐力型的每周有氧训练、按心率区间训练,或提高静息心率、最大摄氧量或耐力时,可以使用此方案。该方案构建了一个包含训练计划结构、训练方式选择和进度安排的心率区间有氧训练模块。本方案仅提供一般健身指导,不构成医疗建议。请勿用于设计力量或举重训练计划(请使用力量训练计划)、高强度间歇训练 (HIIT) 或循环训练(请使用体能训练和 HIIT 计划),或灵活性/柔韧性训练(请使用灵活性训练计划)。
youtube-script-writer
Writes retention-engineered long-form YouTube scripts on a hook/body/payoff architecture - a first-30-seconds hook that survives the retention cliff, a beat-sheet body with mini-hooks and bridges, timed pattern interrupts, and a payoff that over-delivers - with B-roll and editing cues marked inline. Use when someone says "script my next video", "my retention graph dies at the start", "tighten this draft", or "outline a video on X". Do NOT use for the 3-second opener of TikTok, Reels, or Shorts - use video-hook-writer instead; do NOT use for spoken voiceover synced to on-screen tutorial actions - use narration-script instead; do NOT use for a sales demo built on a buyer's discovery notes - use demo-script instead; do NOT use to design the thumbnail and title - use thumbnail-concept instead.
win-back-campaign
Designs segmented win-back email sequences for dormant users - dormancy tiers, a three-beat sequence with a single proportional incentive, and a suppression rule - and measures success by re-activation, not opens. Use when someone says "our users went quiet", "design a win-back campaign", "how do I re-engage lapsed customers", "should we email people who haven't logged in for months", or lifecycle messaging has stopped landing with a lapsed segment. Do NOT use for recovering an unfinished checkout - use abandoned-cart-sequence instead. Do NOT use for diagnosing and fixing why active customers cancel - use churn-reduction instead. Do NOT use for building always-on onboarding or nurture flows - use email-drip-builder instead.
wikipedia-style
Writes neutral, verifiable, encyclopedic prose following Wikipedia's core content policies and manual of style - NPOV, inline citations, notability-backed claims, and a strict ban on promotional language. Use when someone asks "write this like a Wikipedia article", "draft a Wikipedia page for this company or person", "make this neutral and encyclopedic", or "why does my article draft keep getting rejected". Do NOT use for persuasive or opinion writing - use op-ed-writer instead; for thesis-driven academic papers, use academic-essay; for verifying claims in existing text, use fact-checker.
whitepapers
Structures and writes authoritative B2B whitepapers - a standalone executive summary, a data-sized problem statement, methodology, evidence-driven findings, recommendations, and a soft call to action - flagging every claim that needs a real citation. Use when a marketer asks "write a whitepaper on this topic", "turn our research into a lead-gen asset", "make this credible enough for executives", or a draft reads like a product pitch instead of analysis. Do NOT use for customer-story-driven proof - use case-study-builder instead; for policy audiences, use policy-brief.
white-space-analysis
Finds unmet market gaps by ranking jobs-to-be-done on importance versus satisfaction, overlaying them against current solutions and workarounds, and validating that each gap is real, winnable, and worth winning. Use when someone asks "where are the gaps in this market", "what's underserved here", "where should we play next", or is hunting for product, feature, or market-entry opportunities. Do NOT use for profiling competitors and building battle cards - use competitive-intelligence instead; for putting a dollar figure on an identified opportunity - use market-sizing instead; for extracting jobs-to-be-done from raw customer interviews - use jtbd-extractor instead.