Skip to content

feat(webrtc): true ICE-Lite listener for WebRTC-Direct (#1512) - #1532

Merged
acul71 merged 9 commits into
libp2p:mainfrom
yashksaini-coder:feat/webrtc-listener-ice-lite
Oct 2, 2026
Merged

acul71 merged 9 commits into
libp2p:mainfrom
yashksaini-coder:feat/webrtc-listener-ice-lite

Conversation

@yashksaini-coder

Copy link
Copy Markdown
Contributor

Closes #1512. The last open blocker on the #1437 WebRTC-Direct track.

The spec requirement

The libp2p WebRTC-Direct spec: "Given that B is publicly reachable, B acts as an [ICE Lite] agent." A Lite agent responds to the controlling dialer's STUN checks, never initiates its own, and stays in the controlled role (RFC 8445 §2.1, §6.1.1). go-libp2p (pion SetLite(true)) and rust-libp2p (webrtc-rs set_lite) get this from a native flag in their ICE stack. aioice has no local lite mode (only remote_is_lite), so its controlled agent still sent connectivity checks — leaving the py listener non-Lite (it interoperated only because the dialer does all the work).

The change

make_connection_ice_lite() makes the muxed listener connection genuinely Lite:

  • check_start — aioice's sole sender of connectivity-check requests — is replaced with a respond-only version that marks the pair valid and completes ICE on the dialer's USE-CANDIDATE nomination. No check ever goes on the wire; binding responses still flow.
  • switch_role is pinned so the agent stays controlled even if a peer sends an ICE-CONTROLLED attribute (a Lite agent never switches — otherwise the respond-only agent could not self-nominate and ICE would fail).

Consent-freshness liveness is unchanged, matching pion's Lite.

Verification

  • go-libp2p v0.49 interop, all four combos green (go→py + py→py, v1+v2); the interop suite instruments 0 outbound connectivity checks from the listener.
  • New tests: a loopback test asserting the listener sends zero establishment checks (respond-only), and a unit test asserting the Lite agent stays controlled under an ICE-CONTROLLED attribute.
  • tests/core/transport/webrtc 246 passed; ruff / format / mypy clean.

The change was adversarially reviewed across five dimensions (override correctness, spec/interop, regression, lifecycle, test fragility); the two confirmed findings — the role-switch edge and a consent-timing test flake — are fixed in this PR.

Notes: touches aioice 0.10.x internals (the same private-API surface attach_muxed_connection / add_ice_connection already rely on), guarded with asserts that fail loudly on an aioice bump.

The spec requires the publicly-reachable server to act as an ICE-Lite agent:
respond to the controlling dialer's STUN checks, never initiate its own, and
stay in the controlled role (RFC 8445 §2.1, §6.1.1). go-libp2p (pion
SetLite(true)) and rust-libp2p (webrtc-rs set_lite) flip a native flag; aioice
has no local lite mode (only remote_is_lite), so its controlled agent still
sends connectivity checks.

make_connection_ice_lite() makes the muxed listener connection genuinely Lite:
- check_start (aioice's sole sender of connectivity-check requests) is replaced
  with a respond-only version that marks the pair valid and completes ICE on the
  dialer's USE-CANDIDATE nomination -- no check ever goes on the wire.
- switch_role is pinned so the agent stays controlled even if a peer sends an
  ICE-CONTROLLED attribute (a Lite agent never switches; otherwise the
  respond-only agent could not self-nominate and ICE would fail).

Binding responses and consent-freshness liveness are unchanged. Verified against
go-libp2p v0.49 (go->py and py->py, v1+v2) -- all complete with zero outbound
connectivity checks from the listener.

Closes libp2p#1512
@yashksaini-coder

yashksaini-coder commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Note on CI: both red checks are unrelated flakes, not from this PR (which only touches libp2p/transport/webrtc/).

  • tox (3.13, interop) — failed at the Nim toolchain install step (choosenim download → curl: (35) Recv failure: Connection reset by peer), before any test ran. Every interop leg that reached the tests passed (tox (3.10/3.11/3.12, interop), all demos, Windows).
  • windows (3.12, core) — failed on tests/core/transport/quic/test_integration.py::test_cid_retirement_under_load (a QUIC load test: QUICStreamTimeoutError: Read timeout), 3336 passed / 1 failed. It's unrelated to this WebRTC change — the same test passes on tox (3.12, core) and tox (3.13, core), and this PR's own tests (TestIceLite::test_lite_agent_stays_controlled, test_listener_is_ice_lite_respond_only) passed on this exact Windows run. A ..._under_load read-timeout on the Windows runner is a timing flake.

A re-run should clear both.

Make ice_lite a flag on the mux connection factory so the listener cannot
forget the override, assert controlled role, document harness vs spec path,
and pin aioice 0.10.x for the private-API surface Lite relies on.

Co-authored-by: Cursor <cursoragent@cursor.com>

@acul71 acul71 left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review (with follow-ups applied)

Earlier AI/maintainer review of this PR rated it Ready for the core ICE-Lite change (make_connection_ice_lite, respond-only check_start, pinned switch_role, tests + 1512.feature.rst). Full write-up: local downloads/AI-PR-REVIEWS/1532/AI-PR-REVIEW-1532-0.md.

Completions landed on this branch (f95e378d)

Addressed the optional in-repo nits from that review:

  1. ice_lite= on UdpMux.add_ice_connection — listener uses ice_lite=True so Lite cannot be forgotten on a second call site.
  2. assert not conn.ice_controlling in make_connection_ice_lite, plus harness docstring noting Lite is STUN/spec-path only (not the experimental /sdp path).
  3. Explicit aioice>=0.10.0,<0.11 in the webrtc extra so the private-API surface Lite relies on stays pinned.

Out of scope for this PR (as discussed): consent config flag, and upstreaming Lite into aioice. Tracking for a native aioice Lite mode: aiortc/aioice#114.

Merge plan

Approve. Waiting for CI/CD checks to go green, then squash-merge.

@yashksaini-coder

Copy link
Copy Markdown
Contributor Author

@acul71 PR ready for merge, no conflicts, and all ci checks passed successfully

@yashksaini-coder

Copy link
Copy Markdown
Contributor Author

Gentle nudge on this one: @acul71 approved it on 2026-09-14, CI is green (38/38) and GitHub reports it mergeable with no conflicts, but it has been sitting open since. Nothing is blocking it that I can see.

@acul71 @sumanjeet0012 — could one of you merge it, or tell me what it still needs? @seetadev flagging it too since it predates the 0.8.0 release and would be good to have in the next one.

Happy to rebase onto current main (d9dd9fb) first if you would rather have a fresh CI run.

@acul71
acul71 merged commit 758328c into libp2p:main Oct 2, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(webrtc): true ICE-Lite controlled agent for the WebRTC-Direct listener

2 participants