Skill: ivangdavila/websocket (version 1.0.0)

--- name: WebSocket description: Implement reliable WebSocket connections with proper reconnection, heartbeats, and scaling. metadata: {"clawdbot":{"emoji":"🔌","os":["linux","darwin","win32"]}} --- ## Reconnection (Always Forget) - Connections drop silently—TCP FIN may never arrive; don't assume `onclose` fires - Exponential backoff: 1s, 2s, 4s, 8s... cap at 30s—prevents thundering herd on server recovery - Add jitter: `delay * (0.5 + Math.random())`—prevents synchronized reconnection storms - Track reconnection state—queue messages during reconnect, replay after - Max retry limit then surface error to user—don't retry forever silently ## Heartbeats (Critical) - Ping/pong frames at protocol level—browser doesn't expose; use application-level ping - Send ping every 30s, expect pong within 10s—no pong = connection dead, reconnect - Server should ping too—detects dead clients, cleans up resources - Idle timeout in proxies (60-120s typical)—heartbeat must be more frequent - Don't rely on TCP keepalive—too infrequent, not reliable through proxies ## Connection State - `readyState`: 0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED—check before sending - Buffer messages while CONNECTING—send after OPEN - `bufferedAmount` shows queued bytes—pause sending if backpressure building - Multiple tabs = multiple connections—coordinate via BroadcastChannel or SharedWorker ## Authentication - Token in URL query: `wss://host/ws?token=xxx`—simple but logged in access logs - First message auth: connect, send token, wait for ack—cleaner but more round trips - Cookie auth: works if same origin—but no custom headers in WebSocket - Reauthenticate after reconnect—don't assume previous session valid ## Scaling Challenges - WebSocket connections are stateful—can't round-robin between servers - Sticky sessions: route by client ID to same server—or use Redis pub/sub for broadcast - Each connection holds memory—thousands of connections = significant RAM - Graceful shutdown: send close frame, wait for clients to reconnect elsewhere ## Nginx/Proxy Config ``` proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 3600s; ``` - Without these headers, upgrade fails—connection closes immediately - `proxy_read_timeout` must exceed your ping interval—default 60s too short - Load balancer health checks: separate HTTP endpoint, not WebSocket ## Close Codes - 1000: normal closure; 1001: going away (page close) - 1006: abnormal (no close frame received)—usually network issue - 1008: policy violation; 1011: server error - 4000-4999: application-defined—use for auth failure, rate limit, etc. - Always send close code and reason—helps debugging ## Message Handling - Text frames for JSON; binary frames for blobs/protobuf—don't mix without framing - No guaranteed message boundaries in TCP—but WebSocket handles framing for you - Order preserved per connection—messages arrive in send order - Large messages may fragment—library handles reassembly; set max message size server-side ## Security - Validate Origin header on handshake—prevent cross-site WebSocket hijacking - Same-origin policy doesn't apply—any page can connect to your WebSocket server - Rate limit per connection—one client can flood with messages - Validate every message—malicious clients can send anything after connecting ## Common Mistakes - No heartbeat—connection appears alive but is dead; messages go nowhere - Reconnect without backoff—hammers server during outage, prolongs recovery - Storing state only in connection—lost on reconnect; persist critical state externally - Huge messages—blocks event loop; stream large data via chunking - Not handling `bufferedAmount`—memory grows unbounded if client slower than server


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)