# ADR-038: HTTP/3 and WebTransport as First-Class HTTP Transports

## Status

**Superseded by [ADR-044](044-defer-webtransport-browsers-use-websocket.md).**

This ADR's *correction* of the "two-way-door-as-deferral" anti-pattern
(ADR-009 §"What this framework is NOT") stands as a document — the
anti-pattern is real, and the reasoning that rejected deferral-as-hedging is
correct. However, this ADR's *specific decision* — that `h3`/WebTransport is
in scope now, not deferred — is reversed by ADR-044. ADR-044 is a
**scope** decision (permitted by ADR-009: "not needed for the current
scope"), not a hedging deferral: the browser bidirectional path uses
WebSocket (RFC 6455, mature, native axum support), the ALPN-stream-proxy
(ADR-040) is the speculative use case whose deferral is the reversal
trigger, and the draft-standard + experimental-deps surface area is not
justified by a concrete v1 requirement.

ADR-040 and ADR-043 are **parked, not superseded** — their designs revive
unchanged when WebTransport revives. See ADR-044 for the full scope rationale,
the reversal trigger, and the research note on the wtransport-vs-hyperium
dependency choice (recorded for the revival so it is not re-derived).

## Context

The alknet-http Phase 0 research findings
(`docs/research/alknet-http/phase-0-findings.md`, decision point DH-2)
framed HTTP/3 + WebTransport as "deferred past v1" — a two-way-door
addition to land "when the agent service needs browser streaming." That
framing was a residual of the "two-way door as deferral" anti-pattern
that ADR-009 §"What this framework is NOT" was later written to prevent.
The deferral framing is rejected here; this ADR records the decision as
made.

WebTransport is not a "later" feature. It is the browser-streaming
transport — QUIC streams are cheap (better than WebSocket/SSE by far for
multiplexed bidirectional streaming), and WebTransport is supported in
major browsers. The `alknet-http` crate's purpose is to be the HTTP
interface library for downstream crates that need to expose HTTP, and
browser streaming is a first-class requirement of that purpose, not a
fast-follow.

### The ALPN registry already reserves h3

The overview ALPN Registry maps `h3` to `HttpAdapter (HTTP/3 +
WebTransport)`. The `h3` ALPN is reserved; the implementation lands as
part of `alknet-http`, not as a future crate. This ADR confirms that
reservation and records the architectural decision: HTTP/3 + WebTransport
is in scope, in this crate, as a first-class transport alongside `h2` and
`http/1.1`.

### Why WebTransport matters

- **QUIC streams are cheap.** A browser opens a WebTransport connection
  once and multiplexes many bidirectional streams over it. This is
  fundamentally better than WebSocket (one stream, one connection) or
  SSE (one-directional, one stream per subscription) for the
  subscription-heavy, streaming-heavy patterns the call protocol
  supports natively.
- **Browser support.** WebTransport is supported in modern Chromium-based
  browsers (Chrome, Edge). The browser path for alknet is WebTransport,
  not WebSocket.
- **The call protocol maps cleanly onto WebTransport streams.** A
  `call.requested` over a WebTransport bidirectional stream is the same
  EventEnvelope framing over a different QUIC stream source. The
  `CallConnection`/`Dispatcher` dispatch loop is stream-agnostic
  (ADR-012) — the `h3` handler hands a bidirectional stream to the call
  protocol the same way the `h2`/`http/1.1` handler hands a hyper
  connection to axum.

### The TLS constraint (browsers require X.509)

Browsers do not support RFC 7250 raw public keys (ADR-027, OQ-12). A
WebTransport session from a browser requires an X.509 cert — meaning the
`h3` handler is a domain-hosted-service concern, not a P2P concern. A
node serving WebTransport must have an X.509 identity (`TlsIdentity::X509`
or `TlsIdentity::Acme`). This is a property of the browser, not a
decision this ADR makes — it's recorded so the spec doesn't pretend a
raw-key node can serve browsers.

### WebTransport relay-as-proxy

A distinct WebTransport feature — a proxy that terminates the browser's
WebTransport connection and forwards encrypted traffic to a P2P hub's
Ed25519 endpoint (so the hub need not expose its own public X.509 cert)
— was recorded in ADR-034 §5. That feature does not change the auth model
(bearer token + `PeerEntry.auth_token_hash`; the proxy is transport-only)
and was explicitly placed in the same bucket as the rest of h3/
WebTransport. With this ADR, h3/WebTransport is in scope; the
relay-as-proxy is a genuine scope question (does the proxy belong in
alknet-http or in a separate relay crate?), tracked as OQ-38 — not
deferred, just scoped to a separate decision when the proxy use case
becomes concrete.

## Decision

**HTTP/3 + WebTransport is a first-class HTTP transport in `alknet-http`,
implemented alongside `h2` and `http/1.1`. It is not deferred.**

The `HttpAdapter` implements `ProtocolHandler` for three ALPNs:

| ALPN | Transport | Use case | Browser? |
|------|-----------|----------|----------|
| `http/1.1` | HTTP/1.1 over QUIC stream | Legacy clients, curl | No (browsers use h2/h3) |
| `h2` | HTTP/2 over QUIC stream | Modern HTTP clients, curl | No (browsers use h3) |
| `h3` | HTTP/3 / WebTransport | Browser streaming | Yes (X.509 required) |

All three are served by `alknet-http`. The `h3` ALPN handler upgrades to
WebTransport sessions and serves both HTTP/3 requests (the standard
HTTP/3 over QUIC framing) and WebTransport streams (the bidirectional/
unidirectional stream API). The handler dispatches HTTP/3 requests
through the same axum `Router` as `h2`/`http/1.1`; WebTransport streams
that target the call protocol are handed to the call protocol's dispatch
loop directly (a WebTransport stream is a QUIC bidirectional stream, the
same stream type the call protocol already speaks).

### WebTransport as the browser streaming path

The `h3` handler drives two distinct stream types, distinguished at the
HTTP/3 framing layer (not by peeking application bytes):

1. **HTTP/3 request streams** — standard HTTP/3 GET/POST carrying
   `:method`/`:path`. Dispatched through the axum `Router`, same as
   `h2`/`http/1.1` (ADR-036). These are not WebTransport streams.
2. **WebTransport sessions** — opened by a browser's
   `new WebTransport(url)` call (an HTTP/3 extended CONNECT request).
   The handler accepts the session (the `wtransport` crate's
   `Endpoint::server` + `accept` + `accept_bi` pattern, or the quinn
   HTTP/3 endpoint's WebTransport extension). Within an established
   session, the browser creates bidirectional streams via
   `transport.createBidirectionalStream()`; the handler accepts each
   and dispatches by sub-protocol. For a call-protocol session, the
   first frame is an `EventEnvelope` and the handler hands the stream
   to the call protocol's `Dispatcher` — no SSE translation, no HTTP
   framing. The browser's `WebTransport` JS API speaks to this handler
   directly.

The stream-type distinction (HTTP/3 request vs. WebTransport session)
is made at the HTTP/3 frame layer (regular request headers vs. extended
CONNECT), not by reading the first application byte. The first-frame
routing applies *within* a WebTransport session (determining the
sub-protocol), not between an HTTP/3 request and a WebTransport stream.

This means the browser's subscription/streaming path uses WebTransport
streams directly, not the SSE projection (ADR-036) that HTTP/1.1 + HTTP/2
clients use. The same `Subscription` operation is served as SSE over
`h2` and as a native WebTransport stream over `h3` — the projection is
transport-dependent, the operation is the same.

### h3 and the stealth mapping

The `h3` handler participates in the same stealth model as `h2`/
`http/1.1` (ADR-010, ADR-036): a client that offers `h3` gets the HTTP
handler. Unknown WebTransport paths and unknown HTTP/3 paths get the
decoy (configurable). Real services use `alknet/ssh`, `alknet/call`, etc.

### Implementation reference: wtransport

The `wtransport` crate (`/workspace/wtransport/`, v0.7.1) is a pure-Rust
WebTransport implementation built on `quinn` + `h3`/`qpack`. It provides
the `Endpoint::server` + `accept` + `accept_bi` API that the `h3`
handler uses. `wtransport` is a candidate dependency for the `h3`
feature gate; the exact WebTransport library choice (wtransport vs a
quinn-native HTTP/3 + WebTransport extension) is a two-way-door
implementation detail. The one-way constraint is: `h3` is served, by this
crate, as a first-class transport.

### Feature gating

The `h3`/WebTransport support is behind an `h3` feature gate (the
WebTransport/HTTP/3 dependencies are heavier than `h2`/`http/1.1`):
```toml
[features]
default = ["h2", "http1"]
h3 = ["dep:wtransport"]      # or the quinn h3 extension
mcp = ["dep:rmcp"]           # MCP feature gate (ADR-037)
```
A deployment that only needs `h2`/`http/1.1` (a non-browser-facing
node) does not compile the WebTransport dependencies. A browser-facing
node enables `h3`.

## Consequences

**Positive:**
- Browser streaming uses QUIC streams directly, not SSE-over-HTTP/2.
  The call protocol's subscription model maps onto WebTransport streams
  with no translation loss — a `call.responded` stream over a
  WebTransport bidirectional stream is the native representation.
- The `h3` ALPN reservation in the overview is honored — the
  implementation lands in this crate, not a future one.
- A browser-facing node (a hub with an X.509 cert) serves the same
  operations over `h3` as a non-browser-facing node serves over `h2` —
  the operation registry is transport-agnostic, the projection is
  transport-dependent.
- The WebTransport relay-as-proxy (ADR-034 §5) has a clear home: it's a
  feature that lives in or near `alknet-http`'s `h3` handler, scoped by
  OQ-38.

**Negative:**
- `alknet-http` gains the `wtransport` (or equivalent HTTP/3 +
  WebTransport) dependency behind the `h3` feature. This is a heavier
  dependency than `h2`/`http/1.1`. The feature gate keeps it out of
  non-browser-facing builds.
- WebTransport is still a draft standard (the `wtransport` README notes
  it). The API may change. This is inherent to being an early adopter
  of WebTransport; the `h3` feature gate isolates the risk.
- Browsers require X.509 (ADR-027). A raw-key-only node cannot serve
  WebTransport. This is a browser limitation, not an alknet decision,
  but it means the `h3` feature is useful only on domain-hosted nodes
  with X.509 certs.

## Assumptions

1. **WebTransport is the browser streaming transport for alknet.** The
   browser path is WebTransport, not WebSocket and not SSE-over-HTTP/2.
   SSE remains the streaming projection for non-WebTransport HTTP
   clients (curl, axios over h2); WebTransport is the native path for
   browsers.

2. **The `wtransport` crate (or an equivalent quinn-native HTTP/3 +
   WebTransport implementation) is the dependency for the `h3` feature.**
   The exact library is a two-way-door implementation detail; the
   one-way constraint is that `h3` is served by this crate.

3. **The WebTransport relay-as-proxy (ADR-034 §5) is a separate scope
   decision (OQ-38).** It is not deferred — it's a feature with a clear
   home (the `h3` handler or a sibling relay crate) that gets designed
   when the browser-to-P2P-peer proxy use case becomes concrete.

## References

- [ADR-009](009-one-way-door-decision-framework.md) §"What this framework
  is NOT" — the anti-pattern this ADR corrects (two-way door as deferral)
- [ADR-010](010-alpn-router-and-endpoint.md) — the ALPN router; `h3` is
  one of the ALPNs the `HttpAdapter` registers for
- [ADR-012](012-call-protocol-stream-model.md) — stream-agnostic
  correlation; a WebTransport stream is a QUIC bidirectional stream
- [ADR-027](027-tls-identity-redesign-acme-rawkey-decoupling.md) — the
  browser limitation (no RFC 7250); WebTransport requires X.509
- [ADR-034](034-outgoing-only-x509-and-three-peer-roles.md) §4 (browsers
  are not alknet peers) and §5 (WebTransport relay-as-proxy, recorded
  for this bucket)
- [ADR-036](036-http-to-call-operation-mapping.md) — the SSE projection
  for `h2`/`http/1.1` that WebTransport replaces for the browser path
- `docs/research/alknet-http/phase-0-findings.md` DH-2 — the deferral
  framing this ADR rejects
- `/workspace/wtransport/` — pure-Rust WebTransport (the `h3` feature's
  reference implementation)
- `crates/http/webtransport.md` — the spec that implements the `h3`
  handler