5.2 KiB
@suaveplan/api-ws-broker-redis — Redis pub/sub broker for cross-host fan-out
Package: @suaveplan/api-ws-broker-redis
Status: PROPOSED
Type: new-package
Stage: 7 (GraphQL + WebSocket / SSE engines) — see umbrella openspec/changes/api-plugin-foundation/
Why
Per design doc §11.3 single-host deployments use an in-memory broker, but production deployments with multiple host replicas need cross-host broadcast: a bus.broadcast on host A must reach the locally-connected subscribers on host B. Redis pub/sub is the simplest and most operationally familiar transport for this. This package is the foundation's reference cross-host broker — required for any multi-replica deployment. The §11.6 SSE design explicitly reuses the same broker abstraction, so this package serves both api-ws-bun (WebSocket) and api-sse-bun (SSE) without duplication.
Sticky sessions are NOT required because connections are not migrated across hosts — the broker just makes broadcasts visible everywhere; each host delivers to its own locally-connected subscribers.
What changes
Net-new package depending on @suaveplan/api-types, @suaveplan/api-ws (broker contract), @suaveplan/api-sse (also consumes the broker), @suaveplan/error, @suaveplan/logger, @suaveplan/opentelemetry, ioredis (peer), and zod.
- ADDED:
createRedisWsBroker(opts)— factory returning a broker value implementing theWsBrokercontract from@suaveplan/api-ws. - ADDED:
RedisWsBrokerOptions— Zod-validated config:connection(URL orioredisoptions),keyPrefix(defaultsuaveplan:ws:),channelPrefix(defaultws.broadcast.),eventBufferTtlSec(default 300 = 5 min — pairs withapi-ws-bun's reconnect TTL),eventBufferMaxEvents(default 256 per(tenant, topic)),compressionThresholdBytes(default 1024 — payloads above this are gzipped before publish),reconnectStrategy. - ADDED: Pub/sub transport —
broker.broadcast(topic, { tenant, payload })publishes to<channelPrefix><topic>on Redis; every subscribed host receives the message and delivers to its locally-connected subscribers (looked up via localConnectionRegistry). - ADDED: Reconnect event-buffer storage — for
api-ws-bunwhen itsreconnectEventBufferStrategyresolves to broker-backed, this package owns the event buffer: Redis sorted-set per(tenant, topic), scored bylastEventId, capped ateventBufferMaxEvents, TTLeventBufferTtlSec. SSE consumes the same buffer forLast-Event-Idresume. - ADDED: Cross-host topology — every host subscribes to the host-wide channel pattern at start; on plugin install/uninstall the broker is informed so per-plugin topic prefixes can be added/dropped from the subscription set if optimisation is wanted. Default is whole-pattern subscription for simplicity.
- ADDED: Health probe —
broker.healthz()returns broker's connection state; consumed by the host's readiness endpoint. - ADDED:
WS_BROKER_REDIS_*error codes:WS_BROKER_REDIS_CONNECTION_LOST,WS_BROKER_REDIS_PUBLISH_FAILED,WS_BROKER_REDIS_SUBSCRIBE_FAILED,WS_BROKER_REDIS_BUFFER_WRITE_FAILED,WS_BROKER_REDIS_BUFFER_READ_FAILED,WS_BROKER_REDIS_PAYLOAD_TOO_LARGE,WS_BROKER_REDIS_DESERIALISE_FAILED,WS_BROKER_REDIS_CONFIG_INVALID,WS_BROKER_REDIS_RECONNECT_FAILED,WS_BROKER_REDIS_TIMEOUT. - ADDED: OTel instruments:
ws.broker.publishes_total { topic, outcome },ws.broker.publish_duration_ms,ws.broker.receives_total { topic },ws.broker.buffer_writes_total,ws.broker.buffer_reads_total { hit },ws.broker.connection_lost_total.
Out of scope
- A non-Redis broker — design doc §11.3 specifically calls Redis as the reference. Other brokers (NATS, Kafka) can implement the same
WsBrokercontract in future packages. - The WebSocket engine itself — that's
api-ws-bun. This broker just makes broadcasts visible across hosts. - The SSE engine —
api-sse-bun. Consumes this same broker for fan-out + event-buffer. - Connection migration across hosts — out of scope by design.
Source mapping
Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.
| Requirement | Design doc section |
|---|---|
| Cross-host fan-out via Redis pub/sub | §11.3 |
| Sticky sessions NOT required | §11.3 |
| Reconnect event-buffer storage in Redis | §11.5 (paragraph 4) + §14 |
| SSE reuses same broker | §11.6 |
| Health probe | §12.6 |
Verification approach
src/broker/redis-broker.test.ts(integration, 30000ms) — uses@suaveplan/testing-integration-redis; covers publish/subscribe round-trip, multi-host fan-out simulation (two broker instances bound to same Redis), reconnect after Redis bounce.src/broker/redis-broker.spec-conformance.test.ts— pins theWsBrokercontract conformance suite fromapi-ws/testing.src/event-buffer/event-buffer.test.ts(integration, 30000ms) — sorted-set ops; TTL expiry; cap enforcement; replay bylastEventId.src/health/health.test.ts—healthzreflects connection state; recovers after reconnect.tests/integration/two-host-broadcast.test.ts(60000ms) — boots twoapi-ws-bunengines against the same Redis; connects clients to each; broadcasts from one; asserts delivery on both.
Migration impact
None — additive only.