You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
v1 STUN-dispatch listener + v1 dialer landed on main via #1449 (merge 1d0876da; stack #1446–#1448 merged with it).
Default path: spec STUN on a shared UDP port (offer inferred from first packet). HTTP POST /sdp is opt-in (WebRTCTransportConfig(enable_sdp_http_harness=True)) for py↔py experiments only.
Still open on this issue: v2 listener/dialer flow, go/js interop, true ICE-Lite in aioice, optional UdpMux upstream.
Summary
Tracking issue for a spec-aligned /webrtc-direct listener that demuxes concurrent inbound dials on one advertised UDP port via STUN, including WebRTC-Direct v1 and v2 version-prefix dispatch per libp2p/specs#715.
Built on the ICE-mux primitive (#1397 / spike #1352). Umbrella: #546. Earlier scaffolding (#1309) is superseded for node-to-node by the spec STUN path.
Background
WebRTC-Direct v1 and v2 are no-signaling transports. The listener reconstructs the dialer's offer from the inbound STUN connectivity check:
Read USERNAME = server_ufrag:client_ufrag
Dispatch on version prefix (libp2p+webrtc+v1/ vs libp2p+webrtc+v2/)
For v2: recover client_pwd by stripping libp2p+webrtc+v2/ from server_ufrag, then set the server's own ICE ufrag/pwd to server_ufrag before answering (critical for ICE)
Wire UdpMux into listener.py (spec path default; HTTP /sdp harness opt-in and clearly marked experimental — #1449)
First-contact path via set_unknown_stun_handler: parse USERNAME, validate both ufrag halves (RFC 8839 ice-char / length), reject malformed input
Version dispatch — v1 + reject path (v2 acceptance not implemented yet):
libp2p+webrtc+v1/ → v1 flow
libp2p+webrtc+v2/ → v2 flow (recover client_pwd, set local ICE creds to server_ufrag before answer)
unknown / missing prefix → reject (do not assume v1)
Infer dialer offer from STUN (v1: ufrag/pwd, source IP for c= line — IP only, not port; placeholder fingerprint + DTLS verify disabled inbound per spec; Noise authenticates)
ICE Lite / DTLS roles per spec — partial on main:
DTLS server / setup:passive on listener answer (force_listener_dtls_server_role; inferred offer uses actpass)
True ICE-Lite controlled agent in aioice (dialer's synthesised answer carries a=ice-lite; listener is still a full controlled agent)
register_addr after ICE nomination (via attach_muxed_connection state callback); teardown unregister — covered in UdpMux unit tests (#1448)
Status (2026-08-28)
v1 STUN-dispatch listener + v1 dialer landed on
mainvia #1449 (merge1d0876da; stack #1446–#1448 merged with it).Default path: spec STUN on a shared UDP port (offer inferred from first packet). HTTP
POST /sdpis opt-in (WebRTCTransportConfig(enable_sdp_http_harness=True)) for py↔py experiments only.Still open on this issue: v2 listener/dialer flow, go/js interop, true ICE-Lite in aioice, optional UdpMux upstream.
Summary
Tracking issue for a spec-aligned
/webrtc-directlistener that demuxes concurrent inbound dials on one advertised UDP port via STUN, including WebRTC-Direct v1 and v2 version-prefix dispatch per libp2p/specs#715.v1 is on
main(#1449). v2 and interop remain.Built on the ICE-mux primitive (#1397 / spike #1352). Umbrella: #546. Earlier scaffolding (#1309) is superseded for node-to-node by the spec STUN path.
Background
WebRTC-Direct v1 and v2 are no-signaling transports. The listener reconstructs the dialer's offer from the inbound STUN connectivity check:
USERNAME=server_ufrag:client_ufraglibp2p+webrtc+v1/vslibp2p+webrtc+v2/)client_pwdby strippinglibp2p+webrtc+v2/fromserver_ufrag, then set the server's own ICE ufrag/pwd toserver_ufragbefore answering (critical for ICE)v2 exists because Chromium is removing SDP ICE-credential munging (libp2p/specs#672). Spec PR libp2p/specs#715 is approved but not yet merged; Go reference support has landed (go-libp2p#3520); JS is in progress (js-libp2p#3480).
Internal note / roadmap compiled from the spec:
downloads/AI-PR-REVIEWS/1309/WebRTC_NewSpecs.md(local maintainer doc).Prerequisites
UdpMuxshared-port demux — #1397UdpMuxto aioice / pin tested aioice versionsScope (checklist)
Listener (server) — primary
UdpMuxintolistener.py(spec path default; HTTP/sdpharness opt-in and clearly marked experimental — #1449)set_unknown_stun_handler: parseUSERNAME, validate both ufrag halves (RFC 8839ice-char/ length), reject malformed inputlibp2p+webrtc+v1/→ v1 flowlibp2p+webrtc+v2/→ v2 flow (recoverclient_pwd, set local ICE creds toserver_ufragbefore answer)c=line — IP only, not port; placeholder fingerprint + DTLS verify disabled inbound per spec; Noise authenticates)main:setup:passiveon listener answer (force_listener_dtls_server_role; inferred offer usesactpass)a=ice-lite; listener is still a full controlled agent)register_addrafter ICE nomination (viaattach_muxed_connectionstate callback); teardown unregister — covered inUdpMuxunit tests (#1448)Dialer / SDP seam
make_v1_credential+build_synthetic_answer; spec-pathice_servers=[](no default Google STUN)client_pwdin synthetic answer ufrag/pwd)Interop and docs
newsfragments/1437.feature.rst)masterNon-goals (for this issue)
/webrtcvia relay) polish — see #773 / umbrella #546References
Suggested sequencing
UdpMux).UdpMuxpath.