From be57cf9c3b499c8e59a94d13059f16f1456fcae1 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sat, 29 Aug 2026 14:49:11 +0200 Subject: perf(node): filter ICE interfaces to eliminate 5s STUN timeout on VPN/virtual adapters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aioice sends STUN binding requests from every IPv4 interface and waits up to 5 seconds for all to complete. On a machine with Tailscale (wt0), the STUN request never gets a response, adding a fixed 5-second penalty to every WebRTC connection — measured at 6 s total (vs 1-2 s without it). Auto-exclude virtual/VPN adapters (tailscale, virbr, docker, veth, podman, cni) and CGNAT-range IPs (100.64.0.0/10). Operator can override with ice_interfaces in node.toml [node] section for explicit control. Co-Authored-By: Claude Opus 4.6 --- packages/meshbay-node/src/meshbay_node/config.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'packages/meshbay-node/src/meshbay_node/config.py') diff --git a/packages/meshbay-node/src/meshbay_node/config.py b/packages/meshbay-node/src/meshbay_node/config.py index e7f3116..5d9282d 100644 --- a/packages/meshbay-node/src/meshbay_node/config.py +++ b/packages/meshbay-node/src/meshbay_node/config.py @@ -51,6 +51,12 @@ max_concurrent_streams = 8 # Set to false only if every viewer's client is known to decode HEVC itself. transcode_incompatible_video = true +# ICE candidate gathering. By default, virtual/VPN interfaces (Tailscale, +# libvirt, Docker) are auto-excluded — a STUN request that can't reach the +# server holds the WebRTC answer for 5 seconds. Set this to restrict +# gathering to specific interfaces (by OS adapter name). +# ice_interfaces = ["wlp0s20f3", "eth0"] + # Browser and native clients reach this node over WebRTC DataChannel via hub # signaling — no inbound port to open. QUIC is the optional direct path. @@ -133,6 +139,12 @@ class NodeConfig: # that already decode the source codec directly, since transcoding costs # real CPU per concurrent viewer, unlike the copy path. transcode_incompatible_video: bool = True + # ICE candidate gathering: which network interfaces to include or exclude. + # By default, virtual and VPN interfaces (Tailscale, libvirt, Docker) are + # auto-excluded because a STUN request that can't reach the server holds + # the gather for the full 5-second timeout — measured at 6 s total on a + # machine with a Tailscale wt0 interface. + ice_interfaces: list[str] = field(default_factory=list) # include-list overrides auto @dataclass @@ -290,6 +302,9 @@ def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config: cfg.node.max_concurrent_streams, "max_concurrent_streams") cfg.node.transcode_incompatible_video = bool( nd.get("transcode_incompatible_video", cfg.node.transcode_incompatible_video)) + ice_if = nd.get("ice_interfaces") + if isinstance(ice_if, list): + cfg.node.ice_interfaces = [str(s) for s in ice_if] # Multi-group: [[groups]] array if "groups" in raw: -- cgit v1.2.3