Client-side hardening of the CPX airport plugin, squashed from 46 commits
forked at 89c2bb0e. Behaviour changes:
- Unified operation model (§0.4/0.5): one deadline + one AbortSignal + one
persistence commit per operation; per-plugin lock → vault lock hierarchy;
tombstone on delete; every wait (lock, DNS preflight, proxy resolution,
vault decrypt) is bounded by the same budget.
- Routing: direct/proxy auto-fallback with a pre-send guard; proxied https
builds its own CONNECT tunnel (an aborted hung CONNECT closes its socket);
invalid local-proxy ports are refused instead of falling back to :80; the
core's inbound credentials are carried to the local proxy; NAT64 and
site-local IPv6 ranges are non-public.
- Gateways: multi-gateway recovery with one rediscovery per operation,
normalized endpoint paths, signed discovery documents (Ed25519, seq/digest
accept/align/rollback/equivocation), commit order vault → plugin.yaml.
- Subscriptions: a fetched subscription is validated by the core (mihomo -t)
against the current override set before it replaces the profile, inside
the profile write critical section (profile.yaml and override.yaml share
one write queue); schedule fields are read at write time; the first
subscription is activated through the real switch flow; profile deletion
removes the record last so any failure stays retryable.
- Devices: a re-login that replaces a still-valid device records it in the
vault (staleDevices) and retires it after the login, after later
successful fetches and on removal; enroll compensation restores the old
vault and keeps an un-revoked new device for retirement.
- Vault: on Linux the vault is persisted only behind a system secret store
(backend name + ciphertext-prefix canary); otherwise it stays in memory.
A cache-miss read releases the caller at the budget while the lock is held
until the decrypt ends.
- Config caches (plugin.yaml, profile.yaml, override.yaml) can no longer be
rolled back by a late cold read.
- Reference gateway and provider guides updated (deploy contract, discoveryUrls
same-origin rule, /revoke after re-login); https-proxy-agent dropped.
Reviewed in a Codex loop (gpt-6, 38 calls): 74 findings, 68 fixed and
verified, 6 invalid, no backlog. Tests: vitest 501, gateway 106.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* feat: auto-switch profile by WiFi SSID
Add ssidProfileMap (SSID -> profile ID) and ssidProfileRestore to app config.
When connecting to a mapped WiFi SSID, automatically switch to the
corresponding subscription profile. Optionally restore the previous
profile when leaving the mapped SSID. pauseSSID takes priority.
* fix: send profileConfigUpdated after SSID-triggered profile switch
changeCurrentProfile() does not notify the renderer via IPC.
The renderer relies on the profileConfigUpdated event to revalidate
its SWR cache, so SSID-based switching was effective in the backend
but the UI didn't reflect it until a manual refresh.
* perf: event-driven SSID detection on macOS via scutil -w
Replace 30s polling with scutil -w child process on macOS.
scutil -w blocks on a SystemConfiguration key, consuming zero CPU
until the network state changes, then exits. On exit we debounce
500ms and re-check the SSID.
Windows/Linux keep polling, reduced from 30s to 15s.
Also add anti-tight-loop guard (1s minimum restart interval).
* feat: add option to use system DNS when SSID triggers direct mode
- Add 'disableDnsOnPauseSSID' setting to use system default DNS when switching to direct mode
- Clear dns-hijack in TUN config when controlDns is disabled to prevent DNS resolution failure
- Update UI with new setting toggle under SSID direct connection settings
- Add i18n translations for zh-CN, zh-TW, en-US, ru-RU, fa-IR
* fix: persist DNS state and restore on any mode switch
- Save controlDnsBeforePause to appConfig for persistence across restarts
- Move DNS restore logic to patchControledMihomoConfig for unified handling
- Use event-driven restartCore to avoid circular dependency
- Restore DNS state when manually switching from direct to rule/global mode
* feat: add user agent support in profile configuration
* Introduced a new user agent field in profile settings, allowing users to specify a custom user agent.
* Updated relevant components and localization files to support the new user agent feature.
* Added a new pnpm workspace configuration for built dependencies.
* feat: enhance profile settings with user agent input and toggle functionality
* Added a user agent input field to the advanced profile settings.
* Implemented toggle icons for showing/hiding advanced settings.
* Updated layout to accommodate the new user agent input alongside the existing auth token field.
* chore: remove pnpm workspace configuration for built dependencies
1. Add updateTimeout property to IProfileItem interface
2. Add dedicated timeout config option for subscriptions
3. Improve subscription update logic efficiency
4. Use subscriptionTimeout as the smart fallback time
5. Localize
profiles.editInfo.updateTimeout and profiles.editInfo.updateTimeoutPlaceholder for en-US/fa-IR/ru-RU/zh-CN/zh-TW