Skip to content

Latest commit

 

History

339 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

wave-moq-edge

Canonical repository. This repo is the source of truth for WAVE MoQ edge — it is no longer an auto-mirror of an internal WAVE monorepo's workers/moq-edge. Open PRs here. (The upstream workers/moq-edge directory is being retired per the WAVE Protocol Plane spec.)

Sub-second live media at the edge. A Cloudflare Worker that implements IETF draft-ietf-moq-transport at draft-20 — the current IETF working draft (2026-08-31), the frontier of the spec. Publish a track. Subscribe to it. Globally distributed in under 100ms.

POST   /v1/publish/:namespace/:track       Become a publisher
GET    /v1/subscribe/:namespace/:track     Become a subscriber (WebTransport)
GET    /v1/track/:namespace/:track         Track metadata + live counts
GET    /v1/announce                        List all active tracks
GET    /v1/catalog                         draft-ietf-moq-catalog-01 listing
GET    /health                             JSON liveness probe
GET    /metrics                            Prometheus exposition
GET    /                                   Branded landing page

Why this exists

Live streaming at scale is a coordination problem. Every viewer needs every frame, ordered, on time, fast. Today's stack — RTMP origins, HLS chunks, CDN replicas — solves it with delay measured in seconds. WAVE built moq-edge because seconds isn't fast enough for the next decade of streaming.

MoQ Transport (Media over QUIC) puts publish/subscribe at the edge. One Durable Object per track. Publishers write. Subscribers read. The edge fans out. Latency: sub-100ms p95, globally.

Architecture

Publisher → POST /v1/publish/ns/track → DO → in-memory frame queue
                                          ↓
                                 fan-out to N subscribers
                                          ↓
Subscriber ← GET /v1/subscribe/ns/track ← DO ← bytes

Each track gets one Durable Object instance. The DO holds publisher state, subscriber list, and a small object cache for late joiners. WebTransport sessions are sticky to a Worker; the DO is the rendezvous so all subscribers reach the same place regardless of which Worker they hit.

R2 backs replay. Tracks are recorded for 24h by default. Subscribe with ?from=<timestamp> to replay.

Quick start

git clone https://github.com/wave-av/wave-moq-edge
cd wave-moq-edge
pnpm install
pnpm wrangler deploy --env staging

Publish your first track:

curl -X POST https://<your-worker>.workers.dev/v1/publish/demo/hello \
  -H 'authorization: Bearer <your-token>'

Subscribe:

curl https://<your-worker>.workers.dev/v1/track/demo/hello
# {"namespace":"demo","track":"hello","subscriber_count":1,"region":"BOS"}

See examples/quick-start.md for a full walkthrough.

Spec compliance

moq-edge tracks the IETF draft at the frontier. The wire codec (src/moq-wire.ts) implements draft-20 — the current IETF working draft (2026-08-31) — including its spec-distinctive changes: ALPN-only version negotiation (moqt-20), leading-1-bits varints (§1.4.1, not RFC 9000's 2-bit prefix), and the ANNOUNCEPUBLISH_NAMESPACE rename. #212 tracks the draft-18→20 uplevel epic; E0/E1 (this bump, plus the safe -19 renames — GOAWAY drops Request ID, PUBLISH_BLOCKEDPUBLISH_SKIPPED, MAX_REQUEST_UPDATES) ship no wire-body change. E2+ (strict Type-Flags bitfields, fill-fetch, Range Filters, REQUEST_UPDATE) land as separate later phases.

Release Preferred draft Advertised range Status
0.x draft-20 draft-07 .. draft-20 Current
(planned) 1.x draft-21+ drops drafts < 12 Future

What the advertised range is — and isn't. The codec is draft-20-native (base draft-18 message shapes, uplevel per #212). The draft-07 floor is the advertised negotiation minimum, not a guarantee of cross-version wire interop: draft-18's leading-1-bits varint is not byte-compatible with the RFC-9000 varints used by draft ≤17, so a real connection below draft-18 also needs a varint bridge (plus UNSUBSCRIBE, currently unimplemented). Treat anything below draft-18 as a roadmap target, not proven interop.

Negotiation-safety note (#212 E0). This relay speaks MoQ over a single CF Workers WebSocket (no WebTransport-server API), so there is no TLS ALPN on the wire between our own relay and moq-client — MOQ_ALPN is a single hardcoded string constant, not an accept-set the relay checks. Because of that, E0's version bump is NOT a live wire-negotiated flip: relay and moq-client (both in this repo) ship together in one Worker deploy, which flips both atomically — there is no mid-deploy window where they could disagree. MOQ_DRAFT_SUPPORTED in wrangler.toml is the informational advertised range and stays additive (20,19,18,...) so a real ALPN-based peer (native QUIC/WebTransport, once that binding lands) still sees moqt-18 offered alongside moqt-20.

Interop target. Cloudflare runs public MoQ relays at draft-07.cloudflare.mediaoverquic.com and draft-14.cloudflare.mediaoverquic.com — their deployment currently tracks draft-07, eleven revisions behind the spec. Those endpoints are the named targets for cross-relay interop testing once the native WebTransport/QUIC binding lands (today's transport is WebSocket — see CHANGELOG).

Compliance tests live in __tests__/. Interop reports welcome — file an issue with your client implementation, transport, and findings.

Performance

  • p50 publish→subscribe latency: <50ms intra-region, <100ms cross-region
  • Capacity: 1000 concurrent subscribers per track, 10K tracks per Worker
  • Cache: last 100 objects in DO memory, full track history in R2 (24h hot, 365d cold)
  • Edge regions: every Cloudflare colo (300+)

These numbers come from production traffic on the WAVE platform. Your mileage will vary based on payload size, encoder pacing, and subscriber density.

What this repo is not

This is the transport relay. It moves bytes. It does not:

  • Encode video (that's your encoder's job)
  • Adapt bitrate (your client picks the rendition)
  • Authenticate users (your auth layer issues capability tokens)
  • Record analytics (use Workers Analytics Engine or push to your own pipeline)

The full WAVE platform stacks all those layers on top. moq-edge is the bottom one.

Constraints

  • Per-track DO instance — sticky routing means publisher + subscribers always meet at the same DO
  • Namespace + track names: lowercase alphanumeric + dash, 1-64 chars (Zod-validated)
  • Max 16 MiB per object (MAX_OBJECT_SIZE_BYTES env var)
  • 10K subscribers/track in production, 1K in staging
  • KV registry has 24h TTL per track (publisher must refresh on long sessions)

Configuration

# wrangler.toml
name = "moq-edge"
main = "index.ts"

[vars]
MOQ_DRAFT_PREFERRED = "draft-20"
MOQ_DRAFT_SUPPORTED = "draft-20,draft-19,draft-18,draft-17,draft-16,draft-15,draft-14,draft-13,draft-12,draft-11,draft-10,draft-09,draft-08,draft-07"
MAX_SUBSCRIBERS_PER_TRACK = "1000"
MAX_OBJECT_SIZE_BYTES = "1048576"  # 1MB

[[durable_objects.bindings]]
name = "MOQ_SESSIONS"
class_name = "MoqSessionDO"

[[kv_namespaces]]
binding = "MOQ_TRACK_REGISTRY"
id = "<your-kv-id>"

[[r2_buckets]]
binding = "MOQ_RECORDINGS"
bucket_name = "<your-r2-bucket>"

Roadmap

  • 0.1.x (current): scaffold, HTTP API, DO pattern, KV/R2 bindings, catalog endpoint, draft-18 advertised in version negotiation
  • 0.2.x: real wire protocol over WebTransport QUIC streams (draft-18 message types, object headers, GROUP/SUBGROUP framing)
  • 0.3.x: protocol adapters (WebRTC↔MoQ, SRT↔MoQ, HLS-LL↔MoQ)
  • 0.4.x: public live demo at moq-demo.wave.online, reference clients
  • 1.0: GA when draft-20+ ships, full interop testing complete, working group consensus on stable framing

Contributing

See CONTRIBUTING.md. Open an issue before non-trivial PRs. Bug fixes, spec compliance fixes, and interop test reports are always welcome.

Security

Vulnerabilities: security@wave.online. 90-day coordinated disclosure. See SECURITY.md.

License

Apache-2.0. See LICENSE and NOTICE.

Built by WAVE Online.

About

Sub-second live media at the edge — IETF MoQ Transport (preferred draft-17, negotiation draft-07..draft-17) relay on Cloudflare Workers. Built by WAVE Online.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages