Context
We have just shipped WebSocket topics / pub-sub (#2, #118, #120). Question raised: is there anything newer than WebSocket worth targeting, and what would it cost us?
Short answer: WebTransport over HTTP/3. It went Baseline in March 2026 (Safari 26.4 finally shipped it, closing the iOS gap), so it is now production-viable, not a Chromium experiment.
What WebTransport buys over WebSocket
- Independent streams in one connection — no head-of-line blocking. A WebSocket is always one TCP connection = one ordered frame stream.
- Unreliable datagrams (fire-and-forget) — what people previously dragged in WebRTC DataChannel (SDP/ICE) for.
- QUIC properties: 0-RTT reconnect, connection migration across networks.
Browser support: Chrome 97+, Edge 98+, Firefox 114+, Safari 26.4+ (macOS/iOS), Opera 83+, Samsung Internet 18+.
Is it HTTP/3-only?
In practice, yes. draft-ietf-webtrans-http2 defines a TCP fallback over HTTP/2 (same extended CONNECT, SETTINGS_WT_MAX_SESSIONS), but neither Chrome nor Firefox implements it — both only speak WebTransport over HTTP/3 (draft-02). The HTTP/2 draft is also still not an RFC.
Consequence: WebTransport = QUIC/UDP only. Corporate proxies that block UDP block WebTransport. WebSocket does not go away — it stays the compatibility transport.
What our NG stack already provides
Checked the installed libraries (nghttp3 1.15.0, ngtcp2):
| Building block |
Status |
Extended CONNECT (:protocol), RFC 9220 |
available — nghttp3_settings.enable_connect_protocol |
| HTTP/3 Datagrams, RFC 9297 |
available — nghttp3_settings.h3_datagram |
| QUIC DATAGRAM frame |
available — ngtcp2_conn_writev_datagram |
| WebTransport layer proper |
absent — no symbol anywhere |
nghttp3 deliberately leaves WebTransport to the application: no SETTINGS_WT_MAX_SESSIONS, no WEBTRANSPORT_STREAM stream type (0x54), no CLOSE_WEBTRANSPORT_SESSION capsule.
Are there ready-made C implementations?
Per our "ready-made libs only" preference — checked, and for our stack the answer is no:
- picoquic + picowt (
pico_webtransport.h, picowt_connect(), wt_baton demo) — the only full WebTransport implementation in plain C, and it is good. But it sits on h3zero, picoquic's own HTTP/3. Reusing it means replacing our entire QUIC stack. Not an option.
- owt-sdk-quic (Intel OWT) — C++ WebTransport server/client API, but on top of Chromium QUIC. Pulling a slice of Chromium into the server: no.
- Everything else alive is not C:
webtransport-go (quic-go), wtransport (Rust/quinn).
So: WebTransport on our stack means writing the layer ourselves on top of ngtcp2/nghttp3.
Implementation cost estimate
Medium. The QUIC foundation is already in production; this is a layer on top, not a transport rewrite.
What we would have to write:
- Stream demultiplexer (the hard part). WebTransport streams bypass HTTP framing — uni streams start with type
0x54 + session ID, bidi streams with signal value 0x41. nghttp3 discards these as unknown. So inside our recv_stream_data callback from ngtcp2 we must peek the varint prefix ourselves and route the stream to either nghttp3 or the WT layer.
- Write scheduler. The send loop currently drives
nghttp3_conn_writev_stream. WT streams must be interleaved with nghttp3's streams by hand, with flow-control accounting.
- Capsule protocol on the CONNECT stream (
CLOSE_WEBTRANSPORT_SESSION, DRAIN) — easy, the body arrives via the normal recv_data callback.
- Datagrams — parse the quarter-stream-ID varint and map it to a session.
Roughly comparable in scope to the H3 static work, but with tighter ngtcp2 ↔ nghttp3 coupling.
The existing topics/pub-sub API maps onto WebTransport almost one-to-one — only the "connection → message stream" level changes.
Cheap first step: WebSocket over HTTP/3 (RFC 9220)
nghttp3's header explicitly cites RFC 9220 for enable_connect_protocol. Extended CONNECT is therefore already supported out of the box — our existing wslay can ride a QUIC stream.
- Client API does not change at all.
- We still gain QUIC benefits: connection migration, no TCP head-of-line blocking between separate WS connections, 0-RTT.
- Effort: wiring an upgrade path, not a new protocol layer.
Proposal
- Phase 1 (cheap): WebSocket over HTTP/3 via RFC 9220 extended CONNECT. Validates our H3 + wslay plumbing under a persistent-stream workload.
- Phase 2: WebTransport over HTTP/3 as a second transport behind the same topics API, with WebSocket as the fallback for UDP-hostile networks.
Opening this as a research/design issue — no code yet, looking for a decision on whether Phase 1 is worth scheduling.
References
Context
We have just shipped WebSocket topics / pub-sub (#2, #118, #120). Question raised: is there anything newer than WebSocket worth targeting, and what would it cost us?
Short answer: WebTransport over HTTP/3. It went Baseline in March 2026 (Safari 26.4 finally shipped it, closing the iOS gap), so it is now production-viable, not a Chromium experiment.
What WebTransport buys over WebSocket
Browser support: Chrome 97+, Edge 98+, Firefox 114+, Safari 26.4+ (macOS/iOS), Opera 83+, Samsung Internet 18+.
Is it HTTP/3-only?
In practice, yes.
draft-ietf-webtrans-http2defines a TCP fallback over HTTP/2 (same extended CONNECT,SETTINGS_WT_MAX_SESSIONS), but neither Chrome nor Firefox implements it — both only speak WebTransport over HTTP/3 (draft-02). The HTTP/2 draft is also still not an RFC.Consequence: WebTransport = QUIC/UDP only. Corporate proxies that block UDP block WebTransport. WebSocket does not go away — it stays the compatibility transport.
What our NG stack already provides
Checked the installed libraries (nghttp3 1.15.0, ngtcp2):
:protocol), RFC 9220nghttp3_settings.enable_connect_protocolnghttp3_settings.h3_datagramngtcp2_conn_writev_datagramnghttp3 deliberately leaves WebTransport to the application: no
SETTINGS_WT_MAX_SESSIONS, noWEBTRANSPORT_STREAMstream type (0x54), noCLOSE_WEBTRANSPORT_SESSIONcapsule.Are there ready-made C implementations?
Per our "ready-made libs only" preference — checked, and for our stack the answer is no:
pico_webtransport.h,picowt_connect(),wt_batondemo) — the only full WebTransport implementation in plain C, and it is good. But it sits on h3zero, picoquic's own HTTP/3. Reusing it means replacing our entire QUIC stack. Not an option.webtransport-go(quic-go),wtransport(Rust/quinn).So: WebTransport on our stack means writing the layer ourselves on top of ngtcp2/nghttp3.
Implementation cost estimate
Medium. The QUIC foundation is already in production; this is a layer on top, not a transport rewrite.
What we would have to write:
0x54+ session ID, bidi streams with signal value0x41. nghttp3 discards these as unknown. So inside ourrecv_stream_datacallback from ngtcp2 we must peek the varint prefix ourselves and route the stream to either nghttp3 or the WT layer.nghttp3_conn_writev_stream. WT streams must be interleaved with nghttp3's streams by hand, with flow-control accounting.CLOSE_WEBTRANSPORT_SESSION,DRAIN) — easy, the body arrives via the normalrecv_datacallback.Roughly comparable in scope to the H3 static work, but with tighter ngtcp2 ↔ nghttp3 coupling.
The existing topics/pub-sub API maps onto WebTransport almost one-to-one — only the "connection → message stream" level changes.
Cheap first step: WebSocket over HTTP/3 (RFC 9220)
nghttp3's header explicitly cites RFC 9220 for
enable_connect_protocol. Extended CONNECT is therefore already supported out of the box — our existing wslay can ride a QUIC stream.Proposal
Opening this as a research/design issue — no code yet, looking for a decision on whether Phase 1 is worth scheduling.
References