# `broker` — multi-host fan-out contract _(no submodules)_ ## Purpose A single-host deployment never needs a broker — the `SubscriptionBus` inside that process already sees every connection. Production deployments that run multiple host replicas behind a load balancer DO need one: a broadcast issued on host A has to reach the connections attached to host B without migrating connections between processes. The `WsBroker` contract defined here is the seam those broker adapters implement. Concrete brokers (`api-ws-broker-in-memory` for single-host tests, `api-ws-broker-redis` for production) plug behind this interface and stay out of plugin code paths. ## Features - `BrokerEnvelope` — wire payload (`topic`, `tenant`, `payload`, optional `originHost`). - `BrokerHandler` — subscriber callback signature. - `BrokerSubscription` — disposal handle returned from `subscribe`. - `WsBroker` interface — `publish`, `subscribe`, `dispose`. Each method may return synchronously or asynchronously so in-memory brokers stay cheap and Redis-backed brokers can `await` round trips. ## Usage: Basic ```ts import type { WsBroker } from "@suaveplan/api-ws/broker"; async function fanOut(broker: WsBroker<{ orderId: string }>): Promise { await broker.publish("orders.created", { topic: "orders.created", tenant: tenantId, payload: { orderId: "o1" }, }); } ``` ## Usage: Advanced ```ts import type { WsBroker, BrokerSubscription } from "@suaveplan/api-ws/broker"; async function bridge( broker: WsBroker, local: { broadcast: (...args: unknown[]) => void }, ): Promise { const subscription = await broker.subscribe("orders.created", (envelope) => { local.broadcast(envelope.topic, { tenant: envelope.tenant, payload: envelope.payload, }); }); return subscription; } ``` ## API Reference - `interface BrokerEnvelope { topic, tenant, payload, originHost? }`. - `type BrokerHandler = (env: BrokerEnvelope) => void | Promise`. - `interface BrokerSubscription { topic; unsubscribe(): void | Promise }`. - `interface WsBroker { publish, subscribe, dispose }`. ## Implementation Notes - The contract intentionally does not specify reliability semantics. Each adapter declares its own delivery guarantees (`in-memory` is at-most- once with FIFO inside the process; `redis` is at-most-once with Redis pub/sub semantics). Plugins that need stronger guarantees use the saga / 2PC capabilities elsewhere in the foundation. - `originHost` is optional so adapters that don't need loop-back suppression can omit it. Adapters that DO suppress loop-back compare against the local host's identity at the receive side. - `dispose` MUST close every subscription cleanly; the conformance harness pins this invariant against every concrete adapter.