6.8 KiB
OpenSpec Change: enterprise-browser-network-webrtc
- Change ID: enterprise-browser-network-webrtc
- Package:
@suaveplan/network-webrtc - Category: browser
- Target path:
browser/network/network-webrtc - Status: SUPERSEDED-BY-GENESIS-WAVE
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/network-webrtc.md
Summary
Deliver @suaveplan/network-webrtc on the genesis branch from scratch at enterprise quality. The package provides the browser-side WebRTC transport for @suaveplan/network: a WebRTCTransport wrapping RTCPeerConnection + RTCDataChannel, a dual-channel system for reliable + unreliable messaging, a ChannelRouter dispatching by topic, and a first-class SignalingChannel interface with Memory / WebSocket / WebTorrent implementations. No React dependencies — demo UI moves to a React companion package.
Current-state audit (genesis)
The package does not exist on genesis yet — fresh build.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/network (workspace:^) |
Transport interface, NetworkError, connection lifecycle types |
@suaveplan/error (workspace:^) |
Typed WebRTC errors |
@suaveplan/types (workspace:^) |
LoggerLike, EventBusLike |
@suaveplan/events (workspace:^) |
EventEmitter for transport event maps |
@suaveplan/serialize (workspace:^) |
Structured message encoding/decoding |
@suaveplan/schema-registry (workspace:^) |
Register signalling Zod schemas |
zod (catalog:forms) |
Schemas for signalling message types |
@opentelemetry/api (catalog:observability) |
WebRTC metrics |
tslib (catalog:build) |
Runtime helpers |
Dev deps: vitest, @vitest/coverage-istanbul, @playwright/test (catalog:testing), @suaveplan/testing workspace:^.
Preserved public API surface (from main branch)
Core transport
class WebRTCTransport {
constructor(config: WebRTCConfig)
dial(signal: SignalingChannel): Promise<void>
close(): void
send(data: Uint8Array | string): void
sendUnreliable(data: Uint8Array | string): void
onMessage(handler: (data: Uint8Array | string) => void): void
onClose(handler: () => void): void
getStats(): Promise<RTCStatsReport>
readonly state: RTCPeerConnectionState
}
interface WebRTCConfig {
iceServers?: RTCIceServer[]
ordered?: boolean
maxRetransmits?: number
allowedWorkletUrls?: string[]
}
Dual channel
class DualChannel {
constructor(connection: RTCPeerConnection, config?: DualChannelConfig)
sendReliable(data: Uint8Array | string): void
sendUnreliable(data: Uint8Array | string): void
onMessage(handler: (data: Uint8Array | string, reliable: boolean) => void): void
close(): void
}
Channel router
class ChannelRouter {
register(channelId: string, handler: ChannelHandler): void
unregister(channelId: string): void
route(message: ChannelMessage): void
send(channelId: string, payload: unknown): void
}
interface ChannelMessage { channelId: string; payload: unknown; reliable?: boolean }
type ChannelHandler = (payload: unknown) => void
Signalling interface
interface SignalingChannel {
sendOffer(offer: RTCSessionDescriptionInit): Promise<void>
sendAnswer(answer: RTCSessionDescriptionInit): Promise<void>
sendIceCandidate(candidate: RTCIceCandidateInit): Promise<void>
onOffer(handler: (offer: RTCSessionDescriptionInit) => void): void
onAnswer(handler: (answer: RTCSessionDescriptionInit) => void): void
onIceCandidate(handler: (candidate: RTCIceCandidateInit) => void): void
close(): void
}
class MemorySignalingChannel implements SignalingChannel { ... }
class WebSocketSignalingChannel implements SignalingChannel { constructor(url: string) }
New additions for genesis
1. Structured errors via @suaveplan/error
Error codes: WEBRTC_DIAL_FAILED, WEBRTC_ICE_FAILED, WEBRTC_CHANNEL_CLOSED, WEBRTC_SIGNALING_TIMEOUT, WEBRTC_INVALID_STATE.
2. Perfect-negotiation pattern
PeerConnection implements "polite peer" logic — avoids offer/answer glare on simultaneous negotiation.
3. ICE-restart policy
Detect iceConnectionState === 'failed'; auto-restart ICE; surface as typed event 'ice:restart'.
4. Flow control + back-pressure
DualChannel.sendReliable tracks RTCDataChannel.bufferedAmount; blocks or queues when buffer exceeds a configurable watermark.
5. OTel metrics
Counters webrtc.dial_total, webrtc.bytes_in_total, webrtc.bytes_out_total; histogram webrtc.ice_gather_ms, webrtc.rtt_ms.
6. Signalling message Zod schemas
Schemas for offer/answer/ICE-candidate messages registered in @suaveplan/schema-registry.
7. STUN/TURN configuration helpers
Curated iceServers bundles with validation helper; TURN_CONFIG constants for common providers.
File layout
browser/network/network-webrtc/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
webrtc/
peer-connection/
peer-connection.ts (RTCPeerConnection wrapper + ICE restart + perfect negotiation)
peer-connection.md
peer-connection.test.ts
index.ts
data-channel/
data-channel.ts
data-channel.md
data-channel.test.ts
index.ts
signaling-handlers/
signaling-handlers.ts
signaling-handlers.md
signaling-handlers.test.ts
index.ts
webrtc-transport/
webrtc-transport.ts
webrtc-transport.md
webrtc-transport.types.ts
webrtc-transport.test.ts
index.ts
dual-channel/
dual-channel/
dual-channel.ts
dual-channel.md
dual-channel.test.ts
index.ts
channel-router/
channel-router/
channel-router.ts
channel-router.md
channel-router.test.ts
index.ts
signaling/
signaling-channel/
signaling-channel.ts (interface + MemorySignalingChannel)
signaling-channel.md
signaling-channel.test.ts
index.ts
websocket-signaling/
websocket-signaling.ts
websocket-signaling.md
websocket-signaling.test.ts
index.ts
config/
ice-servers/
ice-servers.ts
ice-servers.md
index.ts
tests/
integration.test.ts
e2e/
webrtc.spec.ts (Playwright two-browser offer/answer/ICE/dial/send)
examples/ (NOT in package.json "files")
basic-usage.ts
memory-signaling.ts
Impact
Downstream: @suaveplan/drive-webrtc, @suaveplan/network-discovery-webtorrent, WebRTC-based game networking.
Breaking changes: react removed from runtime deps (demo moved to companion); examples moved out of published tarball.
Prerequisites: @suaveplan/network, @suaveplan/serialize, @suaveplan/error, @suaveplan/events on Verdaccio.