From d85dbf5c0be71d870cc9b60510a2d43a9efc9f69 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 4 Sep 2026 13:44:00 +0200 Subject: fix(client): publish the real local IP on every platform, not just win32 Chromium replaces host candidates with random `.local` mDNS names. aioice has no mDNS resolver on any platform: it logs `Remote candidate ".local" could not be resolved` and drops the candidate. Concealment therefore does not degrade for us, it deletes the only LAN-routable candidate and leaves reflexive pairs -- which fail whenever both peers are behind the same NAT, because that pair needs the router to hairpin. Measured with a Linux client and a node in a libvirt guest: exactly one pair formed, host -> srflx on a shared public address, five binding requests, zero responses, FAILED after 63 s. The same log shows a peer on the guest's own subnet succeeding in 24 ms with a real-IP host candidate. 5022f2b scoped this to win32 from a session where the guest ran the *client*, so the condition encoded "the machine being debugged" rather than "the peer is a node". The peer is always a node, on every platform. The trade is that our private address reaches the hub and the node in the SDP -- the user's own infrastructure, not an arbitrary page. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DtfG7z6wHWj8RKHCvxQtY1 --- packages/meshbay-client/src/main.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'packages/meshbay-client') diff --git a/packages/meshbay-client/src/main.js b/packages/meshbay-client/src/main.js index bd82342..a348007 100644 --- a/packages/meshbay-client/src/main.js +++ b/packages/meshbay-client/src/main.js @@ -40,16 +40,22 @@ const { pathToFileURL } = require('node:url'); // runs (`electron .`) consistent with it. app.commandLine.appendSwitch('class', 'MeshBay'); -// TEST-ENV WORKAROUND (Windows libvirt/KVM guest) — REVISIT BEFORE RELEASE. -// In that guest Chromium hides the host candidate behind a random `.local` -// mDNS name that the node's ICE stack cannot resolve across the KVM bridge, -// so the one working candidate pair is present on some attempts and missing -// on others (60 s ICE timeouts, "2nd connection hangs"). Publishing the real -// local IP removes the dependency. Scoped to win32 so it changes nothing on -// Linux/macOS, where mDNS concealment works and should stay on. -if (process.platform === 'win32') { - app.commandLine.appendSwitch('disable-features', 'WebRtcHideLocalIpsWithMdns'); -} +// Chromium publishes host candidates as random `.local` mDNS names rather +// than as the real local IP. Every peer this client talks to is a node running +// aiortc/aioice, and aioice has no mDNS resolver on any platform: it logs +// `Remote candidate ".local" could not be resolved` and drops the +// candidate outright. Concealment therefore does not degrade here, it removes +// the only LAN-routable candidate and leaves reflexive pairs — which fail +// whenever both peers sit behind the same NAT, since that pair needs the +// router to hairpin. Measured: a Linux client and a node in a libvirt guest +// formed exactly one pair, host -> srflx on a shared public address, and it +// answered none of five binding requests. +// +// This was previously scoped to win32, from a session where the guest ran the +// *client*; the platform that matters is the peer's, not ours, and the peer is +// always a node. The trade is that our private address reaches the hub and the +// node in the SDP — both the user's own infrastructure, not an arbitrary page. +app.commandLine.appendSwitch('disable-features', 'WebRtcHideLocalIpsWithMdns'); const UI_DIR = path.join(__dirname, '..', 'ui'); const SCHEME = 'app'; -- cgit v1.2.3