使用场景 / Use case
插件既可以从 Cindy 官方市场安装,也可以由用户直接选择本地 .cindy、通过 Git/本地第三方市场安装。我们需要让三种来源共用一条可理解、可验证、可迁移的安装路径:
包体自身的 ghost.json 是版本、权限和宿主兼容性的唯一事实来源;
服务器负责列表、访问授权、不可变 Release 与更新发现,不成为第二个权限裁决者;
用户可明确用本地或第三方版本替换同 ID 的官方插件,但不会因此静默继承官方身份、官方特权或历史凭证;
第三方市场单源损坏不会拖垮官方市场或其它来源;
插件作者能清楚知道普通能力、官方特权、版本兼容和发布契约的边界。
关联事故与历史:
当前问题 / Current limitation
当前官方市场在本地 .cindy 安装器之外又维护了一套权限与来源状态机:
权限存在多份真相:服务器 Release manifest、下载包 manifest、已安装 canonical/localized manifest、市场 ledger manifestDigest;
安装前、下载后、提交前多次重新计算来源与权限,产生大量 PRECONDITION 状态和竞态;
未安装的其它市场条目会预占 ghostId;任一自定义来源暂时不可读,所有官方新安装都 fail-closed;
Renderer/IPC 丢失 declared / accepted / skipped / unreadable 等状态,用户只看到“成功 / 0 个”或通用“安装失败”;
本地同 ID 包替换官方插件时,现有规则主要依赖 cindy- / filo- / xd- 前缀一刀切,非保留 ID 的官方插件又可被直接覆盖,来源与凭证边界不一致;
市场更新按 releaseId 而不是可比较版本判断;本地 version 当前只是展示字符串,同版本不同内容和降级都被当作普通更新;
单仓测试大量 mock 下载与最终安装出口,无法发现 production catalog + 实际包 + 最低支持客户端的跨仓不兼容。
这些复杂度并没有增强最终运行时权限:Desktop 最终仍然必须解析并执行真实 .cindy 包。相反,它制造了平行裁决与升级兼容风险。
目标原则 / Target principles
包体是唯一权限真相
安装确认、权限 diff、运行时能力都以本次将要落位的实际 .cindy 中 canonical manifest 为准。服务器可缓存包内元数据用于列表展示,但不提供另一份参与安全裁决的权限 manifest。
市场是可信下载与更新索引
服务器只负责目录、访问控制、不可变 Release、包体大小/SHA、兼容版本选择和下载授权。
一个权威安装器,来源只是窄上下文
本地文件、官方市场、第三方市场共用 inspect → 权限确认 → 原子落位/回滚。来源上下文只决定 Release SHA、来源记录、官方保留能力是否可用。
用户选择来源,不让未安装目录预占 ID
只有磁盘上已经安装的同 ghostId 才构成替换/更新冲突。其它市场存在同名条目只做展示提示,不阻断用户选择。
允许来源切换,但隔离身份与敏感资产
用户可以 official ↔ local ↔ third-party 切换;未经官方证明的包不继承官方身份、Host 特权和历史凭证。
存量升级无感
已安装、已启用、已批准的插件升级客户端后继续工作;不得要求批量重装、重新授权或重新配置。账本、批准状态和凭证命名空间必须自动迁移。
期望架构 / Proposed architecture
1. 包与兼容性契约
包内 manifest 至少承担:
{
"id" : " example-plugin" ,
"version" : " 1.2.3" ,
"minHostApiVersion" : 3 ,
"slots" : [],
"network" : {},
"node" : {}
}
id:本地安装身份;
version:市场发布要求 SemVer;本地开发包若不可比较则按“替换版本”呈现;
minHostApiVersion:声明实际依赖的宿主能力,不用产品版本号间接猜能力;
permissions/slots/network/node:实际包的唯一权限声明。
新增 Host 能力必须先落客户端,再允许要求该 Host API 的插件 Release 对兼容客户端可见。
2. 服务器边界
服务器负责:
下发插件目录、介绍、图标和 Release 列表;
对 pluginId + version 保持不可变,同版本不同 SHA 拒绝发布;
上传时计算并保存包体 SHA 与大小;
私有/组织插件下载鉴权;
根据客户端 hostApiVersion 返回该客户端可运行的最新 Release,而非总是返回全市场最新版本;
发布门实际下载包并对最低支持 Host API 做 conformance 检查;
CN / Global 保持相同包与兼容性结果。
服务器不负责:
生成另一份安装权限事实;
判断用户是否批准某项权限;
参与本地包的更新/降级授权;
因第三方来源状态决定官方插件能否安装。
3. Desktop 权威安装器
收敛为类似下列单一入口:
installCindyPackage ( filePath , {
source : "local" | "official-market" | "third-party-market" ,
sourceKey,
expectedReleaseSha256,
releaseId,
officialTrust,
} )
公共管线:
限量读取包体并计算 SHA;
解包、校验 manifest、签名/trust、zip-slip、zip bomb、skill/资源一致性;
与当前已安装实际 manifest 比较权限;
用户只确认一次真实包权限;
将确认绑定到同一包 SHA;
按 ghostId 加锁,staging → backup → 原子替换,失败回滚;
保存最小来源记录并重启运行时。
保留现有本地安装器的包体 SHA 钉死、原子换目录、运行时权限和安装锁。删除市场专属的第二套权限复核。
4. SHA 与来源判断
场景
行为
官方市场下载,实际 SHA 与 Release 不同
硬失败;不能静默降级成“非官方”
本地包 SHA 命中已知官方 Release
可视为该官方包的离线重装/更新
本地包与官方同 ID / 同版本但 SHA 不同
提示“与官方发布内容不同”,用户确认后可切换为本地版本
无法联网或没有官方 SHA 记录
视为来源未经验证,允许本地安装
第三方市场 SHA 匹配
只证明与该第三方市场登记的 Release 一致,不等于 Cindy 官方背书
未来如需离线证明官方身份,可增加包签名;不能用“有一个 SHA”本身冒充签名。
5. ID、版本与更新
市场同一 id + version 不可变;
客户端比较“已安装包版本”与“当前 Host API 下最新兼容版本”;
相同 SHA:显示已是当前包,不重复替换;
同版本不同 SHA:明确提示本地/第三方自定义替换;
更高版本:普通更新;
更低版本:明确标注降级,用户确认后允许;
非 SemVer:不假装判断高低,显示“替换版本”;
市场 releaseId 只做溯源,不代替版本比较。
6. 来源切换、凭证与官方特权
来源切换必须是一等状态,而不是依赖 ID 前缀和残留 ledger 推断。
建议将敏感资产至少绑定到:
owner + ghostId + trustDomain/sourceKey
规则:
同一可信来源的正常更新可延续凭证和 OAuth 连接;
official → local / third-party:必须明确提示来源变化;
未经官方证明的新包不得自动继承官方 Token、OAuth、Connection 或其它敏感资产;
普通 UI 偏好、布局等非敏感状态可以保留;
用户可另行明确选择迁移/重新授权凭证;
local / third-party 不得声明或使用只对官方 trustDomain 开放的能力,例如受控 tokenBroker、组织身份、宿主登录令牌等;
exact official SHA 或未来可信包签名可恢复 official trustDomain;
切回官方版本时同样走明确来源切换,并恢复/重新连接对应官方凭证域。
需要替换当前仅靠 cindy- / filo- / xd- 前缀的一刀切规则;前缀仍可保留为命名规范,但不能独自承担来源真实性。
7. 第三方市场边界
第三方市场可以提供目录、版本、SHA 和下载地址;
其 SHA 只建立“包与该市场记录一致”的来源证明,不授予官方身份;
不得预占尚未安装的 ghostId;
一个来源不可读、部分条目非法或同步失败,不阻断其它来源安装;
来源 UI 必须展示 declared / accepted / skipped / unreadable 及稳定 reason;
正常空市场、全部条目被拒绝、暂时不可读是三种不同状态;
同 ID 多来源由用户选择;已安装后 provenance 决定默认从哪个来源检查更新;
切换来源使用第 6 节的凭证与特权边界。
8. 第三方插件开发边界
作者文档与 Forge 需要明确:
包内 manifest 是权限和兼容性的权威契约;
市场插件版本必须 SemVer,同版本不可重新发布不同内容;
普通第三方能力与官方 Host 特权的完整清单;
未支持 Host API 时如何开发、测试和发布;
本地开发允许同版本不同内容、替换和降级,但会显示非官方/自定义来源;
第三方不得通过抢注官方 ID、复用 secret key 或来源切换继承官方资产;
Forge 检查应与 Desktop 实际 validator 共用同一契约,不再维护平行枚举。
建议删除/收缩的现有机制
跨全部市场的潜在 ghostId 所有权计算;
assertSourceOwnsGhostId 对不完整来源的全局安装阻断;
服务器 manifest 与真实包 manifest 的市场专属双重权限裁决;
reviewedManifest / previouslyInstalledManifest / reviewedBaseline 等为平行 manifest 补洞的状态;
ledger manifestDigest 作为权限批准或潜在来源所有权的事实;如其它安全用途仍需要,须重新命名并收窄职责;
releaseId 变化直接等同版本更新;
非稳定错误统一折叠为无法诊断的“安装失败”。
迁移要求
这是插件基座与凭证边界改造,迁移是 P0:
现有官方市场安装记录自动映射到 official trustDomain;
现有 Git/本地市场记录自动映射到对应 sourceKey;
现有纯本地插件映射到 local;
现有凭证不得删除或丢失;
能确定来源的凭证自动迁移到对应 trustDomain;
来源不明确时保持插件可用并给出一次性恢复路径,不能静默把凭证交给不同来源;
升级后不要求用户重新安装、重新批准或重新配置所有插件;
迁移可重复执行、失败可恢复,并覆盖 Windows 文件锁/中断场景。
分阶段实施建议
Phase 0:事故止血与观测
Phase 1:抽取统一安装核心
将本地与市场共同的 inspect、SHA、权限 diff、原子更新抽成单一 Main API;
增加明确 source context 和 trustDomain;
行为先保持兼容,建立全链路 fixture。
Phase 2:官方市场变薄
权限确认改为只看实际包;
删除二次 market manifest 复核状态机;
ledger 收缩为 provenance;
服务器按 Host API 返回最新兼容 Release。
Phase 3:第三方市场与来源切换
删除潜在 ID 预占;
实现来源切换确认、敏感资产隔离和第三方特权限制;
补齐来源状态 UI 和第三方作者文档。
Phase 4:迁移与发布门收口
完成旧 ledger/vault 自动迁移;
CN / Global、Windows / macOS、最低支持客户端矩阵实测;
删除旧兼容分支和不再使用的平行 manifest 状态。
验收标准 / Acceptance criteria
已考虑的替代方案 / Alternatives considered
继续修补现有市场状态机
已由 chore(plugins): 市场发现跳过插件条目时记录原因与位置 #1524 、fix(plugin): 支持真实包权限变化后重新确认 #1648 、fix(desktop): preserve legacy plugin permission approvals #1657 等证明只能修局部症状;多份 manifest 与来源所有权仍会继续产生竞态和兼容事故。
完全取消 SHA
本地文件可以没有官方 SHA 证明,但官方市场下载仍必须用 Release SHA 发现分发错误或内容替换。
禁止本地包覆盖官方 ID
简单但限制用户开发、fork、回滚和离线安装;更合理的是允许来源切换,同时隔离官方身份、特权和敏感资产。
直接用 Cindy 产品版本做兼容门
可行但把能力与发版号耦合。优先使用单调的 Host API version;产品版本只用于展示和灰度。
让服务器继续裁决权限
会继续产生服务器投影 manifest 与实际包不一致。服务器可以在发布时校验,但客户端安装授权只能以实际包为准。
跨仓范围与门禁
可能涉及:
makecindy/cindy:Desktop installer、market service、ledger/vault migration、Renderer;
makecindy/cindy-protocol:manifest / Host API / conformance 契约;
Cindy server:Release 不可变性、兼容版本选择、下载与发布门;
makecindy/cindy-official-plugins:官方插件版本与能力声明、发布 CI。
本议题触及插件运行时、批准状态、凭证边界、manifest 契约和存量迁移,后续实现 PR 均需按插件基座白名单确认门审查;不得通过放宽运行时沙箱或丢弃存量凭证换取简化。
使用场景 / Use case
插件既可以从 Cindy 官方市场安装,也可以由用户直接选择本地
.cindy、通过 Git/本地第三方市场安装。我们需要让三种来源共用一条可理解、可验证、可迁移的安装路径:ghost.json是版本、权限和宿主兼容性的唯一事实来源;关联事故与历史:
gh-cli能力未按 protocol → server → client → plugin 顺序发布,正式插件先于客户端支持上线。当前问题 / Current limitation
当前官方市场在本地
.cindy安装器之外又维护了一套权限与来源状态机:ghostId;任一自定义来源暂时不可读,所有官方新安装都 fail-closed;cindy- / filo- / xd-前缀一刀切,非保留 ID 的官方插件又可被直接覆盖,来源与凭证边界不一致;version当前只是展示字符串,同版本不同内容和降级都被当作普通更新;这些复杂度并没有增强最终运行时权限:Desktop 最终仍然必须解析并执行真实
.cindy包。相反,它制造了平行裁决与升级兼容风险。目标原则 / Target principles
包体是唯一权限真相
安装确认、权限 diff、运行时能力都以本次将要落位的实际
.cindy中 canonical manifest 为准。服务器可缓存包内元数据用于列表展示,但不提供另一份参与安全裁决的权限 manifest。市场是可信下载与更新索引
服务器只负责目录、访问控制、不可变 Release、包体大小/SHA、兼容版本选择和下载授权。
一个权威安装器,来源只是窄上下文
本地文件、官方市场、第三方市场共用 inspect → 权限确认 → 原子落位/回滚。来源上下文只决定 Release SHA、来源记录、官方保留能力是否可用。
用户选择来源,不让未安装目录预占 ID
只有磁盘上已经安装的同
ghostId才构成替换/更新冲突。其它市场存在同名条目只做展示提示,不阻断用户选择。允许来源切换,但隔离身份与敏感资产
用户可以 official ↔ local ↔ third-party 切换;未经官方证明的包不继承官方身份、Host 特权和历史凭证。
存量升级无感
已安装、已启用、已批准的插件升级客户端后继续工作;不得要求批量重装、重新授权或重新配置。账本、批准状态和凭证命名空间必须自动迁移。
期望架构 / Proposed architecture
1. 包与兼容性契约
包内 manifest 至少承担:
{ "id": "example-plugin", "version": "1.2.3", "minHostApiVersion": 3, "slots": [], "network": {}, "node": {} }id:本地安装身份;version:市场发布要求 SemVer;本地开发包若不可比较则按“替换版本”呈现;minHostApiVersion:声明实际依赖的宿主能力,不用产品版本号间接猜能力;新增 Host 能力必须先落客户端,再允许要求该 Host API 的插件 Release 对兼容客户端可见。
2. 服务器边界
服务器负责:
pluginId + version保持不可变,同版本不同 SHA 拒绝发布;hostApiVersion返回该客户端可运行的最新 Release,而非总是返回全市场最新版本;服务器不负责:
3. Desktop 权威安装器
收敛为类似下列单一入口:
公共管线:
ghostId加锁,staging → backup → 原子替换,失败回滚;保留现有本地安装器的包体 SHA 钉死、原子换目录、运行时权限和安装锁。删除市场专属的第二套权限复核。
4. SHA 与来源判断
未来如需离线证明官方身份,可增加包签名;不能用“有一个 SHA”本身冒充签名。
5. ID、版本与更新
id + version不可变;6. 来源切换、凭证与官方特权
来源切换必须是一等状态,而不是依赖 ID 前缀和残留 ledger 推断。
建议将敏感资产至少绑定到:
规则:
tokenBroker、组织身份、宿主登录令牌等;需要替换当前仅靠
cindy- / filo- / xd-前缀的一刀切规则;前缀仍可保留为命名规范,但不能独自承担来源真实性。7. 第三方市场边界
ghostId;8. 第三方插件开发边界
作者文档与 Forge 需要明确:
建议删除/收缩的现有机制
ghostId所有权计算;assertSourceOwnsGhostId对不完整来源的全局安装阻断;reviewedManifest / previouslyInstalledManifest / reviewedBaseline等为平行 manifest 补洞的状态;manifestDigest作为权限批准或潜在来源所有权的事实;如其它安全用途仍需要,须重新命名并收窄职责;迁移要求
这是插件基座与凭证边界改造,迁移是 P0:
分阶段实施建议
Phase 0:事故止血与观测
gh-cli协议/客户端/插件发布顺序;Phase 1:抽取统一安装核心
Phase 2:官方市场变薄
Phase 3:第三方市场与来源切换
Phase 4:迁移与发布门收口
验收标准 / Acceptance criteria
已考虑的替代方案 / Alternatives considered
继续修补现有市场状态机
已由 chore(plugins): 市场发现跳过插件条目时记录原因与位置 #1524、fix(plugin): 支持真实包权限变化后重新确认 #1648、fix(desktop): preserve legacy plugin permission approvals #1657 等证明只能修局部症状;多份 manifest 与来源所有权仍会继续产生竞态和兼容事故。
完全取消 SHA
本地文件可以没有官方 SHA 证明,但官方市场下载仍必须用 Release SHA 发现分发错误或内容替换。
禁止本地包覆盖官方 ID
简单但限制用户开发、fork、回滚和离线安装;更合理的是允许来源切换,同时隔离官方身份、特权和敏感资产。
直接用 Cindy 产品版本做兼容门
可行但把能力与发版号耦合。优先使用单调的 Host API version;产品版本只用于展示和灰度。
让服务器继续裁决权限
会继续产生服务器投影 manifest 与实际包不一致。服务器可以在发布时校验,但客户端安装授权只能以实际包为准。
跨仓范围与门禁
可能涉及:
makecindy/cindy:Desktop installer、market service、ledger/vault migration、Renderer;makecindy/cindy-protocol:manifest / Host API / conformance 契约;makecindy/cindy-official-plugins:官方插件版本与能力声明、发布 CI。本议题触及插件运行时、批准状态、凭证边界、manifest 契约和存量迁移,后续实现 PR 均需按插件基座白名单确认门审查;不得通过放宽运行时沙箱或丢弃存量凭证换取简化。