From 07e6e4271ea3a45e9cd364eb6ec05801a653d9e8 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sun, 30 Aug 2026 22:17:30 +0200 Subject: feat: configurable STUN server fallbacks for WebRTC ICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The WebRTC transport relied on a single Google STUN server — if it was unreachable, ICE gathering waited the full 4s timeout. Now four public servers are used by default (Google ×2, Cloudflare, Mozilla), configurable via node.toml, the Node page UI, and the CLI (meshbay-node stun list|add| remove|reset). Changes are hot-swapped on the live transport. 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 5d9282d..b94d03a 100644 --- a/packages/meshbay-node/src/meshbay_node/config.py +++ b/packages/meshbay-node/src/meshbay_node/config.py @@ -18,6 +18,13 @@ except ImportError: log = logging.getLogger(__name__) +DEFAULT_STUN_SERVERS: list[str] = [ + "stun:stun.l.google.com:19302", + "stun:stun1.l.google.com:19302", + "stun:stun.cloudflare.com:3478", + "stun:stun.services.mozilla.com:3478", +] + DEFAULT_CONFIG_PATH = Path.home() / ".config" / "meshbay" / "node.toml" EXAMPLE_CONFIG = """\ @@ -57,6 +64,10 @@ transcode_incompatible_video = true # gathering to specific interfaces (by OS adapter name). # ice_interfaces = ["wlp0s20f3", "eth0"] +# STUN servers for WebRTC ICE candidate gathering (NAT traversal). By default +# four public servers are used; set this to override. +# stun_servers = ["stun:stun.l.google.com:19302", "stun:stun.cloudflare.com:3478"] + # Browser and native clients reach this node over WebRTC DataChannel via hub # signaling — no inbound port to open. QUIC is the optional direct path. @@ -145,6 +156,7 @@ class NodeConfig: # 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 + stun_servers: list[str] = field(default_factory=list) # empty = DEFAULT_STUN_SERVERS @dataclass @@ -305,6 +317,9 @@ def load_config(path: Path = DEFAULT_CONFIG_PATH) -> Config: ice_if = nd.get("ice_interfaces") if isinstance(ice_if, list): cfg.node.ice_interfaces = [str(s) for s in ice_if] + stun = nd.get("stun_servers") + if isinstance(stun, list): + cfg.node.stun_servers = [str(s) for s in stun] # Multi-group: [[groups]] array if "groups" in raw: -- cgit v1.2.3