From 6daf288af8df5d8aa366db1cd0bfd45d5a6d0988 Mon Sep 17 00:00:00 2001 From: AAA <35992542+TNTcraftHIM@users.noreply.github.com> Date: Mon, 31 Aug 2026 00:57:25 +0800 Subject: [PATCH] docs: document browser NAT traversal boundary (#346) Record the verified Browser ICE observability limits and keep predictive candidates behind a controlled experiment gate. --- docs/research/nat-traversal.md | 114 +++++++++++++++++++++++++++++++++ docs/todo.md | 5 ++ 2 files changed, 119 insertions(+) create mode 100644 docs/research/nat-traversal.md diff --git a/docs/research/nat-traversal.md b/docs/research/nat-traversal.md new file mode 100644 index 0000000..558df02 --- /dev/null +++ b/docs/research/nat-traversal.md @@ -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/) diff --git a/docs/todo.md b/docs/todo.md index 6e44e85..6183f15 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -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