diff --git a/CLAUDE.md b/CLAUDE.md
index 334c57a..e2d6fef 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -82,3 +82,35 @@ cd web/website && npm run gen:tokens
- `client/lib/pangolin_theme.dart` — 只含实现层(`PangolinScheme`/`PangolinText`/`PangolinTheme`),不含 token 数值。
- `design/flutter/` 已删除;Flutter 组件 canonical 实现在 `client/lib/widgets/`,规格在 `design/preview/`。
- **禁止**再向 `design/` 提交 Dart/TS 组件代码副本(会漂移)。
+
+## client/ macOS 原生隧道(PacketTunnel 系统扩展 + 内嵌 libbox)
+
+内嵌 sing-box(`Libbox.xcframework`)的 `NEPacketTunnelProvider` **系统扩展**(站外 Developer ID
+分发)。让它能被 `sysextd` 加载并真正连通踩了一长串坑,**改这块前必读**
+`docs/macos-sysext-realize-troubleshooting.html`。以下是铁律:
+
+**构建 / 发版**
+- 一律走 `scripts/local_test.sh`(`build`/`notarize`/`copy`/`run`):Developer ID 签名 + 公证 + staple。
+- **每次构建必递增 `CFBundleVersion`**(`CURRENT_PROJECT_VERSION`)——否则 `sysextd` 视为同版本**不更新**,装上去跑的还是旧扩展。
+- SIP 开启的机器只接受**已公证**的 sysext;`client/macos/sign_libbox.sh` 在构建期以 Developer ID 重签内嵌 Libbox。
+
+**系统扩展能被 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`。
+- 扩展 `Info.plist` 必须有 **`NSSystemExtensionUsageDescription`**(网络扩展类别强制,主 app 的不顶用)。
+- **App Group 用 macOS 原生格式 ` 系统扩展能 realize/激活只是第一步。让内嵌的 libbox(sing-box)真正建起隧道、能上网,又踩了四个坑(均在 扩展进程启动 360ms 后自杀(SIGABRT)。把 stderr 重定向到 App Group 容器文件后抓到 Go panic: 此版本 libbox 会解引用第三个 openTun 打点显示卡在 修复:把 libbox 启动整段放进后台队列( 隧道起来了、TCP 能经 REALITY 出海,但域名打不开。box.log 显示发往隧道 DNS 的查询走了直连: 隧道 DNS 地址 172.19.0.2 落在 LAN 直连规则 172.16/12 内,被路由成直连(发往不存在的主机)→ 解析全失败。修复(服务端 注:sing-box 1.13 的 另一个发版必知:每次构建必递增 连通验证(从命令行):5.5 加载之后:从"扩展能起"到"真正连通"(运行时)
+PacketTunnelProvider.swift / 服务端配置):运行时 ① libbox
+startOrReloadService(options:) 传 nil → 空指针崩溃 扩展进程 SIGABRTpanic: runtime error: invalid memory address or nil pointer dereference
+libbox.(*CommandServer).StartOrReloadService(server, {config}, 0x0) command_server.go:175
+options 参数(虽标 _Nullable)。修复:传非空 LibboxOverrideOptions() 而非 nil。运行时 ② 默认接口监控异步返回 →
+no available network interface 启动报错startDefaultInterfaceMonitor 启动 NWPathMonitor 后立即返回,但首个 path 回调是异步晚到的;sing-box 紧接着的网络操作拿到的默认接口索引还是 -1。修复:用信号量**阻塞到首个 path 更新再返回**(带 5s 超时),对齐 sing-box-for-apple。运行时 ③ provider 队列三方死锁 → 隧道永远卡 connecting 最隐蔽
+setTunnelNetworkSettings 的信号量等待,回调永不触发:
+
+startTunnel → 同步调 startOrReloadService(阻塞该队列)sem.wait() 等 setTunnelNetworkSettings 完成DispatchQueue.global().async),startTunnel 立即返回、provider 队列腾出来投递回调。运行时 ④ 缺 DNS 劫持 → 隧道连上但打不开网站 最后一关
+inbound packet connection to 172.19.0.2:53
+router: match ip_cidr=[..172.16.0.0/12..] => route(direct) # DNS 被 LAN 规则吞去直连 → 解析失败
+clientconfig.go):route.rules 首条加 {"action":"hijack-dns","port":[53]}(排在 LAN 规则之前),把 :53 查询交给 sing-box DNS 模块。protocol:"dns" 匹配需先 sniff;按目的端口 53 匹配最稳、不依赖 sniff。CFBundleVersion。 sysextd 按(标识符, 版本)去重;同版本号重装**不会替换**已激活的旧扩展,跑的还是旧代码(排查时极易被误导)。$ curl https://api.ipify.org → 103.119.13.48 # 出口=节点 IP,流量走隧道
+$ curl -o/dev/null -w '%{http_code}' https://github.com → 200
+box.log: router: match[0] port=53 => hijack-dns → dns: exchanged A github.com 140.82.116.3
+
6. 给后人的排查 checklist(Developer ID 网络系统扩展)
systemextensionsctl developer on,或在 15/14 机器上验证。
startOrReloadService(options:) 传**非空** LibboxOverrideOptions(),别传 nil。startTunnel 里的 libbox 启动放**后台队列**(DispatchQueue.global().async),别在 provider 队列同步跑(死锁)。startDefaultInterfaceMonitor **阻塞到首个 path 更新再返回**。action:hijack-dns,按 port 53),排在 LAN/分流规则之前。CFBundleVersion**,否则 sysextd 不更新已激活的扩展。log.output 指向容器文件(writeLogs 回调启动期不触发);Go fatal 走 stderr,需把扩展 stderr 重定向到文件才看得到。改动文件:client/macos/PacketTunnel/Info.plist · client/macos/PacketTunnel/PacketTunnel.entitlements · client/macos/Runner/Release.entitlements · client/macos/PacketTunnel/PacketTunnelProvider.swift · client/macos/Runner.xcodeproj/project.pbxproj(OTHER_LDFLAGS/PRODUCT_NAME/移除 Embed Libbox/加 timestamp)。验证机:cara,干净 macOS 15.3.2 Intel。
改动文件:扩展 Info.plist/entitlements/main.swift/PacketTunnelProvider.swift · Runner/Release.entitlements · Runner.xcodeproj/project.pbxproj(OTHER_LDFLAGS/PRODUCT_NAME/移除 Embed Libbox/--timestamp/版本号) · scripts/local_test.sh · client/macos/sign_libbox.sh · 服务端 server/internal/httpapi/clientconfig.go(DNS 劫持)。验证机:cara,干净 macOS 15.3.2 Intel,出口 IP=节点、国外站可达。