<!-- The English version is available. --> Thanks for maintaining ClashX Meta. I have searched the issue tracker and did not find an existing report for this regression. ## Environment - macOS 26.5.2 (25F84), Apple Silicon - ClashX Meta v1.4.43 (build 739) - App commit: `eae3bf7c506fe3f6a74bbde2d552a2a9fc7b308a` - Update channel: official Sparkle beta feed - Mihomo v1.19.28 ## Description After upgrading with TUN previously enabled, ClashX Meta can show TUN as enabled and Mihomo's configuration can contain `tun.enable: true`, while the privileged helper has not reapplied the configured Meta TUN DNS to macOS. Disabling TUN and enabling it again immediately fixes the problem. After the toggle, the system DNS changes to the configured Meta TUN DNS and Terminal DNS/ping traffic is handled correctly. This appears to be a user-defaults migration regression introduced by commit [8225db4](https://github.com/MetaCubeX/ClashX.Meta/commit/8225db414b01ba71234295ca818a0430b4ecedd5). ## State observed before the manual toggle ```text restoreTunProxy = 1 tunOverridden = <key absent> ``` The helper was installed, running as root, and able to apply the DNS successfully. Therefore this was not caused by missing helper authorization. ## Steps to reproduce 1. Start from an older ClashX Meta version with TUN enabled, so the existing preference contains `restoreTunProxy = 1`. 2. Upgrade to v1.4.43, where `tunOverridden` has not yet been created. 3. Launch ClashX Meta. 4. Observe that the TUN UI/config may indicate enabled, but the configured Meta TUN DNS has not been applied to the active macOS network service. 5. Disable TUN and enable it again. 6. Observe that the DNS is now applied and traffic works correctly. ## Expected behavior The legacy enabled TUN preference should be migrated, and startup reconciliation should call both the Mihomo API and the privileged helper: ```swift UpdateTun(state: true, dns: ConfigManager.metaTunDNS) ``` No manual off/on toggle should be required after upgrading. ## Actual behavior and suspected cause `readUserIntent()` reads the new key with: ```swift tunOverridden: UserDefaults.standard.bool(forKey: Keys.tunOverridden) ``` For an upgraded installation where the key is absent, this evaluates to `false` even though the legacy `restoreTunProxy` value is `true`. Then `reconcileTun()` only applies TUN when `userIntent.tunOverridden` is true: ```swift if userIntent.tunOverridden { if userIntent.tunEnabled { _ = await enableTun() } else { _ = await disableTun() } } ``` Consequently, startup skips `applyTunOnKernel()` and the helper's `UpdateTun` request. Manually toggling TUN calls `setTunEnabled()`, which sets `tunOverridden = true`, explaining why the off/on workaround fixes it. Relevant source: - [Preference loading](https://github.com/MetaCubeX/ClashX.Meta/blob/eae3bf7c506fe3f6a74bbde2d552a2a9fc7b308a/ClashX/General/Managers/ProxyManager.swift#L86-L98) - [Manual toggle sets tunOverridden](https://github.com/MetaCubeX/ClashX.Meta/blob/eae3bf7c506fe3f6a74bbde2d552a2a9fc7b308a/ClashX/General/Managers/ProxyManager.swift#L194-L212) - [Startup reconciliation is gated by tunOverridden](https://github.com/MetaCubeX/ClashX.Meta/blob/eae3bf7c506fe3f6a74bbde2d552a2a9fc7b308a/ClashX/General/Managers/ProxyManager.swift#L344-L358) - [Helper UpdateTun call](https://github.com/MetaCubeX/ClashX.Meta/blob/eae3bf7c506fe3f6a74bbde2d552a2a9fc7b308a/ClashX/General/Managers/ProxyManager.swift#L372-L388) ## Possible fix When `tunOverridden` is absent, migrate an enabled legacy preference: ```swift let legacyTunEnabled = defaults.bool(forKey: Keys.tunEnabled) let tunOverridden: Bool if defaults.object(forKey: Keys.tunOverridden) != nil { tunOverridden = defaults.bool(forKey: Keys.tunOverridden) } else { tunOverridden = legacyTunEnabled defaults.set(tunOverridden, forKey: Keys.tunOverridden) } ``` This preserves the new "not overridden" behavior for fresh installations while restoring the explicit enabled state for existing users. A regression test covering `restoreTunProxy = true` with a missing `tunOverridden` key would also prevent this from recurring.