fix(corp-dns + menubar): bundle plumbing, /usr/local/bin symlink, live clash-api polling#31
Conversation
…ar exit-server + metacubexd UI
…dryrun to keep Case 2 hermetic
…o heal upgrades from old 0o600 binaries
… Info.plist for clash-api poll
…ortURL instead of hardcoded path
…o chmod-heal fires
… so chmod-heal can be observed
…ready targets our binary
|
@copilot please re-review — fixes pushed since the last review |
Re-reviewed the latest pushed fixes at |
There was a problem hiding this comment.
Pull request overview
This PR fixes three related client-side operational gaps discovered during a corp-DNS incident: it propagates corp-DNS settings through the published bundle (instead of relying on per-machine env), moves the bb-vpn convenience symlink to /usr/local/bin so it works under sudo, and updates the menubar app to show the live urltest-selected exit (and serve a bundled metacubexd dashboard) via sing-box’s clash-api on localhost.
Changes:
- Bundle publishing: embed
internal_dns_1andcompany_domaininto the bundle whenwith_corp_dns: true, and strip them when false (with new script-level tests). - Packaging: ship offline metacubexd UI assets in the .pkg, add a preinstall cleanup for legacy UI dirs, and switch the CLI symlink to
/usr/local/bin/bb-vpn. - Menubar: replace external public-IP/country probing with local clash-api polling for the currently selected
autooutbound and add “Open dashboard…” (plus ATS opt-in for local HTTP).
Reviewed changes
Copilot reviewed 59 out of 60 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| scripts/test-publish-bundle | Adds corp-DNS embedding/stripping regression tests and hardens env isolation for test cases. |
| scripts/publish-bundle | Sources .env and conditionally injects/strips corp-DNS fields in the render block before publishing. |
| README.md | Updates menubar behavior documentation (live exit server via clash-api + bundled dashboard). |
| docs/release.md | Documents parse-strict rollout sequencing, bundled UI, symlink location, and updated verification steps. |
| config/client/sing-box-skeleton.json | Removes external UI download config; keeps external_ui: "ui" for bundled UI serving. |
| client/pkg-build/uninstall.sh | Removes /usr/local/bin/bb-vpn symlink (defensively) and sweeps legacy per-user symlinks. |
| client/pkg-build/README.md | Documents ui/ payload requirements/refresh workflow and symlink behavior. |
| client/pkg-build/preinstall.sh | New preinstall script to remove any pre-existing ui/ dir before payload extraction. |
| client/pkg-build/postinstall.sh | Creates /usr/local/bin/bb-vpn symlink and keeps it idempotent/defensive. |
| client/pkg-build/install-page-template.html | Updates operator instructions to use sudo bb-vpn ... and references exit server display. |
| client/pkg-build/build.sh | Enforces presence of UI assets, stages them into the .pkg, and installs preinstall script. |
| client/menubar/Info.plist | Adds ATS exception for local networking to allow HTTP polling of 127.0.0.1:9090. |
| client/menubar/BBVPN/StatusModel.swift | Replaces ifconfig-based country probe with clash-api polling + bundle map caching to display exit server: name (host). |
| client/menubar/BBVPN/EnrollHandler.swift | Adds currentBundleURL for reading /Library/.../bundles/current.json. |
| client/menubar/BBVPN/BBVPNApp.swift | Updates UI row label and adds “Open dashboard…” menu item. |
| client/bb-vpn/pkg/state/state_test.go | Adds tests guarding bundle file-mode contracts and chmod-heal behavior. |
| client/bb-vpn/pkg/state/bundles.go | Makes current.json world-readable (0o644), keeps snapshots root-only, and adds chmod-heal on short-circuit. |
| client/bb-vpn/pkg/launchctl/sync.go | Passes bundle into buildSyncEnv, heals current bundle mode on no-op path, and implements bundle-first corp-DNS precedence. |
| client/bb-vpn/pkg/launchctl/sync_test.go | Adds unit tests for buildSyncEnv precedence/fallback behavior and chmod-heal regression guard. |
| client/bb-vpn/pkg/bundle/bundle.go | Extends bundle render schema with optional corp-DNS fields (omitempty). |
| client/bb-vpn/pkg/bundle/bundle_test.go | Adds roundtrip/omitempty tests and validates legacy compatibility (no Validate() tightening). |
| client/bb-vpn/internal/tests/golden/skeletons/sing-box-skeleton.json | Updates golden skeleton to match removal of external UI download keys. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-on_corp-on_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-on_corp-on_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-on_corp-on_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-on_corp-off_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-on_corp-off_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-on_corp-off_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-off_corp-on_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-off_corp-on_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-off_corp-on_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-off_corp-off_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-off_corp-off_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-xhttp_ts-off_corp-off_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-on_corp-on_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-on_corp-on_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-on_corp-on_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-on_corp-off_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-on_corp-off_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-on_corp-off_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-off_corp-on_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-off_corp-on_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-off_corp-on_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-off_corp-off_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-off_corp-off_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-tcp-vision_ts-off_corp-off_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-on_corp-on_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-on_corp-on_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-on_corp-on_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-on_corp-off_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-on_corp-off_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-on_corp-off_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-off_corp-on_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-off_corp-on_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-off_corp-on_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-off_corp-off_n3/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-off_corp-off_n2/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| client/bb-vpn/internal/tests/golden/expected/proto-all_ts-off_corp-off_n1/sing-box.json | Updates golden expected sing-box config for clash_api UI settings. |
| AGENTS.md | Updates CLI/symlink documentation to reflect /usr/local/bin/bb-vpn. |
| .gitignore | Ignores .claude/scheduled_tasks.lock. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| USR_LOCAL_BIN="/usr/local/bin" | ||
| USR_LOCAL_LINK="$USR_LOCAL_BIN/bb-vpn" | ||
| BB_VPN_TARGET="$APP_SUPPORT/bin/bb-vpn" | ||
| mkdir -p "$USR_LOCAL_BIN" | ||
| if [[ -e "$USR_LOCAL_LINK" || -L "$USR_LOCAL_LINK" ]]; then | ||
| if [[ -L "$USR_LOCAL_LINK" ]]; then |
Summary
Three coordinated fixes that surfaced from one incident: toggling
with_corp_dns: truein the bundle broke every client because the renderer demands corp-DNS values from env vars nothing populates.make publish-bundlepropagatesinternal_dns_1+company_domainto every client; per-Mac plist edits no longer required.buildSyncEnvtakes precedence: bundle-supplied values win over env vars. Backward-compatible viaomitempty.bb-vpnsymlink moved to/usr/local/bin/sosudo bb-vpn …and barebb-vpn …both resolve cleanly.~/.local/bin/isn't insudo'ssecure_path. Postinstall is idempotent + defensive against foreign symlinks.127.0.0.1:9090. Replaces the externalifconfig.cocountry probe. Bundled metacubexd UI ships at/Library/Application Support/bb-dpi/ui/for offline access via "Open dashboard…".bundles/current.jsonloosened to0o644so the menubar (console-user-context) can read tag→host map.Rollout sequencing (load-bearing)
bundle.Parse()usesDisallowUnknownFields(). Pre-PR bb-vpn binaries will REJECT any bundle that carries the newinternal_dns_1/company_domainfields withparse_failed(daemons keep running on cached config; no new bundles land). Deploy the .pkg to every client FIRST, thenmake publish-bundle. Rollback path: republish withwith_corp_dns: false—omitemptydrops the fields cleanly.Today's fleet is one client (macold). Phase-7 fleet rollout will need the same gate.
Multi-phase review history
23 commits = 11 implementation + 12 review-fix. Review pipeline: 5-agent comprehensive → critical re-check → smells → codex iter 1 (4 MAJOR fixed) → codex iter 2 (only MINOR; loop exits) → final 2-agent critical sweep (clean).
Notable bugs caught by review:
PromoteBundlebutTickskipsPromoteBundleon render-no-op path → moved heal into the unconditional path (codex iter 1)publish-bundlepassed hand-editedinternal_dns_1/company_domainthrough verbatim whenwith_corp_dns: false→ strip explicitly (codex iter 1)test-publish-bundle's missing-env case silently passed when dev shell exported the vars →unsetinrun_dryrun(phase 1)NSAllowsLocalNetworking(phase 1)Test plan
make test-bb-vpnclean (all 7 packages)make test-publish-bundleclean (12/12 assertions including new corp-DNS strip/leak guards)make build-menubarproduces universal binarymake build-pkgproduces ad-hoc-signed .pkgwhich bb-vpnreturns/usr/local/bin/bb-vpnsudo bb-vpn statusworks without absolute pathsudo bb-vpn syncexits clean;last_erroremptycurrent.jsonmode0o644after install/Library/Application Support/bb-dpi/ui//proxies/autoreturns 200 with validnowfieldexit server: <name> (<host>)row (operator screenshot)