OpenSurge for Mac App Icon

OpenSurge for Mac

Turn a Mac into an observable, recoverable whole-home gateway with transparent routing and per-device policies.

Latest release macOS 13+ Apple Silicon and Intel packages GPL-3.0-only

简体中文 · English

Download · App guide · Capabilities · Per-device policies · Web GUI · Agent workspace

OpenSurge whole-home gateway dashboard OpenSurge policy and proxy health view
OpenSurge per-device policy view
OpenSurge for Mac is an open-source, Surge-style macOS gateway and control plane. It turns a Mac into the proxy egress for an entire LAN: phones, TVs, PS5 and other game consoles, VR headsets, VMs, and other devices on the same network can obtain DHCP/DNS from the Mac and share policy-controlled connectivity. Each device can also have its own egress policy—send the phone through a proxy, keep the game console DIRECT—with no configuration required on the device itself. - Import an existing mihomo subscription. OpenSurge takes ownership only of gateway-critical fields without replacing its nodes or rules. - Use the Web GUI and menu bar app to see which devices are active, how much traffic they are moving, and which egress chain they use. Under the hood, dnsmasq provides DHCP/DNS, mihomo serves as the proxy engine, and macOS pf plus IPv4 forwarding provide the native gateway path. The repository is also designed as an [AI-agent-friendly engineering workspace](#an-ai-agent-friendly-engineering-workspace): project knowledge is versioned beside the code, risky network behavior has executable proof gates, and virtual-lab plus real-device evidence is fed back into the next engineering loop. ## Capabilities **Friendly App experience** - Use the macOS menu bar app to check status, receive recovery warnings, and open the local Web GUI. - Import sources, configure the network, route individual devices, check proxy health and connectivity, and inspect diagnostics from one control surface. - Follow a recovery state machine through same-LAN DHCP takeover startup, client validation, shutdown, and network restoration. Start with the [OpenSurge for Mac App User Guide](docs/app-user-guide.md). **Gateway and proxying** - Start and stop DHCP/DNS, mihomo, pf NAT, and IPv4 forwarding with rollback. - Provide explicit proxying through mihomo `mixed-port`. - Provide transparent proxying through mihomo TUN on macOS. - Generate a MAC-backed fixed IPv4 lease in DHCP takeover mode, or use a stable main-router IPv4 in same-LAN manual-gateway mode, with an independent egress policy available in either topology. **Observability** - Attribute active-session traffic to DHCP devices or same-LAN registered and currently observed devices, showing per-device connection counts, live upload/download rates, cumulative bytes, and the dominant mihomo egress chain. - Test proxy-node reachability and latency in one place, then switch an applied Selector from the health view. - Probe a fixed catalog of real services through the applied mihomo mixed-port path, showing the three-round median latency, matched rule, and actual egress chain. - Inspect and switch policy groups, inspect imported proxy/rule provider status, and view current connections. - Produce text/JSON status, doctor, logs, and snapshot output, including a partial-failure JSON snapshot for diagnostics and UI use. **Safety and validation** - Configuration validation, TUN-only transparent proxying, rollback, and an explicit recovery contract. - Isolated virtual-LAN validation of risky network behavior before touching a normal LAN. ## Per-device policies One mihomo process can apply independent policies to registered LAN devices. DHCP takeover mode gives each device a MAC-backed fixed IPv4 lease. Same-LAN manual-gateway mode instead uses an IPv4 kept stable by the main router and can assist registration with current traffic plus ARP-neighbor observations. Both topologies emit per-device mihomo selector groups and `SRC-IP-CIDR` rules. The optional JSON policy file lets each device either follow the Mac/global rules or take a dedicated device selector before global rules. It also supports direct device-specific actions such as `REJECT` and domain/IP/protocol/port/rule-provider overlays. Local/private destinations remain direct in dedicated mode. OpenSurge intentionally ships no household templates or third-party rule lists. Operators supply their own policy content; the empty starter file is valid. See [per-device policy overlays](docs/device-policy.md) for the JSON model, precedence, CLI commands, and validation boundary. ## Web GUI and menu bar app If you installed OpenSurge from a package, start with the [OpenSurge for Mac App User Guide](docs/app-user-guide.md). The repository now includes the loopback Go Control API, an embedded React Web GUI, and a read-only native SwiftUI menu bar launcher. For a development build: ```sh make web-install make control-build ./bin/opensurge-control --config examples/config.example.yaml make menubar-build ``` The control service listens only on `127.0.0.1` and prints a one-time Web GUI bootstrap link. The menu bar app shows status and recovery warnings and opens the Web GUI; it deliberately has no gateway start/stop or policy-selection actions. It separates quitting only the menu bar app from quitting OpenSurge. The latter is available only after the gateway data plane has stopped, and quits the menu bar app plus the user-level Control Service. The launchd-managed root Helper remains loaded and idle, so reopening OpenSurge needs no new administrator authorization. The menu bar also provides a separate Uninstall OpenSurge action. Once the gateway is stopped, macOS administrator authorization can remove the app, Control Service, and root Helper while either preserving configuration data for reinstallation or deleting everything. The Web GUI includes a native connectivity page for the applied gateway policy path and links to Net.Coffee for a separate browser-local check. Neither result is presented as proof of a downstream device's DHCP/DNS/TUN path. See the [GUI architecture notes](docs/gui-architecture.zh-CN.md) for the current security and packaging boundary. `make gui-installer` builds a macOS package after requiring real mihomo and dnsmasq binaries. Developer ID signing and notarization are opt-in through the environment variables documented in the architecture notes. Stable GitHub releases contain explicitly named `arm64-unsigned.pkg` and `x86_64-unsigned.pkg` builds; a stable Release is never described as signed, notarized, or Gatekeeper-ready. ### Install an unsigned stable GitHub release Stable releases provide packages for both Apple Silicon and Intel Macs. Download `arm64-unsigned.pkg` for Apple Silicon or `x86_64-unsigned.pkg` for Intel, plus `SHA256SUMS`, from the matching GitHub Release. You can verify downloaded files with `shasum -a 256 -c SHA256SUMS` and the selected package's GitHub build provenance with: ```sh gh attestation verify OpenSurge-for-Mac-*-arm64-unsigned.pkg \ -R YTwsy/OpenSurge-for-Mac gh attestation verify OpenSurge-for-Mac-*-x86_64-unsigned.pkg \ -R YTwsy/OpenSurge-for-Mac ``` Double-click the package. If Gatekeeper blocks it, open **System Settings → Privacy & Security**, choose **Open Anyway**, authenticate, and open the same package again. Do not disable Gatekeeper globally or recursively remove quarantine attributes. Finish Installer with an administrator account, then open **OpenSurge** from `/Applications`. Installation starts the local helper and Control Service, but the gateway remains stopped until you explicitly start it from the control plane. Package upgrades refuse to run while same-LAN DHCP recovery is incomplete. Before replacing payload files, preinstall stops the user control service and menu bar app, runs the installed `omg stop`, and unloads the root helper. The existing config, imported sources, policy data, and runtime history are kept; only a first installation seeds `config.yaml` from the packaged example. ## Transparent proxying TUN is the supported transparent proxy path on macOS. Mihomo `redir-port` and PF TCP redirection are intentionally unsupported because the current Darwin build reports redir as unsupported at runtime. Keep `mihomo.redir_port` and `pf.redirect_tcp_to` at `0`; enable transparent proxying with `transparent.mode: "tun"`. ## Mihomo profiles OpenSurge for Mac can render a managed mihomo config or import an existing mihomo profile. In imported mode, OpenSurge keeps owning gateway-critical fields such as LAN binding, `allow-lan`, the DNS listener/fake-IP range, TUN, `external-controller`, and runtime paths. The imported profile contributes `proxies`, `proxy-providers`, `proxy-groups`, `rule-providers`, and `rules`, plus its non-gateway DNS resolver/filter fields. Preserving fields such as `nameserver-policy`, `proxy-server-nameserver`, and `fake-ip-filter` keeps proxy server hostnames resolvable without allowing the profile to replace the gateway DNS listener or TUN DNS contract. ```yaml mihomo: profile_mode: "imported" profile: "./profiles/home.yaml" ``` Relative `mihomo.profile` paths are resolved from the OpenSurge config file's directory. Relative `path:` entries inside imported `proxy-providers` and `rule-providers` are resolved from the imported mihomo profile's directory. OpenSurge renders `profile.store-selected: true` so mihomo can persist policy group choices across restarts. Preview the final generated mihomo config before starting gateway services: ```sh go run ./cmd/omg doctor --config examples/config.imported-profile.example.yaml go run ./cmd/omg render-mihomo --config examples/config.example.yaml go run ./cmd/omg render-mihomo --config examples/config.imported-profile.example.yaml ``` Use `validate-mihomo` when `mihomo.binary` points to an installed mihomo binary. It renders the final config and runs mihomo's own `-t` validation without starting gateway services. ```sh go run ./cmd/omg validate-mihomo --config examples/config.imported-profile.example.yaml ``` ## CLI usage These commands are intended for development, automation, and diagnostics. Package users can follow the graphical workflow in the [App User Guide](docs/app-user-guide.md). ### Status and diagnostics ```sh go run ./cmd/omg doctor --config examples/config.example.yaml go run ./cmd/omg status --config examples/config.example.yaml go run ./cmd/omg status --config examples/config.example.yaml --format json go run ./cmd/omg logs --config examples/config.example.yaml --tail 50 --format json go run ./cmd/omg snapshot --config examples/config.example.yaml --tail 50 --format json ``` ### Policies, devices, and providers ```sh go run ./cmd/omg policies --config examples/config.imported-profile.example.yaml go run ./cmd/omg policy-select \ --config examples/config.imported-profile.example.yaml \ --group Proxy \ --policy DIRECT # After configuring device_policy.file: go run ./cmd/omg devices --config ./config.yaml --format json go run ./cmd/omg device-policy-select \ --config ./config.yaml \ --device alice-phone \ --slot default \ --policy DIRECT go run ./cmd/omg connections \ --config examples/config.imported-profile.example.yaml \ --format json go run ./cmd/omg providers \ --config examples/config.imported-profile.example.yaml \ --format json go run ./cmd/omg provider-update \ --config examples/config.imported-profile.example.yaml \ --provider demo-provider \ --format json ``` ### Config rendering ```sh go run ./cmd/omg render-mihomo --config examples/config.example.yaml go run ./cmd/omg validate-mihomo \ --config examples/config.imported-profile.example.yaml ``` ### Gateway lifecycle The following commands modify DHCP, DNS, PF, IPv4 forwarding, or mihomo runtime state and require `sudo`: ```sh sudo go run ./cmd/omg start --config examples/config.example.yaml --format json sudo go run ./cmd/omg reload --config examples/config.example.yaml --format json sudo go run ./cmd/omg restart-mihomo --config examples/config.example.yaml --format json sudo go run ./cmd/omg stop --config examples/config.example.yaml --format json ``` Additional behavior: - `policy-select` reads live mihomo policy groups and rejects unknown groups or policies before sending a selection change. - `provider-update --provider ` asks mihomo to refresh the selected proxy provider and returns its refreshed state. - `logs --tail N --format json` returns recent dnsmasq and mihomo log lines with per-file existence and read-error fields. - `snapshot --format json` aggregates status, doctor checks, leases, logs, policy groups, connections, and providers. Mihomo API failures do not prevent the rest of the snapshot from being returned. - `restart-mihomo` restarts only the proxy engine. It does not stop dnsmasq, unload PF, restore IPv4 forwarding, or change host network settings. - `--format json` preserves non-zero failure exit codes and emits structured errors to stderr. Successful `start` and `stop` operations return `command`, `ok`, and `config_path` in their payloads. ## An AI-agent-friendly engineering workspace OpenSurge treats the repository as part of the engineering system, not merely a place to store code. The goal is to make product intent, network safety rules, runtime evidence, and accumulated project knowledge directly legible to both human contributors and coding agents. ### Harness Engineering: engineer the environment around the agent The workspace applies the practical idea behind [Harness Engineering](https://openai.com/index/harness-engineering/): reliable agent work depends on the context, constraints, tools, observability, and acceptance gates around the model. - `AGENTS.md` is the compact entry map: it states product identity, hard network invariants, and which deeper documents an agent must read for a task. - [`docs/agent-wiki/`](docs/agent-wiki/README.md) provides progressively disclosed architecture, decision, and validation context instead of forcing every task to rediscover the repository from scratch. - Machine-readable CLI surfaces such as `status`, `doctor`, `logs`, and `snapshot`, plus deterministic `make` targets and retained artifacts, make the running system observable to agents. - Config validation, TUN-only transparent routing, rollback behavior, isolated labs, and explicit recovery contracts turn safety guidance into enforceable boundaries. ### Loop Engineering: close the loop with executable evidence OpenSurge follows the core of [Loop Engineering](https://addyosmani.com/blog/loop-engineering/): design a repeatable system that can act, observe, verify, recover, and carry the result into the next iteration, rather than relying on a single clever prompt. ```text intent + constraints ↓ AGENTS.md → Agent Wiki → source of truth ↓ implement → fast tests → Virtual LAN Lab ↓ ADB-assisted or manual real-device validation ↓ logs + artifacts + cleanup/recovery proof ↓ durable learning returns to sources/ and wiki/ ↺ ``` The verification layers are complementary: - `make test` and focused UI/control-plane gates provide the fast inner loop. - The Lima + socket_vmnet Virtual LAN Lab makes privileged DHCP, DNS, pf/NAT, forwarding, TUN, policy, rollback, and cleanup behavior reproducible without risking a normal LAN. - Real-device and same-LAN/same-WiFi runners close the physical-topology loop. ADB can collect Android route, DNS, and connectivity evidence while the Mac side correlates dnsmasq/mihomo logs; manual phone checkpoints remain supported when the operator needs to retain direct control. - Recovery is part of acceptance for risky DHCP takeover flows. A successful traffic probe alone is not enough if the router, Mac, or clients cannot be returned to a known-good state. Virtual Lab results do not stand in for real-device behavior, and one physical smoke does not replace the deterministic Lab gates. See the [validation contract](docs/agent-wiki/wiki/concepts/validation-gates.md) for the exact claim each gate is allowed to support. ### Agent Wiki: externalized project memory The [Agent Wiki](docs/agent-wiki/wiki/index.md) applies the LLM Wiki idea of moving durable memory out of a transient context window and into a small, versioned, source-backed knowledge layer: - `docs/agent-wiki/sources/` records stable project briefs, decisions, and validation contracts. - `docs/agent-wiki/wiki/` distills those sources into short, linked pages that an agent can load progressively for the task at hand. - `.codex/hooks.json` integrates the local Session Wiki hook, when installed, so session continuity and compaction can use project-local memory without committing private session state. Only reusable, verified knowledge belongs in this layer. One-off logs, temporary output, unverified guesses, and ordinary TODOs do not. ## License OpenSurge for Mac original code and assets without a separate notice are licensed under the [GNU General Public License version 3 only](LICENSE) (`GPL-3.0-only`). Bundled third-party programs and libraries retain their own licenses; see [Third-Party Notices](THIRD_PARTY_NOTICES.md), including exact corresponding-source links for the bundled mihomo and dnsmasq versions. ## Safety `start` and `stop` are intended to run with `sudo` because they manage DHCP, pf, and IPv4 forwarding. Runtime files are written under `runtime.dir` from the config file. ## Development workflow Use `make test` as the fast default gate. CI currently runs this unit-test gate only, so ordinary pushes and pull requests do not need host networking, passwordless sudo, Lima, or socket_vmnet. Run `make lab-test` locally before committing or reviewing high-risk network changes. This includes changes to DHCP, DNS, mihomo startup/config rendering, pf rules, forwarding/rollback behavior, gateway lifecycle, lab scripts, and example configs that affect runtime traffic. Keep the virtual LAN lab as a local, nightly, or manual gate until a dedicated macOS runner can provide the same controlled host privileges and network isolation. Use `make lab-test-tun` for the supported transparent proxy path. That test keeps clients proxy-free and requires mihomo to log the direct HTTPS connection through its TUN inbound. Use `make lab-test-tun-imported-profile` when changing mihomo profile import or overlay behavior; it runs the same TUN gate with an imported profile fixture. Use `make lab-test-tun-imported-egress` when changing imported provider or policy-selection behavior that should affect transparent TUN traffic; it uses a local HTTP provider and controlled HTTP CONNECT proxy to prove `policy-select` changes the TUN egress path between `DIRECT` and the controlled proxy. Use `make lab-test-tun-device-policy` when changing MAC reservations, per-device selectors, or the device override data path. It proves that two clients receive their own fixed leases, can independently select different TUN egress paths, and enforce a device-level domain `REJECT`. Domain/protocol rule compilation, templates, and HTTP/MRS rule-provider configuration are covered by unit tests; they do not require one Lab run per operator-defined rule. Use `make policy-control-test` for policy-control and machine-readable CLI changes. It starts the real mihomo binary without sudo, dnsmasq, pf, or TUN and checks `policies`, invalid and valid `policy-select`, persisted selection restore after mihomo restart, local DIRECT-vs-proxy egress switching through mihomo's mixed-port, `connections`, `providers`, `provider-update` for file and HTTP proxy providers, and `snapshot` against the live external-controller API. Use `make same-lan-start-tun` and `make same-lan-adb-check` for the narrow same-LAN default-gateway smoke. This gate keeps DHCP disabled, requires TUN, and uses ADB to inspect one Android test device whose gateway and DNS point at the Mac's LAN IP. Use `make same-lan-start-tun-proxy` with `OMG_SAME_LAN_*` upstream-proxy environment overrides to prove one-domain real proxy egress, such as `api.ipify.org`, before importing a full subscription. Use `make same-lan-start-tun-imported-egress` plus `make same-lan-adb-check-imported-egress` for the closer-to-real-device smoke that imports a provider-backed `TunEgress` group, then switches same-LAN TUN traffic from `DIRECT` to a controlled local HTTP CONNECT proxy. These gates do not claim whole-LAN rollout readiness or real remote subscription exits. When ADB is intentionally unavailable, the same imported egress evidence can be collected with manual Android browser probes; see [`tests/same-lan/README.md`](tests/same-lan/README.md#manual-phone-check-without-adb). For a dedicated test Wi-Fi where router DHCP is manually disabled, use `make same-wifi-dhcp-start-imported-egress`, then reconnect the Android client in DHCP mode and run `make same-wifi-dhcp-adb-check-imported-egress`. This separate high-risk runner uses `gateway.mode: "same_wifi_dhcp"` and requires an explicit protected-static-address list plus an operator confirmation that router DHCP is disabled. Its stop gate verifies OpenSurge cleanup, but router DHCP and client automatic addressing must still be restored manually; see [`tests/same-lan/WIFI-DHCP-RUNNER.md`](tests/same-lan/WIFI-DHCP-RUNNER.md). ## Virtual LAN lab The integration lab runs the real macOS gateway against two lightweight Linux clients. Lima provides the clients, while socket_vmnet creates an isolated Layer 2 host network without a competing DHCP server. The test covers DHCP, DNS, ICMP/NAT, direct HTTPS, and explicit HTTPS through mihomo `mixed-port`. ```sh make lab-install make lab-up sudo -v make lab-test make lab-test-tun make lab-test-tun-imported-profile make lab-test-tun-imported-egress make lab-test-tun-device-policy make lab-down ``` The one-time installer adds a root-owned, fixed-function network helper and a narrow sudoers rule for starting, stopping, and inspecting the lab network. The gateway binary itself is not granted passwordless root access; refresh the sudo ticket with `sudo -v` before an end-to-end test. See `tests/lab/README.md` for the topology, safety checks, and troubleshooting steps.