every.channel: sanitized baseline
This commit is contained in:
commit
897e556bea
258 changed files with 74298 additions and 0 deletions
|
|
@ -0,0 +1,76 @@
|
|||
# ECP-0058: One-to-Many Web Bootstrap via Stream Relay DO (WebSocket)
|
||||
|
||||
Status: Draft
|
||||
|
||||
## Problem
|
||||
|
||||
Our initial web viewer path used a direct WebRTC data channel between publisher and viewer.
|
||||
That path is fundamentally 1:1: once one viewer consumes the offer/answer, additional viewers
|
||||
cannot join, which breaks the "send a link to mom" requirement and contradicts the project's
|
||||
one-to-many goal.
|
||||
|
||||
TURN improves NAT traversal but does not change the 1:1 nature of WebRTC offers/answers.
|
||||
|
||||
We need a bootstrap path that:
|
||||
- is one-to-many by construction
|
||||
- works in a normal browser without plugins
|
||||
- carries the same CMAF object stream the native pipeline already produces
|
||||
- remains obviously replaceable by MoQ/WebTransport later (no long-term lock-in)
|
||||
|
||||
## Proposal
|
||||
|
||||
Add a per-stream fanout relay implemented as a Durable Object:
|
||||
|
||||
- `GET /api/stream/ws?stream_id=<id>&role=sub|pub`
|
||||
- Upgrades to a WebSocket.
|
||||
- `role=pub` registers a single publisher for the stream.
|
||||
- `role=sub` registers a subscriber.
|
||||
|
||||
Publisher behavior:
|
||||
- Connect once to the relay DO (`role=pub`).
|
||||
- Send the CMAF object stream as "direct-wire" chunked messages (same framing as the existing
|
||||
WebRTC direct path).
|
||||
- Continue to refresh the directory listing while publishing (multiple viewers are supported).
|
||||
|
||||
Subscriber behavior:
|
||||
- Connect to the relay DO (`role=sub`).
|
||||
- Receive the live message stream.
|
||||
- On connect, receive a buffered init segment (`chunk_index=0`) plus a small ring buffer of recent
|
||||
segments so playback can start immediately.
|
||||
|
||||
Web app behavior:
|
||||
- The global live list (`/api/directory`) remains the discovery surface.
|
||||
- "Watch" connects by `stream_id` to the relay websocket and plays via MSE.
|
||||
|
||||
## Wire Framing
|
||||
|
||||
We reuse the existing "direct-wire" message format:
|
||||
|
||||
- Each WebSocket message is binary and begins with a 1-byte tag:
|
||||
- `0x01` = STREAM chunk (payload is a slice of `[u32be frame_len][frame_bytes...]`)
|
||||
- `0x00` = FRAME (optional; not required)
|
||||
- `0x02` = PING (ignored)
|
||||
- `frame_bytes` is `ec-moq::encode_object_frame(meta_json, data)`:
|
||||
- `[u32be meta_len][meta_json_bytes][data_bytes...]`
|
||||
|
||||
The relay DO decodes publisher STREAM chunks into frames only for buffering (to identify init vs
|
||||
segments). For live fanout, it forwards publisher messages to subscribers as-is.
|
||||
|
||||
## Limits / Abuse Notes
|
||||
|
||||
This is not spam-resistant. It is a bootstrap relay.
|
||||
|
||||
Defensive bounds:
|
||||
- Buffer only `init` plus the last N segments (currently 12) per stream DO.
|
||||
- If publisher reassembly buffer grows beyond a few MiB (garbage input), reset it.
|
||||
|
||||
Future ECPs cover signatures/manifests/merkle/anti-junk.
|
||||
|
||||
## Rollout
|
||||
|
||||
1. Deploy Worker changes:
|
||||
- add Durable Object binding/class `StreamRelayDO`
|
||||
- route `/api/stream/ws` to it
|
||||
2. Add `ec-node ws-publish` / `ec-node ws-subscribe`.
|
||||
3. Update the web viewer to prefer the relay path for global watching.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue