docs: document browser NAT traversal boundary (#346)

Record the verified Browser ICE observability limits and keep predictive candidates behind a controlled experiment gate.
This commit is contained in:
AAA authored and GitHub committed 2026-08-31 00:57:25 +08:00
1 parent ce4e931100
commit 6daf288af8
2 files changed
+119

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
# Browser NAT Traversal
Last reviewed: 2026-08-31
This document owns evidence for improving direct Browser ICE without adding a
new relay or a custom transport. The current product contract remains standard
WebRTC ICE with STUN discovery and bounded SFU fallback.
## Confirmed Boundaries
- STUN discovers mapped addresses for ICE; it does not carry media.
- Chromium's shared UDP port sends to multiple configured STUN servers. Its
tests retain two srflx candidates when the servers report different mapped
addresses and deduplicate the result when they report the same address.
- A STUN operator observes the client's source address, source port, request
time and protocol metadata. It never receives the DTLS-SRTP media path. An
external STUN endpoint is therefore a metadata dependency, not a media relay.
- The current `STUN_URLS` wire already accepts a bounded list. Adding an
auxiliary STUN destination needs no client compatibility surface, but it has
no product value until a valid Browser observation or prediction consumer
exists.
On 2026-08-31, Chrome 151 on Windows gathered a host plus IPv4 srflx candidate
through the self-hosted STUN endpoint and independently through Cloudflare's
official STUN endpoint while the machine used TUN and fake-IP DNS. With both
configured, Chromium emitted one deduplicated srflx candidate. No raw address or
port was retained. This proves endpoint reachability and candidate
deduplication; it does not classify the NAT or prove another direct path.
## Current Decision
Do not change the flagship STUN configuration in this phase. A second endpoint
does not itself traverse a hard NAT and does not justify a permanent external
metadata dependency. The self-hosted STUN endpoint remains the production
discovery service.
The Browser-only alternatives do not yet have an accepted implementation:
- WebRTC ICE already exchanges observed candidates and performs coordinated
connectivity checks. libp2p's mature hole-punching specification explicitly
keeps private Browser peers on WebRTC because Browser code cannot own or
reuse the underlying UDP, QUIC or TCP socket.
- Tailscale compares mappings observed by different STUN destinations, but
keeps `MappingVariesByDestIP` as a tri-state network report. It retains an
additional mapped endpoint only after repeated observation; it does not turn
one NAT label into hard connection rejection or a predicted port.
- A second STUN destination supplies an observation, not a new path through an
endpoint-dependent NAT. Deploying it without a consumer adds metadata
exposure and candidate traffic without proving more direct connections.
The Browser API is also weaker than the raw-socket netcheck model:
- Different srflx endpoints prove only that the current Browser socket's mapping
varied by destination. An identical endpoint is deduplicated before the
application sees it, so one emitted candidate cannot distinguish stable
mapping from a second STUN destination that did not answer.
- Candidate events expose response completion order, not a specified STUN send
or NAT allocation order. Sorting by configured URL or event time cannot turn
observed ports into a standards-backed sequential delta.
- ICE restart creates a new generation and may use a new socket. Network
interface, VPN/TUN route and CGNAT allocation traffic can all change between
generations.
Consequently Browser observations can support a positive `varies` diagnostic,
but cannot produce the proposed `eim | edm-sequential | edm-random` participant
fact. They cannot safely remove a Peer candidate or save its five-second window.
Port prediction remains the only reachable Browser experiment that might add
direct paths beyond stock ICE. It requires a controlled sequential/random NAT
matrix and two real Browser ICE agents before any product design. No such
controlled Browser matrix is currently available, and the production server is
not an acceptable network-emulation lab. No candidate injection ships without
that gate.
## Rejected Product Inference
Multiple STUN destinations can expose whether one current socket received the
same or different mappings. They do not establish a stable participant-wide NAT
type:
- RFC 5780 behavior discovery requires carefully controlled alternate address
tests, warns that adding destinations can change observed behavior, and says
results can vary by source port or become more restrictive later.
- Mapping behavior does not reveal filtering behavior. A Browser cannot infer
that a Peer pair is impossible from mapping samples alone.
- A global `eim | edm` fact would cross connection generations, interfaces,
VPN/TUN routes and physical network changes that own different sockets.
Therefore NAT samples must not skip a P2P candidate, alter the committed graph,
or become a persistent participant capability. Successful `addIceCandidate()`
parsing would prove only candidate syntax; it would not prove that a predicted
mapping receives authenticated ICE checks. Port prediction and synthetic
candidate injection remain experiments until controlled Browser and target-
network measurements prove both benefit and bounded false rejection. They do
not enter the current protocol or controller.
QUIC on UDP 443 does not change this Browser boundary. WebRTC's ICE agent owns
its UDP sockets and candidate checks; the Web application cannot replace that
transport with a custom QUIC hole-punching socket. LiveKit may use its own
supported WebRTC transport configuration, but that is the bounded SFU path, not
a new Peer route.
## Primary Sources
- [RFC 8445: Interactive Connectivity Establishment](https://datatracker.ietf.org/doc/html/rfc8445)
- [RFC 4787: UDP NAT behavioral requirements](https://datatracker.ietf.org/doc/html/rfc4787)
- [RFC 5780: NAT Behavior Discovery Using STUN](https://datatracker.ietf.org/doc/html/rfc5780)
- [IETF symmetric NAT prediction draft](https://datatracker.ietf.org/doc/html/draft-takeda-symmetric-nat-traversal-00)
- [W3C WebRTC candidate model](https://www.w3.org/TR/webrtc/)
- [Chromium shared-socket STUN tests](https://webrtc.googlesource.com/src/+/refs/heads/main/p2p/base/stun_port_unittest.cc)
- [Tailscale netcheck mapping observations](https://github.com/tailscale/tailscale/blob/main/net/netcheck/netcheck.go)
- [libp2p Browser and DCUtR hole-punching boundary](https://github.com/libp2p/specs/blob/master/connections/hole-punching.md)
- [coturn listener and auxiliary endpoint reference](https://github.com/coturn/coturn/blob/master/examples/etc/turnserver.conf)
- [Cloudflare Realtime STUN service](https://developers.cloudflare.com/realtime/turn/)
+5
View File
@@ -54,6 +54,11 @@ until their stated boundary is met.
Browser interoperability are proved.
12. **Platform output.** Revisit AirPlay/Cast only when a target Browser and
physical receiver prove the live `MediaStream` contract.
13. **NAT inference and predictive candidates.** Keep multi-STUN observations
diagnostic until controlled Browser and target-network evidence proves a
connection-scoped classifier with bounded false rejection. Do not hard-skip
Peer candidates from a participant-wide NAT label or inject predicted ports
into production ICE without a new accepted decision and a measured gate.
## Decision Needed