# P1 · macOS System Extension 接线指南(方案B) > 状态:**骨架已落**(本仓库)。把 PoC 的 `sudo sing-box run` 外部二进制换成自包含、免 root > 的 NEPacketTunnelProvider(System Extension)+ 嵌入式 libbox。本文档列清单 + 待人工在 > Xcode/开发者后台完成的步骤。总览见 [vpn-core-embedding.md](./vpn-core-embedding.md)。 ## 0. 关键标识(已确定) | 项 | 值 | |---|---| | Team ID | **BYL4KQHMTN** | | 主 app bundle id | `com.pangolin.pangolin`(App ID 已注册,勾 Network Extensions + App Groups)| | 隧道扩展 bundle id | `com.pangolin.pangolin.tunnel`(**待注册**,同样勾 Network Extensions + App Groups)| | App Group | `group.com.pangolin.pangolin`(**待在 Identifiers → App Groups 注册**)| | 分发方式 | 站外 Developer ID + 公证(非 App Store)→ entitlement 用 `-systemextension` 后缀 | | 权限审批 | **无需**——Network Extensions 在 Identifiers 自助勾选即得(Packet Tunnel Provider 已 GA)| ## 1. 仓库里已就位的文件(骨架) ``` client/macos/ ├─ Runner/ │ ├─ DebugProfile.entitlements / Release.entitlements ← application-groups 已备(注释,待 §3.6 解开) │ ├─ MainFlutterWindow.swift ← VpnChannel 注册行已备(注释,待 §3.6 解开) │ └─ VpnChannel.swift ← 主app:NETunnelProviderManager 启停+状态/速率回传(待加入 target) └─ PacketTunnel/ ← 新 System Extension target 的源 ├─ PacketTunnelProvider.swift ← 扩展:LibboxSetup→NewService→start;openTun 建 TUN ├─ PacketTunnel.entitlements ← packet-tunnel-provider-systemextension + app group └─ Info.plist ← NEProviderClasses → PacketTunnelProvider client/lib/bridge/ ├─ vpn_bridge.dart ← VpnNativeBridge(method/event channel,已存在) └─ vpn_bridge_provider.dart ← kUseNativeVpnMacOS 开关(默认 false;原生就绪后置 true) ``` **Channel 契约**(`VpnNativeBridge` ↔ `VpnChannel`): - MethodChannel `pangolin/vpn`:`start(configJson)` / `stop` / `getStatus` / `selectOutbound(tag)` / `getActiveOutbound` / `setKillSwitch(bool)` - EventChannel `pangolin/vpn/status`:状态字符串(`on`/`connecting`/`off`/…) - EventChannel `pangolin/vpn/stats`:每秒一帧 `{up, down, uplinkTotal, downlinkTotal}` ## 2. 开发者后台(你来做,几分钟) 1. **App Group**:Identifiers → App Groups → ➕ → `group.com.pangolin.pangolin`。 2. 回到 App ID `com.pangolin.pangolin` 的 **App Groups** capability → Edit → 勾上该 group。 3. 新建 App ID `com.pangolin.pangolin.tunnel`(扩展)→ 勾 **Network Extensions** + **App Groups**(关联同一 group)。 4. **Developer ID Application** 证书(Certificates → ➕,若还没有)——签名 + 公证用。 ## 3. Xcode:新增 System Extension target + 接 libbox(核心人工步骤) > Flutter 工程开 `client/macos/Runner.xcworkspace`。 1. **编 libbox**:`bash scripts/build-libbox.sh apple macos` → 得 `Libbox.xcframework`(~204MB,不入 git)。 2. **新 target**:File → New → Target → **Network Extension**(macOS)→ Packet Tunnel - Product Name:`PacketTunnel`,bundle id `com.pangolin.pangolin.tunnel`,语言 Swift。 - 删掉模板生成的 `PacketTunnelProvider.swift`,把 `client/macos/PacketTunnel/` 下的三个文件 加入该 target(Provider/Info.plist/entitlements);Info.plist 与 entitlements 设为本 target 用。 3. **链接 libbox**:把 `Libbox.xcframework` 拖进工程,在 **PacketTunnel** target 的 *Frameworks and Libraries* 加入(Embed & Sign);解开 `PacketTunnelProvider.swift` 里 `import Libbox` 与被注释的真实接线段(`LibboxSetup`/`LibboxNewService`/`service.start()`)。 4. **补平台接口**:`PangolinPlatformInterface` 对 `LibboxPlatformInterfaceProtocol` 的完整实现 (openTun / writeLog / useProcFS / findConnectionOwner / defaultInterfaceMonitor / getInterfaces / underNetworkExtension / systemCertificates …)——**对照官方 [sing-box-for-apple `ExtensionPlatformInterface`](https://github.com/SagerNet/sing-box-for-apple) 逐方法移植**(NE 部分两端高度一致,可大段复用)。这是 P1 唯一"重"的代码块。 5. **sysex 激活**:`VpnChannel.activateSystemExtensionIfNeeded()` 用 `OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: tunnelBundleId, queue:)` + delegate 等结果;首启用户在「系统设置 → 隐私与安全性」点允许。 6. **接主 app(Runner)**: - 把 `client/macos/Runner/VpnChannel.swift` 加入 **Runner** target 的 *Compile Sources*。 - 解开 `MainFlutterWindow.swift` 里 `VpnChannel.register(...)` 那行注释。 - 解开 `Runner/DebugProfile.entitlements`、`Release.entitlements` 里 `application-groups` 注释块 (注册 App Group 后)。 > 这三处在仓库里**有意保持注释/未接**,以免在 target/签名就绪前破坏现有 PoC 构建。 7. **签名**:两个 target(Runner + PacketTunnel)都用 Team **BYL4KQHMTN** + Developer ID; 开 Hardened Runtime;Runner 的 *System Extensions* capability 自动带出。 ## 4. 速率/状态联调(libbox CommandServer) - 扩展内 `LibboxNewCommandServer`(loopback)暴露状态/速率/连接;主 app 用 `LibboxCommandClient` 订阅 → 回填 `pangolin/vpn/stats` 与 `…/status`(替换 `VpnChannel` 里的占位 timer)。 - 连接页(`connect_page.dart`)与统计页已消费这两路流,**无需改动**。 ## 5. 切换上线 - 全部联调通过(连真节点、出口 IP=节点、速率/延迟、Kill Switch、退出拆隧道、明暗/中英)后, 把 `vpn_bridge_provider.dart` 的 `kUseNativeVpnMacOS` 置 **true**。 - PoC 的 `DesktopVpnBridge`(sudo 子进程)保留作 fallback/调试,不删。 ## 6. 待办清单(TODO,代码内已标 `TODO(P1)`) - [ ] 开发者后台:App Group + 扩展 App ID + Developer ID 证书(§2) - [ ] Xcode:新 PacketTunnel target + 加入 `Libbox.xcframework`(§3.1–3.3) - [ ] 解开 `PacketTunnelProvider.swift` 真实接线 + 移植 `PangolinPlatformInterface`(§3.4) - [ ] `OSSystemExtensionRequest` 激活流程(§3.5) - [ ] `LibboxCommandClient` 接速率/状态(§4) - [ ] `setKillSwitch` / `selectOutbound` 真实实现 - [ ] 签名 + 公证流水线(Team BYL4KQHMTN) - [ ] 联调通过 → `kUseNativeVpnMacOS = true`(§5)