Skip to content

feat: macOS support plus Windows localhost/kubectl fixes (6.2.57) - #10

Merged
devlifeX merged 9 commits into
devlifeX:mainfrom
asgarihope:feat/macos-support
Oct 3, 2026
Merged

devlifeX merged 9 commits into
devlifeX:mainfrom
asgarihope:feat/macos-support

Conversation

@asgarihope

@asgarihope asgarihope commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

One PR for both platforms. Replaces #8 (closed) and supersedes the earlier macOS-only #9.

  • macOS: full port (platform backend, helper, DNS, .app / .dmg).
  • Windows: keep localhost / ::1 and kubectl working while connected.
  • 6.2.57 merges those branches and resolves the Mihomo config overlap.

Windows (#8)

  • Mihomo v1.19.29 does not apply inet6-address on Wintun, so strict-route: true installed a WFP “block ipv6” filter and localhost → ::1 failed. Windows now uses strict-route: false, emits tun.inet6-address: fdfe:dcba:9876::1/126, and keeps dns.ipv6: false (ADR 0112).
  • Helper kill_orphaned_mihomo before every spawn so a leftover Mihomo cannot hold :19090 / the TUN.
  • Sniffer override-destination: false so kubectl to a real cluster IP is not rewritten to a fake-ip from TLS SNI.
  • Node 26 / jsdom 26 localStorage shim so frontend tests load.

macOS

  • New iran-split-platform-macos, helper macos.rs (launchd + /usr/sbin/networksetup DNS).
  • tun_name → utun9, dns_port → 53.
  • localhost / +.localhost in fake-ip-filter; route-exclude-address for 127.0.0.0/8 and ::1/128 on Windows and macOS.
  • Clearing Hiddify’s system proxy actually sets HTTP/HTTPS/SOCKS off (was replaying the snapshot’s enabled flags → browser 502 on localhost).
  • ./build.sh macos DMG fallback deletes leftover rw.*.dmg and runs from target/release/bundle/dmg.

Testing

  • cargo test / clippy for iran-split-mihomo and iran-split-helper
  • pnpm check (198 UI tests + 90 script tests)
  • Built BiFlow_6.2.55_aarch64.dmg on Apple Silicon; version is now 6.2.57 after the merge

Cross-platform

Linux TUN routing unchanged (no loopback route-exclude). helper_version is #[serde(default)]. Windows-only and macOS-only paths stay cfg-gated.

asgarihope and others added 9 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>
…mihomo support

- New crate iran-split-platform-macos implementing PlatformBackend with
  macOS paths, Hiddify/Happ .app bundle discovery, system_proxy via
  networksetup, TUN detection via GET /configs (ADR 0104 mirror)
- New helper module macos.rs: launchd daemon, socket permissions,
  apply_system_dns/restore_system_dns (networksetup → 127.0.0.1),
  using absolute /usr/sbin/networksetup path for launchd's minimal PATH
- Config: normalize TUN name to utun9 and DNS port to 53 on macOS
  (kernel rejects non-utun names; LAN router DNS bypasses TUN)
- Mihomo: add Platform::Macos, routing-mark Linux-only, process bypass
- Clients: add DriverPlatform::Macos variant
- Core: add helper_version to RuntimeHealth/StackSnapshot for
  auto-reinstall on version mismatch; populate on all backends + CLI
- Helper: start() calls apply_system_dns, cleanup() calls restore_system_dns
  (cfg macOS); spawn_local_proxy uses open for .app bundles; reject
  Linux ELF binaries in resolve_macos_binary; double start timeout

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

- helper_install: install_macos stages helper+mihomo+plist via osascript
  admin prompt, computes real mihomo_sha256 (not empty), writes
  tun_name into helper.toml
- lib.rs: prepare_stack_start version-mismatch check auto-reinstalls
  stale helper on Connect; macOS NativeBackend + paths wiring
- diagnostics: macOS gate for environment snapshot trigger
- deps: macOS dependency discovery (Hiddify/Happ .app bundles)
- github_update: macOS update flow (DMG/AppImage path)
- hiddify_reset: macOS Fresh Hiddify start (kill by Mach-O image match)
- environment/mod.rs: macOS collector gate
- tauri.macos.conf.json: .app/.dmg bundle config, icon.icns
- tauri.conf.json: macOS bundle resources

Co-authored-by: Cursor <cursoragent@cursor.com>
- build.sh: ci-macos target, macOS packaging stages (compile/dmg/collect),
  prefetch, xwin alignment
- scripts/build-plan.mjs: macOS bundle config
- scripts/check-bundled-assets.mjs: macOS asset verification
- scripts/stage-helper.sh: macOS helper staging (no cross-compile needed)
- resources/external-assets.toml: macOS mihomo asset entry
- Cargo.toml/Cargo.lock: iran-split-platform-macos workspace member
- vendor/mihomo/darwin/mihomo: macOS arm64 mihomo binary

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

- presets.ts: macOS download URLs for Hiddify, OpenVPN, Happ, v2rayN, Windscribe
- AGENTS.md: append macOS lessons — utun naming, .app launch via open,
  mihomo_sha256 fix, networksetup absolute path for launchd PATH,
  DNS system resolver redirect, Mach-O binary validation, start timeout
- version: 6.2.52 → 6.2.54 (pnpm version:sync)

Co-authored-by: Cursor <cursoragent@cursor.com>
Skip fake-ip for localhost, exclude loopback from the TUN, and actually turn Hiddify's system proxy off so browsers stop getting 502 on local dev servers. Also make the DMG fallback survive leftover hdiutil files.

Co-authored-by: Cursor <cursoragent@cursor.com>
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 asgarihope changed the title feat(macOS): full macOS support with localhost fix and DMG (6.2.55) feat: macOS support plus Windows localhost/kubectl fixes (6.2.57) Oct 2, 2026
@devlifeX
devlifeX merged commit 546d13e into devlifeX:main Oct 3, 2026
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.

2 participants