# OpenSpec Change: websocket-gateway - **Change ID**: websocket-gateway - **Package**: `@suaveplan/websocket-gateway` - **Category**: server (Node-only) - **Target path**: `packages/network/websocket-gateway` - **Status**: PROPOSED - **Date**: 2026-05-11 - **Parent**: `network-transport-family` (2026-05-11) — Wave 5 - **Migration source**: `../suaveplan/packages/server/websocket-gateway` + `../suaveplan/enterprise/websocket-gateway.md` --- ## Summary Migrate `@suaveplan/websocket-gateway` into genesis as a server-tier daemon providing **rooms + presence** semantics over WebSocket connections. Pairs with `@suaveplan/network-transport-websockets` ✅ (post-Wave-1 adapter refactor) on the client side. Rooms scope message delivery; presence tracks who is connected to which room; channel multiplexing lets multiple logical streams share one TCP connection. --- ## Why Wave 5 (not earlier) - Depends on the `network-transport-websockets-adapter` Wave-1 refactor (server-side rooms need the `Transport` contract on the client side). - Server-tier package — bundles with Wave-4 transports, not Wave-3 browser packages. --- ## Current-state audit Upstream coverage: 97.85% statement. Upstream source layout (`../suaveplan/packages/server/websocket-gateway/src/`): - `gateway/` — main `WebSocketGateway` daemon - `channel/` — channel multiplexing - `room/` — room semantics (join / leave / broadcast / direct) - `presence/` — presence subsystem (who's in which room) - `types/` — public type surface - `index.ts` — barrel --- ## Parity Checklist | Capability | Upstream | Migrated | Notes | |---|---|---|---| | `WebSocketGateway` daemon | ✅ | ✅ | | | Room semantics (join/leave/broadcast/direct) | ✅ | ✅ | | | Presence tracking | ✅ | ✅ | | | Channel multiplexing | ✅ | ✅ | | | Plugin wiring via `@suaveplan/api` `definePlugin()` | upstream-required | ⬆️ | Once `@suaveplan/api` lands on genesis (not yet ✅), wire via `definePlugin()`. Until then a standalone `start()`/`stop()` lifecycle. | | JWT auth via `@suaveplan/jwt-tenant` | upstream-required | ⬆️ | Once shipped on genesis. Until then inject `AuthVerifier` callback. | | RBAC/ABAC via `@suaveplan/permissions` | upstream-required | ⬆️ | Same: inject `AuthorizationCheck` callback. | | Rate-limiting via `@suaveplan/rate-limiter` | upstream-required | ⬆️ | Once available. | | Idempotency via `@suaveplan/idempotency` ✅ | upstream-required | ✅ | Wired directly. | | Audit logging via `@suaveplan/audit-logging` | upstream-required | ⬆️ | Once available. Until then `@suaveplan/logger` ✅ with structured fields. | | OTel spans + Prometheus metrics | upstream-required | ⬆️ | Spans `ws_gateway.connect`/`message`/`broadcast`, counters `ws_gateway.rooms_active`, `ws_gateway.presence_updates_total`, histograms `ws_gateway.message_dispatch_ms`. | | `AbortSignal` everywhere | partial | ⬆️ | Threaded through every lifecycle method. | | Retry + circuit breakers via `@suaveplan/retry` ✅ | upstream-required | ✅ | Wired on every outbound integration. | | Health / readiness / liveness | upstream-required | ⬆️ | Inline `/health`, `/ready`, `/live` endpoints until `@suaveplan/health-check` lands. | | Webhooks via `@suaveplan/webhook-client` ✅ | upstream-required | ✅ | Wired for presence-event webhooks. | | Structured errors | 🚫 | ⬆️ | `defineCodes("WS_GATEWAY", [...])`: `ROOM_NOT_FOUND`, `ROOM_FULL`, `CHANNEL_INVALID`, `AUTH_REJECTED`, `RATE_LIMITED`, `INVALID_OPTIONS`. | | Zod boundary validation | partial | ⬆️ | Every public option + every wire frame. | | `export *` barrels | check | ⬆️ | Convert to explicit named exports per §6a. | | Test-runner imports | direct vitest | ⬆️ | Rewrite to `@suaveplan/testing/runner` per §7. | | 100% test coverage | 🚫 (97.85%) | ⬆️ | Bring to 100/100/100/100. | | Integration tests | partial | ⬆️ | Real-server boot via `@suaveplan/testing-integration`; no mocks. | --- ## Available workspace deps on genesis - `@suaveplan/network-transport-websockets` ⬅ post-Wave-1 (after adapter refactor) - `@suaveplan/idempotency` ✅ - `@suaveplan/retry` ✅ - `@suaveplan/webhook-client` ✅ - `@suaveplan/logger` ✅ - `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/timing` ✅ - `@suaveplan/testing` + `@suaveplan/testing-integration` ✅ - External: `ws` or `bun:websocket` (universal façade) — add to root catalog under `network-server`. **Blocking note.** Migration can start once `network-transport-websockets-adapter` ships and (optionally) `@suaveplan/api`, `@suaveplan/jwt-tenant`, `@suaveplan/permissions`, `@suaveplan/rate-limiter`, `@suaveplan/audit-logging`, `@suaveplan/health-check` are on genesis. Auth and rate-limit hooks accept callbacks until those packages land. --- ## Acceptance gate 1. `bunx turbo lint typecheck test --filter=@suaveplan/websocket-gateway` green. 2. `bunx turbo test:coverage --filter=@suaveplan/websocket-gateway` 100/100/100/100. 3. `bun run docs:check` green. 4. Integration test: 3 clients connect via `network-transport-websockets`, join rooms, exchange messages, observe presence events. No mocks. 5. `STATUS.md` row flipped. --- ## Out of scope - Production TLS termination (deployment concern). - Cross-instance gateway clustering (federate via `@suaveplan/network` mesh in a future change). - Persistent room state (separate `websocket-gateway-store-*` package if needed).