d2d3bc3e3c
Closes the long-standing "Aura killed the internet while Clash Verge is running" symptom. The cause is unsurprising once you stare at the routing table: even after the user turns Tun mode off in Clash's GUI, the clash-verge-service daemon does NOT remove the split-tunnel routes it installed. They linger as `1/8`, `2/7`, `4/6`, `8/5`, `16/4`, `32/3`, `64/2`, `128.0/1` → `198.18.0.1` (Clash's dead TUN). Aura's half-Internet routes (`0.0.0.0/1` + `128.0.0.0/1`) lose by longest-prefix-match to those foreign /8 / /7 / ... entries — so DNS goes to a non-functional foreign interface and the user-visible internet looks dead. ## New module: aura-cli/src/coexist.rs `scan_foreign_routes_macos(our_iface, pool_cidr) -> Vec<ForeignRoute>` — shells out to `netstat -rn -f inet`, parses the output (incl. macOS's classful shorthand: `1` = `1.0.0.0/8`, `169.254` = `169.254.0.0/16`, etc), filters out: ourselves, loopback (`lo*`), link-local, LAN interfaces (`en*` / `eth*` / `wlan*`), reserved ranges (127/8, 169.254/16, 224/4), and the VPN's own pool. What's left is foreign-VPN territory. `generate_override_cidrs(foreign, max_prefix=24) -> Vec<IpNetwork>` — for each foreign /n, emits two strictly-more-specific /(n+1) routes that together cover exactly the same range but point at Aura's TUN. By longest-prefix-match the kernel routes that traffic through Aura; foreign routes stay in the table untouched (which makes rollback trivial: OsRouteGuard's Drop only undoes what Aura installed). Routes /24 or narrower are skipped — those typically are LAN segments operators don't want hijacked. ## Wired through SplitRoutes `SplitRoutes` gains a `force_vpn_cidrs: Vec<IpNetwork>` field for the override list. `macos_apply_plan`'s `DefaultAction::Vpn` arm now installs them between the direct-host bypasses (most specific — server IP) and the half-Internet catch-alls (least specific). Plan ordering becomes: [0..N] direct CIDR / direct host bypasses (server IP, user-direct CIDRs) [N..N+2K] override routes (2 per foreign /n the scan found) [N+2K..] 0.0.0.0/1 + 128.0.0.0/1 catch-alls ## Wired through client.rs After the existing bypass-injection block, when `default == VPN` and we're on macOS, scan foreign routes and append the generated overrides to `split.force_vpn_cidrs`. Logged at INFO level so the operator can see in the journal exactly which foreign VPN was detected and how many overrides were emitted. ## Tests 9 new unit tests in `coexist::tests`: macOS shorthand parsing (`1` / `2/7` / `192.168.1`), bare IP host routes, garbage rejection, full-table netstat-output parsing against a real captured sample (the user's machine's actual routing table with Clash Verge running), half-splitting, classful Clash pattern coverage, the /24 skip rule, and the doubling property of generate_override_cidrs. All workspace tests still pass; `cargo clippy --workspace --all-targets -- -D warnings` is clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>