Files
ShadowsocksX-NG/docs/research/plugin-compat-record.md
T
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

5.4 KiB
Raw Blame History

受管插件功能级兼容实测记录(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 行为按规格保留为发版前人工检查项。