Files
Piik/docs/research/nat-traversal.md
T
AAA 6daf288af8 docs: document browser NAT traversal boundary (#346)
Record the verified Browser ICE observability limits and keep predictive candidates behind a controlled experiment gate.
2026-08-31 00:57:25 +08:00

6.5 KiB

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