Files
ShadowsocksX-NG/docs/research/plugin-compat-record.md
Qiu Yuzhou 5e13c1716f feat(ng2): managed plugins end to end (#38)
- 受管插件静态事实表(v2ray-plugin v1.3.2,来源/许可证/版本/重签)与
  BundleManagedPluginProvider:受管集内且 bundle 内可执行文件在位才返回
  绝对路径;与 Vendor 清单双向锚定(D6/D10)
- 服务器编辑器插件区:受管选择器(无 + 受管列表),选中受管项才显示
  供应链事实与参数输入;集外引用显式「本版本未提供」并原样保留
- updateServer 插件落盘语义:「无」整体清除、参数写删钥匙串、集外不漂移
- wrapper 监听建立判定:3 秒未建立本地监听记 error 日志,监管继续(D10)
- 诊断导出新增受管插件清单(名称/版本/提供状态,无参数)
- 功能级兼容实测记录(docs/research/plugin-compat-record.md)与端到端
  冒烟:契约插件路径 → sslocal 拉起 → v2ray-plugin 进程建立 → 停止链
2026-09-21 10:38:30 +08:00

65 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 受管插件功能级兼容实测记录(spec #21 Further Notes #6,issue #38)
- 实测日期:2026-09-21
- 实测人:Qiu Yuzhou(agent 辅助执行)
- 环境:macOS 26(Darwin 27.0.0)arm64;Xcode DerivedData Debug 构建;sslocal v1.25.0(`aarch64-apple-darwin`,归档 SHA-256 `58e0caf0…`);v2ray-plugin v1.3.2(`darwin-arm64`,归档 SHA-256 `35747669…`),均为 Vendor 清单固定版本,构建期哈希复验 + Developer ID 重签(插件 identifier `com.qiuyuzhou.ShadowsocksX-NG.plugin.v2ray-plugin`,hardened runtime)。
- 证据规则:以下每一条都对应可重复执行的测试或本记录中的命令输出摘要;未实测的系统行为不写成事实。
## 已实测闭环
### 1. 配置生成 → sslocal 拉起 → 插件进程建立(端到端边界)
生产组合根已接线:`CatalogViewModel` / `ProxyRuntimeController` 的默认受管插件提供均为 `BundleManagedPluginProvider`(宿主 app bundle),激活派生、编辑器提供事实与诊断清单读同一事实源;`BundleManagedPluginProviderTests.testHostAppBundleProvidesManagedPlugin` 固化「bundle 内插件在位」防打包回归。
`Tests/RealSslocalSmokeTests.testRealSslocalLaunchesManagedV2rayPluginProcess`:
- 契约 `servers[0]` 携带 bundle 内插件绝对路径(`Contents/Helpers/Plugins/v2ray-plugin`)与 `plugin_opts: "mode=websocket"`;
- wrapper 以真实 sslocal 拉起,SIP003 插件进程建立(`pgrep -f` 按完整路径确认,进程存活);
- 本地 SOCKS 端口完成监听绑定;
- SIGTERM 停止链:wrapper 干净退出(0),插件进程随 sslocal 一并结束。
无插件基线 `testRealSslocalBindsSOCKSAndHTTPPortsAndCompletesHandshake` 在同一套件中保持通过(SOCKS5 完整握手)。
实测观察(补充两条对后续票有用的行为事实):
- **sslocal 在插件进程就绪前不绑定本地监听**。插件冷启动(首次 exec 触发签名校验)时,从 sslocal spawn 到端口可用可能明显超过 3 秒(本机 xcodebuild 冷环境实测出现过 6 秒级)。D10 的「3 秒未建立监听 → error 日志」在此场景是真实告警而非误报,GUI 健康门(15 秒)不受影响。
- shadowsocks-rust 按 SIP003 以 `SS_LOCAL_HOST/SS_LOCAL_PORT` 环境变量让插件监听管道口;插件进程先于本地端口出现。
### 2. 选择器全语义与保存语义
`Tests/CatalogViewModelPluginTests`(9 例)与 `Tests/BundleManagedPluginProviderTests`(6 例):
- 「无 / 受管 / 集外」三态呈现;受管选中显示供应链事实(来源项目、许可证 MIT、固定版本 v1.3.2、重签标识)且只有受管选中出现参数输入;
- 受管选择落盘:程序名入目录、参数入钥匙串(引用形态);空参数删除引用与秘密;「无」整体清除;
- 集外引用(如 Legacy/订阅带入的 `obfs-local`)显式渲染「本版本未提供」、原样保留引用与参数秘密,连接字段照常可编辑;
- 受管集外程序名一律 `executablePath == nil`(含路径形态引用,绝不拼接用户输入),经状态机以 `pluginNotProvided` 原子拒绝;文件缺失/降级同语义。
### 3. 激活失效路径接 #26 语义
`Tests/ActivationStateMachineTests`(存量)+ `BundleManagedPluginProviderTests`(新增,以真实文件存在性驱动 provider):
- 组内一叶引用未提供插件 → 整组原子拒绝并点名;
- 活动目标插件变为不可用(`catalogDidCommit` / `resyncOnLaunch`)→ 清除目标、停止代理、点名原因(`ProxyRuntimeControllerTests` 存量覆盖)。
### 4. ss URI 往返
`Tests/SsUriTests`(存量,逐字节同构语料)+ `CatalogViewModelPluginTests.testSharedSsUriRoundTripsThroughEditorEdit`(经编辑面保存 → 分享 → 再导入,`plugin=name;opts` 同构,与 #32 编解码一致)。
### 5. 诊断页/导出
`Tests/DiagnosticReportTests`(新增 2 例):导出含「受管插件」清单(程序名 + 版本 + 提供/缺失),投毒夹具确认插件参数不进导出。
### 6. wrapper 监听判定
`Tests/AgentLifecycleTests.testListenNotEstablishedWithinDeadlineIsLoggedAndSupervisionContinues`:stub sslocal 不监听 → 拉起 3 秒后 `agent.log` 出现 `listen not established within deadline: <端点>`,监管循环继续,后续 SIGTERM 停止链不受影响(D10「运行期失败以 sslocal 信号为准,GUI 不猜测」)。插件进程退出不经 wrapper 直接观测——插件死亡连带 sslocal 退出(实测行为),由既有的 `sslocalExitedUnexpectedly` error 日志承接,与「以 sslocal 信号为准」一致。
## 仍属发布门槛人工项(不能在本机自动化闭环)
1. **真实服务器功能验证**:经 v2ray-plugin websocket 隧道访问真实远端的协议行为与性能(无真实服务器夹具;本记录只闭环到「插件进程建立 + 本地监听」边界)。
2. **真实公证对嵌套重签插件的接受度**(Further Notes #3,需 notarytool 凭据,#39)。
3. **Gatekeeper 首启/quarantine 实机弹窗形态**(Further Notes #1)与 Hardened Runtime 下两级子进程签名链在陌生机器上的表现(Further Notes #2 的实机部分)。
## 结论
#38 验收标准中「stub 边界验证或真实夹具」档位的功能级兼容实测已闭环并固化为本仓库测试;真实服务器配合、公证与陌生机器 Gatekeeper 行为按规格保留为发版前人工检查项。