diff --git a/CLAUDE.md b/CLAUDE.md index 5e7e81f..a9b89bf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -183,9 +183,10 @@ cd web/website && npm run gen:tokens **系统扩展能被 realize 的硬性要求**(缺一即 `code=4` / 静默拒) - **自包含**:`PacketTunnel` target 设 `OTHER_LDFLAGS = ""`(切断继承项目级 CocoaPods 链接标志,否则会把 `flutter_secure_storage` 链进扩展);`Libbox.xcframework` **只 Link 不 Embed**(它是静态库)。验证:`otool -L` 扩展二进制应**零 `@rpath` 外部依赖**。 -- **bundle 名 = 标识符**:`PRODUCT_NAME = com.pangolin.pangolin.PacketTunnel`。 +- **bundle 名 = 标识符**:`PRODUCT_NAME = com.yanmeiai.pangolin.PacketTunnel`。 - 扩展 `Info.plist` 必须有 **`NSSystemExtensionUsageDescription`**(网络扩展类别强制,主 app 的不顶用)。 -- **App Group 用 macOS 原生格式 `.`**(`BYL4KQHMTN.com.pangolin.pangolin`,非 iOS 的 `group.` 前缀);`NEMachServiceName` 以其为前缀。 +- **App Group 用 macOS 原生格式 `.`**(`44WULXM6SV.com.yanmeiai.pangolin`,非 iOS 的 `group.` 前缀);`NEMachServiceName` 以其为前缀。 +- **签名身份单一真相源 `scripts/signing.env`**(Team ID / 实体名 / Bundle ID / App Group)。shell 脚本 source 它;原生文件(pbxproj/entitlements/plist/swift)的字面量由 `node scripts/gen-signing.mjs` 从它回填(**勿手改这些标识符**),`ci/check-codegen-drift.sh` 有 drift 闸。迁账号/改 Bundle ID 只改 signing.env 跑一次 codegen。详见 `docs/apple-account-migration-runbook.html`。 - 沙箱扩展补 `network.client` / `network.server`;`get-task-allow=false` + 签名加 `--timestamp`。 **libbox / NetworkExtension 集成铁律**(改 `PacketTunnelProvider.swift` 注意) @@ -206,7 +207,7 @@ cd web/website && npm run gen:tokens 同样内嵌 libbox,与 macOS 同「CommandServer 模型」,但形态/打包不同: -- **iOS/iPad**:`NEPacketTunnelProvider` 扩展(`client/ios/PacketTunnel/`),经 `NETunnelProviderManager` 管理(非 sysext);App Group 用 **iOS 的 `group.` 前缀**(`group.com.pangolin.pangolinVpn`)。`ctl_info`/`sockaddr_ctl` iOS SDK 不发,靠 `PacketTunnel-Bridging-Header.h` 手写声明。 +- **iOS/iPad**:`NEPacketTunnelProvider` 扩展(`client/ios/PacketTunnel/`),经 `NETunnelProviderManager` 管理(非 sysext);App Group 用 **iOS 的 `group.` 前缀**(`group.com.yanmeiai.pangolin`)。`ctl_info`/`sockaddr_ctl` iOS SDK 不发,靠 `PacketTunnel-Bridging-Header.h` 手写声明。 - **Android**:`PangolinVpnService`(`client/android/.../PangolinVpnService.kt`),VpnService + libbox **同进程**;明文联调 API 需 manifest `usesCleartextTraffic`(Android 9+ 默认禁 http)。 - iOS/Android 与 macOS 同走 Dart `VpnNativeBridge`,所以 Dart 侧修复(状态/统计流)四端共享。 @@ -216,7 +217,7 @@ cd web/website && npm run gen:tokens - ⚠️ **Android libbox Java 包名是 `io.nekohasekai.libbox`**(不是 `libbox`),Kotlin import 用前者。 **真机装机(老忘——有现成脚本,别手搓 xcodebuild/签名)**: -- **iOS**:`API_URL=https://api.yanmeiai.com bash scripts/local_test.sh ipad "<设备名/id>"`(`local_test.sh ios-devices` 列已连设备)。脚本自动:flutter build ipa(**公司分发证书** Apple Distribution: Yanmei / Team `BYL4KQHMTN`,ad-hoc)→ 核验签名主体(防无声退回个人证书)→ `xcrun devicectl` 装机。`API_URL` 决定客户端连哪个控制面(prod = `https://api.yanmeiai.com`)。 +- **iOS**:`API_URL=https://api.yanmeiai.com bash scripts/local_test.sh ipad "<设备名/id>"`(`local_test.sh ios-devices` 列已连设备)。脚本自动:flutter build ipa(**公司分发证书** Apple Distribution: Yanmei AI LLC / Team `44WULXM6SV`,ad-hoc)→ 核验签名主体(防无声退回个人证书)→ `xcrun devicectl` 装机。`API_URL` 决定客户端连哪个控制面(prod = `https://api.yanmeiai.com`)。 - iOS libbox 是 **gitignore 产物**,新 worktree 常缺 → 从主仓拷免重建:`cp -R /Users/wangjia/code/pangolin/client/ios/Frameworks/Libbox.xcframework client/ios/Frameworks/`(routing 等功能不碰 libbox 接口,主仓那份兼容)。 - 新设备首次装报 `0xe8008012`(描述文件不含 UDID)→ 需 xcodebuild `-allowProvisioningUpdates -allowProvisioningDeviceRegistration`(脚本 die 里给了兜底命令);chen 的设备(chen-macbook/chen-iphone)已注册。装机走**公司**分发证书,禁个人开发证书(记忆 `ios-install-team-cert`)。 diff --git a/ci/check-codegen-drift.sh b/ci/check-codegen-drift.sh index 8df6462..f605668 100755 --- a/ci/check-codegen-drift.sh +++ b/ci/check-codegen-drift.sh @@ -58,3 +58,10 @@ fi echo "→ 校验原型 data-i18n key 全 resolve (check-proto-i18n.mjs) ..." node design/prototype/tools/check-proto-i18n.mjs echo "✅ 原型 i18n 无漂移:I18N 块与单源一致、data-i18n 全 resolve。" + +# ── 签名身份单源:scripts/signing.env → 原生文件字面量(pbxproj/entitlements/plist/swift) ── +# 迁账号/改 Bundle ID 只改 signing.env,跑 gen-signing.mjs 回填;此闸防「改了 env 没重生成」 +# 或「手改了原生里的 Team/Bundle/App Group」导致签名标识符与单源分叉。 +echo "→ 校验签名标识符与 scripts/signing.env 一致 (scripts/gen-signing.mjs --check) ..." +node scripts/gen-signing.mjs --check +echo "✅ 签名标识符无漂移:原生字面量与 signing.env 单源一致。" diff --git a/client/ios/PacketTunnel/MemoryMonitor.swift b/client/ios/PacketTunnel/MemoryMonitor.swift index 1356b58..e347b43 100644 --- a/client/ios/PacketTunnel/MemoryMonitor.swift +++ b/client/ios/PacketTunnel/MemoryMonitor.swift @@ -40,7 +40,7 @@ final class MemoryMonitor { // ── 状态 ──────────────────────────────────────────────────── private var timer: DispatchSourceTimer? - private let queue = DispatchQueue(label: "com.pangolin.memory-monitor", qos: .utility) + private let queue = DispatchQueue(label: "com.yanmeiai.pangolin.memory-monitor", qos: .utility) private(set) var peakResidentBytes: UInt64 = 0 private(set) var minAvailableBytes: UInt64 = UInt64.max private(set) var sampleCount: Int = 0 @@ -48,7 +48,7 @@ final class MemoryMonitor { init( interval: TimeInterval = 10, warningThreshold: UInt64 = 5 * 1024 * 1024, - subsystem: String = "com.pangolin.pangolinVpn.PacketTunnel" + subsystem: String = "com.yanmeiai.pangolin.PacketTunnel" ) { self.interval = interval self.warningThreshold = warningThreshold diff --git a/client/ios/PacketTunnel/PacketTunnel.entitlements b/client/ios/PacketTunnel/PacketTunnel.entitlements index df58bc1..4d64c65 100644 --- a/client/ios/PacketTunnel/PacketTunnel.entitlements +++ b/client/ios/PacketTunnel/PacketTunnel.entitlements @@ -2,15 +2,15 @@ @@ -25,7 +25,7 @@ com.apple.security.application-groups - group.com.pangolin.pangolinVpn + group.com.yanmeiai.pangolin diff --git a/client/ios/PacketTunnel/PacketTunnelProvider.swift b/client/ios/PacketTunnel/PacketTunnelProvider.swift index 6f2c35f..b8bd6c5 100644 --- a/client/ios/PacketTunnel/PacketTunnelProvider.swift +++ b/client/ios/PacketTunnel/PacketTunnelProvider.swift @@ -15,9 +15,9 @@ import Network import NetworkExtension import os -private let log = Logger(subsystem: "com.pangolin.pangolinVpn.PacketTunnel", category: "provider") +private let log = Logger(subsystem: "com.yanmeiai.pangolin.PacketTunnel", category: "provider") // iOS App Group 用 group. 前缀(非 macOS 的 . 前缀)。 -private let appGroup = "group.com.pangolin.pangolinVpn" +private let appGroup = "group.com.yanmeiai.pangolin" final class PacketTunnelProvider: NEPacketTunnelProvider { private var commandServer: LibboxCommandServer? diff --git a/client/ios/Runner.xcodeproj/project.pbxproj b/client/ios/Runner.xcodeproj/project.pbxproj index e6128aa..5111790 100644 --- a/client/ios/Runner.xcodeproj/project.pbxproj +++ b/client/ios/Runner.xcodeproj/project.pbxproj @@ -265,7 +265,7 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 1510; - ORGANIZATIONNAME = com.pangolin; + ORGANIZATIONNAME = "Yanmei AI LLC"; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; @@ -481,14 +481,14 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolinVpn; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -610,14 +610,14 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolinVpn; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -634,14 +634,14 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolinVpn; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -657,7 +657,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Frameworks", @@ -670,7 +670,7 @@ "@executable_path/../../Frameworks", ); OTHER_LDFLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolinVpn.PacketTunnel; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.PacketTunnel; PRODUCT_NAME = PacketTunnel; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -689,7 +689,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Frameworks", @@ -702,7 +702,7 @@ "@executable_path/../../Frameworks", ); OTHER_LDFLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolinVpn.PacketTunnel; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.PacketTunnel; PRODUCT_NAME = PacketTunnel; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -721,7 +721,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Frameworks", @@ -734,7 +734,7 @@ "@executable_path/../../Frameworks", ); OTHER_LDFLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolinVpn.PacketTunnel; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.PacketTunnel; PRODUCT_NAME = PacketTunnel; SDKROOT = iphoneos; SKIP_INSTALL = YES; diff --git a/client/ios/Runner/Runner.entitlements b/client/ios/Runner/Runner.entitlements index 9699003..ba70b25 100644 --- a/client/ios/Runner/Runner.entitlements +++ b/client/ios/Runner/Runner.entitlements @@ -2,16 +2,16 @@ com.apple.security.application-groups - group.com.pangolin.pangolinVpn + group.com.yanmeiai.pangolin diff --git a/client/ios/Runner/VpnManager.swift b/client/ios/Runner/VpnManager.swift index 486fa11..406911d 100644 --- a/client/ios/Runner/VpnManager.swift +++ b/client/ios/Runner/VpnManager.swift @@ -65,9 +65,9 @@ class VpnManager: NSObject { private var tunnelManager: NETunnelProviderManager? // Bundle ID 与 PacketTunnel target PRODUCT_BUNDLE_IDENTIFIER 保持同步 - private let extensionBundleId = "com.pangolin.pangolinVpn.PacketTunnel" + private let extensionBundleId = "com.yanmeiai.pangolin.PacketTunnel" // App Group:与 PacketTunnel extension 共享 UserDefaults 容器 - private let appGroup = "group.com.pangolin.pangolinVpn" + private let appGroup = "group.com.yanmeiai.pangolin" private var statusObserver: NSObjectProtocol? diff --git a/client/macos/PacketTunnel/Info.plist b/client/macos/PacketTunnel/Info.plist index 0828c9d..8ceb099 100644 --- a/client/macos/PacketTunnel/Info.plist +++ b/client/macos/PacketTunnel/Info.plist @@ -12,11 +12,11 @@ NetworkExtension NEMachServiceName - BYL4KQHMTN.com.pangolin.pangolin.PacketTunnel + 44WULXM6SV.com.yanmeiai.pangolin.PacketTunnel NEProviderClasses com.apple.networkextension.packet-tunnel diff --git a/client/macos/PacketTunnel/PacketTunnel.entitlements b/client/macos/PacketTunnel/PacketTunnel.entitlements index 0bc1e86..83c585a 100644 --- a/client/macos/PacketTunnel/PacketTunnel.entitlements +++ b/client/macos/PacketTunnel/PacketTunnel.entitlements @@ -21,7 +21,7 @@ 非 iOS 的 group. 前缀——sysextd realize 前校验请求方/扩展时要求此格式。 --> com.apple.security.application-groups - BYL4KQHMTN.com.pangolin.pangolin + 44WULXM6SV.com.yanmeiai.pangolin diff --git a/client/macos/PacketTunnel/PacketTunnelProvider.swift b/client/macos/PacketTunnel/PacketTunnelProvider.swift index 69d923a..405f6e9 100644 --- a/client/macos/PacketTunnel/PacketTunnelProvider.swift +++ b/client/macos/PacketTunnel/PacketTunnelProvider.swift @@ -14,10 +14,10 @@ import Network import NetworkExtension import os -private let log = Logger(subsystem: "com.pangolin.pangolin.PacketTunnel", category: "provider") +private let log = Logger(subsystem: "com.yanmeiai.pangolin.PacketTunnel", category: "provider") // macOS 原生 App Group 格式 .(非 iOS 的 group. 前缀)。 // sysextd 校验请求方 app 时要求此格式;用 group. 式会在 realize 暂存前被拒。 -private let appGroup = "BYL4KQHMTN.com.pangolin.pangolin" +private let appGroup = "44WULXM6SV.com.yanmeiai.pangolin" final class PacketTunnelProvider: NEPacketTunnelProvider { private var commandServer: LibboxCommandServer? diff --git a/client/macos/Runner.xcodeproj/project.pbxproj b/client/macos/Runner.xcodeproj/project.pbxproj index b2b90ca..1489d62 100644 --- a/client/macos/Runner.xcodeproj/project.pbxproj +++ b/client/macos/Runner.xcodeproj/project.pbxproj @@ -662,7 +662,7 @@ CURRENT_PROJECT_VERSION = 55; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pangolin_vpn.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pangolin_vpn"; @@ -677,7 +677,7 @@ CURRENT_PROJECT_VERSION = 55; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pangolin_vpn.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pangolin_vpn"; @@ -692,7 +692,7 @@ CURRENT_PROJECT_VERSION = 55; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/pangolin_vpn.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/pangolin_vpn"; @@ -758,7 +758,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -893,7 +893,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -916,7 +916,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -961,7 +961,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 55; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -982,8 +982,8 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_LDFLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.PacketTunnel; - PRODUCT_NAME = com.pangolin.pangolin.PacketTunnel; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.PacketTunnel; + PRODUCT_NAME = com.yanmeiai.pangolin.PacketTunnel; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -1011,7 +1011,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 55; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -1032,8 +1032,8 @@ MTL_FAST_MATH = YES; OTHER_CODE_SIGN_FLAGS = "--timestamp"; OTHER_LDFLAGS = ""; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.PacketTunnel; - PRODUCT_NAME = com.pangolin.pangolin.PacketTunnel; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.PacketTunnel; + PRODUCT_NAME = com.yanmeiai.pangolin.PacketTunnel; PROVISIONING_PROFILE_SPECIFIER = "Pangolin PacketTunnel DevID"; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -1060,7 +1060,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 55; - DEVELOPMENT_TEAM = BYL4KQHMTN; + DEVELOPMENT_TEAM = 44WULXM6SV; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -1079,8 +1079,8 @@ MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.PacketTunnel; - PRODUCT_NAME = com.pangolin.pangolin.PacketTunnel; + PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin.PacketTunnel; + PRODUCT_NAME = com.yanmeiai.pangolin.PacketTunnel; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; diff --git a/client/macos/Runner/Configs/AppInfo.xcconfig b/client/macos/Runner/Configs/AppInfo.xcconfig index e4da5ca..59bf6db 100644 --- a/client/macos/Runner/Configs/AppInfo.xcconfig +++ b/client/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = pangolin_vpn // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin +PRODUCT_BUNDLE_IDENTIFIER = com.yanmeiai.pangolin // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2026 com.pangolin. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2026 Yanmei AI LLC. All rights reserved. diff --git a/client/macos/Runner/DebugProfile.entitlements b/client/macos/Runner/DebugProfile.entitlements index ffa43a8..d604622 100644 --- a/client/macos/Runner/DebugProfile.entitlements +++ b/client/macos/Runner/DebugProfile.entitlements @@ -21,12 +21,12 @@ keychain-access-groups - $(AppIdentifierPrefix)com.pangolin.pangolin + $(AppIdentifierPrefix)com.yanmeiai.pangolin diff --git a/client/macos/Runner/MainFlutterWindow.swift b/client/macos/Runner/MainFlutterWindow.swift index 799544c..19a9f14 100644 --- a/client/macos/Runner/MainFlutterWindow.swift +++ b/client/macos/Runner/MainFlutterWindow.swift @@ -5,7 +5,7 @@ import ServiceManagement class MainFlutterWindow: NSWindow { // launch_at_startup 插件在 macOS 没自带实现,需宿主接 MethodChannel。学 Tailscale 用 // SMAppService.loginItem 注册内置的 PangolinLoginHelper(登录时它带 --autostart 拉起主 app)。 - private let loginHelperID = "com.pangolin.pangolin.LoginHelper" + private let loginHelperID = "com.yanmeiai.pangolin.LoginHelper" override func awakeFromNib() { let flutterViewController = FlutterViewController() diff --git a/client/macos/Runner/Release.entitlements b/client/macos/Runner/Release.entitlements index 020cb74..ce61c56 100644 --- a/client/macos/Runner/Release.entitlements +++ b/client/macos/Runner/Release.entitlements @@ -21,14 +21,14 @@ keychain-access-groups - $(AppIdentifierPrefix)com.pangolin.pangolin + $(AppIdentifierPrefix)com.yanmeiai.pangolin com.apple.security.application-groups - BYL4KQHMTN.com.pangolin.pangolin + 44WULXM6SV.com.yanmeiai.pangolin diff --git a/client/macos/Runner/VpnChannel.swift b/client/macos/Runner/VpnChannel.swift index e2105b9..d607f63 100644 --- a/client/macos/Runner/VpnChannel.swift +++ b/client/macos/Runner/VpnChannel.swift @@ -17,15 +17,15 @@ import os.log // os_log + %{public} —— 让日志在 Console.app / `log show` 里可见(NSLog 的 %@ 参数会被 // 系统 redact 成 ,排障时看不到内容)。os_log(C API)自 macOS 10.12 起可用, -// 兼容 Runner 的 10.15 部署目标。过滤:`log show --predicate 'subsystem == "com.pangolin.pangolin"'`。 -private let vpnLogObj = OSLog(subsystem: "com.pangolin.pangolin", category: "vpn") +// 兼容 Runner 的 10.15 部署目标。过滤:`log show --predicate 'subsystem == "com.yanmeiai.pangolin"'`。 +private let vpnLogObj = OSLog(subsystem: "com.yanmeiai.pangolin", category: "vpn") private func vpnLog(_ message: String) { os_log("%{public}@", log: vpnLogObj, type: .default, message) NSLog("[pangolin/vpn] %@", message) // 同时进 stderr,flutter run 控制台也能看到 } final class VpnChannel: NSObject { - private static let tunnelBundleId = "com.pangolin.pangolin.PacketTunnel" + private static let tunnelBundleId = "com.yanmeiai.pangolin.PacketTunnel" private var statusSink: FlutterEventSink? private var statsSink: FlutterEventSink? diff --git a/client/macos/build_login_helper.sh b/client/macos/build_login_helper.sh index 9dd5a21..b835c16 100755 --- a/client/macos/build_login_helper.sh +++ b/client/macos/build_login_helper.sh @@ -5,7 +5,7 @@ set -euo pipefail HELPER_NAME="PangolinLoginHelper" -HELPER_ID="com.pangolin.pangolin.LoginHelper" +HELPER_ID="com.yanmeiai.pangolin.LoginHelper" SRC="${SRCROOT}/login_helper/main.swift" DEST="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Library/LoginItems/${HELPER_NAME}.app" WORK="${DERIVED_FILE_DIR}/login_helper" diff --git a/scripts/gen-signing.mjs b/scripts/gen-signing.mjs new file mode 100644 index 0000000..5d375ef --- /dev/null +++ b/scripts/gen-signing.mjs @@ -0,0 +1,150 @@ +#!/usr/bin/env node +// gen-signing.mjs — 从 scripts/signing.env(单一真相源)回填所有原生文件里的 +// Apple 签名标识符(Team ID / Bundle ID / App Group / 签名主体)。 +// +// 为什么用 codegen 而非硬编码:Apple 工具链要求这些值以字面量出现在 pbxproj / +// entitlements / plist / swift 里,无法像 shell 那样 source 一个变量;又分散在 +// 十几处,迁账号极易漏。本脚本让 signing.env 成为唯一手改点,一键回填全部字面量。 +// +// 关键设计:所有匹配都是**结构化正则**(匹配 `DEVELOPMENT_TEAM = X`、native app +// group 形状等),**不含任何旧的具体值**——所以①本脚本自身无「中国账号痕迹」; +// ②未来再迁账号(改 signing.env 里的值)仍能正确回填,不依赖旧字面量。 +// +// 用法: +// node scripts/gen-signing.mjs 回填(就地改文件) +// node scripts/gen-signing.mjs --check drift 闸:若有文件会被改动则退出 1(不改文件) +// +// 注:源码注释里遗留的旧标识符是内部字符串、不进产物;本脚本只管功能性字面量 +// (会被编译/签名进产物的),注释去痕由一次性清理处理。 + +import { readFileSync, writeFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const ROOT = join(HERE, '..'); +const CLIENT = join(ROOT, 'client'); + +// ── 读 signing.env 的 3 个基值(去引号),派生其余 ─────────────────────────── +function readEnv() { + const txt = readFileSync(join(HERE, 'signing.env'), 'utf8'); + const pick = (k) => { + const m = txt.match(new RegExp('^' + k + '="?([^"\\n]+)"?\\s*$', 'm')); + if (!m) throw new Error(`signing.env 缺 ${k}`); + return m[1].trim(); + }; + const TEAM = pick('SIGNING_TEAM_ID'); + const ENTITY = pick('SIGNING_ENTITY'); + const APP = pick('SIGNING_APP_BUNDLE_ID'); + return { + TEAM, ENTITY, APP, + EXT: `${APP}.PacketTunnel`, + MAC_GROUP: `${TEAM}.${APP}`, + IOS_GROUP: `group.${APP}`, + }; +} + +const V = readEnv(); + +// ── 每文件的结构化替换表(路径相对 client/)────────────────────────────────── +// 每条 [正则, 替换串]。正则只描述「形状」,替换用派生值填入。 +const TABLE = { + // ---- 通用:两个 pbxproj 的 Team + Bundle ID(按后缀区分 app/ext/tests)---- + 'macos/Runner.xcodeproj/project.pbxproj': [ + [/DEVELOPMENT_TEAM = [A-Za-z0-9]+;/g, `DEVELOPMENT_TEAM = ${V.TEAM};`], + [/PRODUCT_BUNDLE_IDENTIFIER = [\w.]+\.PacketTunnel;/g, `PRODUCT_BUNDLE_IDENTIFIER = ${V.EXT};`], + [/PRODUCT_NAME = [\w.]+\.PacketTunnel;/g, `PRODUCT_NAME = ${V.EXT};`], + [/PRODUCT_BUNDLE_IDENTIFIER = [\w.]+\.RunnerTests;/g, `PRODUCT_BUNDLE_IDENTIFIER = ${V.APP}.RunnerTests;`], + ], + 'ios/Runner.xcodeproj/project.pbxproj': [ + [/DEVELOPMENT_TEAM = [A-Za-z0-9]+;/g, `DEVELOPMENT_TEAM = ${V.TEAM};`], + [/PRODUCT_BUNDLE_IDENTIFIER = [\w.]+\.PacketTunnel;/g, `PRODUCT_BUNDLE_IDENTIFIER = ${V.EXT};`], + // 主 app:不以 .PacketTunnel 结尾的那条(负向前瞻,避免误改扩展行)。 + [/PRODUCT_BUNDLE_IDENTIFIER = (?![\w.]*\.PacketTunnel;)[\w.]+;/g, `PRODUCT_BUNDLE_IDENTIFIER = ${V.APP};`], + ], + // ---- macOS xcconfig(主 app Bundle ID + 版权)---- + 'macos/Runner/Configs/AppInfo.xcconfig': [ + [/^PRODUCT_BUNDLE_IDENTIFIER = .+$/m, `PRODUCT_BUNDLE_IDENTIFIER = ${V.APP}`], + [/^PRODUCT_COPYRIGHT = .+$/m, `PRODUCT_COPYRIGHT = Copyright © 2026 ${V.ENTITY}. All rights reserved.`], + ], + // ---- macOS entitlements:app-identifier 前缀 + native app group ---- + 'macos/Runner/Release.entitlements': [ + [/\$\(AppIdentifierPrefix\)[\w.]+/g, `$(AppIdentifierPrefix)${V.APP}`], + [/[A-Z0-9]{10}\.[\w.]+<\/string>/g, `${V.MAC_GROUP}`], + ], + 'macos/Runner/DebugProfile.entitlements': [ + [/\$\(AppIdentifierPrefix\)[\w.]+/g, `$(AppIdentifierPrefix)${V.APP}`], + [/group\.[\w.]+<\/string>/g, `${V.IOS_GROUP}`], + ], + 'macos/PacketTunnel/PacketTunnel.entitlements': [ + [/[A-Z0-9]{10}\.[\w.]+<\/string>/g, `${V.MAC_GROUP}`], + ], + // ---- macOS PacketTunnel Info.plist:NEMachServiceName = app group + .PacketTunnel ---- + 'macos/PacketTunnel/Info.plist': [ + [/[A-Z0-9]{10}\.[\w.]+\.PacketTunnel<\/string>/g, `${V.MAC_GROUP}.PacketTunnel`], + ], + // ---- macOS Swift / 登录助手脚本 ---- + 'macos/PacketTunnel/PacketTunnelProvider.swift': [ + [/subsystem: "[\w.]+"/g, `subsystem: "${V.EXT}"`], + [/appGroup = "[\w.]+"/g, `appGroup = "${V.MAC_GROUP}"`], + ], + 'macos/Runner/VpnChannel.swift': [ + [/subsystem: "[\w.]+"/g, `subsystem: "${V.APP}"`], + [/tunnelBundleId = "[\w.]+"/g, `tunnelBundleId = "${V.EXT}"`], + ], + 'macos/Runner/MainFlutterWindow.swift': [ + [/loginHelperID = "[\w.]+"/g, `loginHelperID = "${V.APP}.LoginHelper"`], + ], + 'macos/build_login_helper.sh': [ + [/HELPER_ID="[\w.]+"/g, `HELPER_ID="${V.APP}.LoginHelper"`], + ], + // ---- iOS entitlements(group. 前缀 app group)---- + 'ios/Runner/Runner.entitlements': [ + [/group\.[\w.]+<\/string>/g, `${V.IOS_GROUP}`], + ], + 'ios/PacketTunnel/PacketTunnel.entitlements': [ + [/group\.[\w.]+<\/string>/g, `${V.IOS_GROUP}`], + ], + // ---- iOS Swift ---- + 'ios/Runner/VpnManager.swift': [ + [/extensionBundleId = "[\w.]+"/g, `extensionBundleId = "${V.EXT}"`], + [/appGroup = "[\w.]+"/g, `appGroup = "${V.IOS_GROUP}"`], + ], + 'ios/PacketTunnel/PacketTunnelProvider.swift': [ + [/subsystem: "[\w.]+"/g, `subsystem: "${V.EXT}"`], + [/appGroup = "[\w.]+"/g, `appGroup = "${V.IOS_GROUP}"`], + ], + 'ios/PacketTunnel/MemoryMonitor.swift': [ + [/subsystem: String = "[\w.]+"/g, `subsystem: String = "${V.EXT}"`], + ], +}; + +// ── 执行 ───────────────────────────────────────────────────────────────────── +const check = process.argv.includes('--check'); +let changed = 0; +const changedFiles = []; +for (const [rel, rules] of Object.entries(TABLE)) { + const path = join(CLIENT, rel); + const before = readFileSync(path, 'utf8'); + let after = before; + for (const [re, repl] of rules) after = after.replace(re, repl); + if (after !== before) { + changed++; + changedFiles.push(rel); + if (!check) writeFileSync(path, after); + } +} + +if (check) { + if (changed) { + console.error(`❌ signing codegen drift:${changed} 个文件与 signing.env 不一致:`); + for (const f of changedFiles) console.error(' - ' + f); + console.error(' 跑 `node scripts/gen-signing.mjs` 回填后重试。'); + process.exit(1); + } + console.log('✅ signing codegen 无 drift(原生文件与 signing.env 一致)'); +} else { + console.log(`signing codegen 完成:回填 ${changed} 个文件` + (changed ? ':' : '(已一致,无改动)')); + for (const f of changedFiles) console.log(' - ' + f); + console.log(` TEAM=${V.TEAM} APP=${V.APP} MAC_GROUP=${V.MAC_GROUP} IOS_GROUP=${V.IOS_GROUP}`); +} diff --git a/scripts/signing.env b/scripts/signing.env index d31432f..068334c 100644 --- a/scripts/signing.env +++ b/scripts/signing.env @@ -1,11 +1,15 @@ # signing.env — Apple 签名身份的【单一真相源】(shell 侧) # -# 迁移开发者账号 / 改 Bundle ID 时,只改这一个文件;三个签名脚本 -# (local_test.sh / ci/compile-macos.sh / ci/compile-ios.sh)全部 source 它, -# 不再各自硬编码。非机密(Team ID / 实体名 / Bundle ID 本就编进产物、公开),入 git。 +# 【迁移账号 / 改 Bundle ID 时,改这一个文件就够】,然后跑一次: +# node scripts/gen-signing.mjs +# 它会把这里的值回填进所有原生文件(pbxproj/entitlements/plist/swift)的字面量 +# ——Apple 工具链要求这些值以字面量出现、无法 source 变量,故用 codegen 保持单源。 # -# ⚠️ Xcode 侧另有等价单一真相源 client/Signing.xcconfig(格式不同、内容须一致), -# 由 ci/check-signing-config.sh 一致性闸比对,防两处漂移。改这里也要改那里。 +# 消费方: +# - 三个签名脚本(local_test.sh / ci/compile-macos.sh / ci/compile-ios.sh)直接 source 本文件; +# - 原生文件(Xcode/Swift)由 scripts/gen-signing.mjs 从本文件回填(勿手改那些标识符)。 +# drift 闸:ci/check-codegen-drift.sh 跑 `gen-signing.mjs --check`,防改了 env 没重生成 / +# 手改了原生标识符。非机密(Team ID / 实体名 / Bundle ID 本就编进产物、公开),入 git。 # 10 位 Team ID(developer.apple.com → Membership details)。 SIGNING_TEAM_ID="44WULXM6SV"