Skip to content

fix(windows): keep ::1 loopback and kubectl working while connected - #8

Closed
asgarihope wants to merge 3 commits into
devlifeX:mainfrom
asgarihope:fix/windows-tun-ipv6-kubectl
Closed

asgarihope wants to merge 3 commits into
devlifeX:mainfrom
asgarihope:fix/windows-tun-ipv6-kubectl

Conversation

@asgarihope

Copy link
Copy Markdown
Contributor

Summary

Three root causes broke localhost and kubectl on Windows while BiFlow was connected (ADR 0112).

1. Orphaned Mihomo held the controller port (6.2.54)

spawn_mihomo uses kill_on_drop(false) so connectivity survives a helper crash, but when the helper itself restarts (machine reboot, helper reinstall, or a new desktop session), self.child is empty and the previous Mihomo is an orphan still holding 127.0.0.1:19090 and the TUN adapter. The new Mihomo cannot bind, the desktop silently talks to the stale process, and new config is never applied.

Fix: The helper now calls kill_orphaned_mihomo (taskkill /F /IM mihomo.exe on Windows, pkill -x mihomo on Linux) before every spawn to reclaim the port and adapter.

2. strict-route blocked ::1 loopback (6.2.55)

Mihomo v1.19.29 does not apply tun.inet6-address to the Wintun adapter on Windows — GET /configs reports inet4-address but no inet6-address, and the adapter only gets a link-local fe80:: address. With strict-route: true and no inet6 address, sing-tun installs an unconditional WFP "block ipv6" connect filter that only exempts Mihomo, so localhost → ::1 is refused while connected.

Fix: Windows now generates strict-route: false. dns.ipv6: false already keeps AAAA out of fake-ip, so the IPv6 leak surface is minimal. The inet6-address and loopback route-exclude-address are kept for when a Mihomo build that actually sets the inet6 address is available.

3. Sniffer override-destination broke kubectl (6.2.56)

With override-destination: true, when kubectl connected to a cluster API IP (78.109.203.123:443) with a tls-server-name SNI, the sniffer resolved the SNI through fake-ip DNS and overrode the destination to 198.18.x.x. Even a PROCESS-NAME,kubectl.exe,DIRECT rule then connected to the fake-ip and the TLS handshake failed with EOF. The connection never appeared in the live connections list because the override happened before rule evaluation.

Fix: The sniffer now generates override-destination: false so the connection keeps its original destination IP; domain-based rules still use the sniffed SNI.

Bonus: localStorage shim for Node 26 / jsdom 26

Node 26 ships an experimental localStorage global that stays undefined unless --localstorage-file is passed, and jsdom 26 defers to it instead of providing its own, so window.localStorage is also absent under vitest. Added a minimal in-memory Storage shim in its own setupFiles entry that runs before src/test/setup.ts, and guarded the i18n read with typeof localStorage !== "undefined".

Testing

  • cargo test -p iran-split-helper — 27 passed (including kill_orphaned_mihomo_swallows_missing_process)
  • cargo test -p iran-split-mihomo — 33 passed (including windows_disables_strict_route_to_keep_loopback_ipv6)
  • cargo clippy -p iran-split-helper --all-targets -- -D warnings — clean
  • cargo clippy -p iran-split-mihomo --all-targets -- -D warnings — clean
  • cargo fmt --all --check — clean
  • pnpm check — 90 passed
  • pnpm build — OK
  • Verified on a Windows 11 host: localhost:6379, ::1:6379, kubectl get nodes all work while connected with the 6.2.56 build.

asgarihope and others added 3 commits September 30, 2026 13:37
Three root causes broke localhost and kubectl on Windows while BiFlow
was connected (ADR 0112):

1. Orphaned Mihomo held the controller port. spawn_mihomo uses
   kill_on_drop(false) so connectivity survives a helper crash, but when
   the helper restarts (reboot, reinstall, new session) self.child is
   empty and the previous Mihomo is an orphan still holding 127.0.0.1:19090
   and the TUN adapter. The new Mihomo cannot bind, the desktop silently
   talks to the stale process, and new config is never applied. The
   helper now calls kill_orphaned_mihomo (taskkill /F /IM mihomo.exe on
   Windows, pkill -x mihomo on Linux) before every spawn to reclaim the
   port and adapter.

2. strict-route blocked ::1 loopback. Mihomo v1.19.29 does not apply
   tun.inet6-address to the Wintun adapter on Windows (GET /configs
   reports inet4-address but no inet6-address, adapter only gets
   link-local fe80::). With strict-route: true and no inet6 address,
   sing-tun installs an unconditional WFP block-ipv6 connect filter
   that only exempts Mihomo, so localhost to ::1 is refused while
   connected. Windows now generates strict-route: false; dns.ipv6: false
   already keeps AAAA out of fake-ip so the IPv6 leak surface is minimal.

3. Sniffer override-destination broke kubectl. With override-destination:
   true, when kubectl connected to a cluster API IP (78.109.203.123:443)
   with a tls-server-name SNI, the sniffer resolved the SNI through
   fake-ip DNS and overrode the destination to 198.18.x.x. Even a
   PROCESS-NAME,kubectl.exe,DIRECT rule then connected to the fake-ip
   and the TLS handshake failed with EOF. The sniffer now generates
   override-destination: false so the connection keeps its original
   destination IP; domain-based rules still use the sniffed SNI.

Co-authored-by: Cursor <cursoragent@cursor.com>
Node 26 ships an experimental localStorage global that stays undefined
unless --localstorage-file is passed, and jsdom 26 defers to it instead
of providing its own, so window.localStorage is also absent under vitest.
i18n/config.ts read the saved language at module-load time, so every
frontend test failed with "Cannot read properties of undefined (reading
getItem')".

- Add a minimal in-memory Storage shim in its own setupFiles entry that
  runs before src/test/setup.ts (ES module imports are hoisted, so the
  shim must live in its own file listed first).
- Guard the i18n read with typeof localStorage !== "undefined".
- Keep the shim returning string | undefined (not false from &&) so the
  lng option stays typed string | undefined.
- Set environmentOptions.jsdom.url in vite.config.ts so jsdom has a
  valid origin and order setupFiles with the shim first.

Co-authored-by: Cursor <cursoragent@cursor.com>
Version sync from root version file via pnpm version:sync.
Updates package.json, Cargo.toml, tauri.conf.json, and lockfiles.

Co-authored-by: Cursor <cursoragent@cursor.com>
asgarihope added a commit to asgarihope/BiFlow that referenced this pull request Oct 2, 2026
Combine PR devlifeX#8 (strict-route off, inet6-address, orphan Mihomo kill, sniffer override-destination, Node 26 localStorage shim) with the macOS port so one PR covers both platforms. Keep localhost fake-ip-filter and macOS TUN loopback exclusions.

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

Copy link
Copy Markdown
Contributor Author

Closing in favor of a single combined PR. The Windows loopback/kubectl/orphan-Mihomo work from this branch is now merged into feat/macos-support (6.2.57) and reviewed in #10.

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.

1 participant