Files
soft_quay/docs/tasks/T-302.md

7.9 KiB
Raw Permalink Blame History

id, title, phase, deps, status, created, issue, context_ref, claim_branch, work_branch, write_paths
id title phase deps status created issue context_ref claim_branch work_branch write_paths
T-302 安装流程整合 3
T-301
T-102
T-103
DONE 2026-07-18 null 6575c9ad9b null agent/codex/T-302
docs/tasks/T-302.md
core/application/
core/installer/
core/storage/
docs/api.md
docs/04-architecture.md
docs/00-ai-start-here.md
docs/06-tasks.md
docs/current-state.md

问题 / 背景

T-301 只能把网络字节可靠地落为隔离的 .download;下载任务元数据、完成路径和文件内容仍可被本地篡改,不能直接作为安装身份或可信包。T-102/T-612/T-613 已分别具备 ZIP 预扫描/安全解压和可恢复的 staging → current → backup 切换原型,但尚未把已验签 Catalog、完整包 SHA-256、严格 app.json 身份比对、安装记录和健康失败回滚串成一个可调用的 core use case。

本任务关闭这条生产安装链的代码级信任边界。外层 Catalog 的 Ed25519 签名覆盖嵌套 packages[arch] 的 size、sha256 与 signature 字段;当前协议没有定义 package signature 的独立待签名字节、密钥或轮换域,T-302 不得臆造第二套包级验签。它只接受已经由 catalog.Client 验签、严格解析并按目标过滤后的 Catalog 选择,并以该选择中的精确 size/SHA-256 重新验证本地完成文件。

方案

  1. 在 core/application/install 落地 InstallService:请求包含已过滤的 catalog.Entry、目标 architecture 和 .download 路径。服务必须拒绝不可安装 entry、缺失 package、architecture 与 App.Packages[architecture] 不一致的选择;不得从 downloader.Task、DownloadCompletedPayload 或本地 metadata 重新取得 app/version/size/hash。该 application 子包独立于被 Catalog 图标投递依赖的父包,避免反向 import cycle。
  2. 在 core/installer 增加经过验证的包提取入口。它以一次 Lstat → open → fstat → Lstat 得到普通文件句柄,先确认实际长度精确等于 Catalog size、再以同一文件句柄计算 SHA-256,并以常量时间比较 Catalog hash;哈希不符时不得构造 zip.Reader、读取 app.json 或创建 staging。随后仍以同一句柄完成 EOCD/ZIP64/中央目录预扫描和 zip.Reader 构造,不能按路径重新打开。
  3. 安全读取 ZIP 根目录唯一的 app.json(最大 1 MiB,严格 JSON、无未知字段/尾随值),校验 v1 的全部字段与共享 Windows 安全相对路径规则。身份字段必须与 Catalog 选择一致:id、version、channel、min_os、architecture、entrypoint、requires_admin;其余 v1 常量和值也必须符合 schemas/app.schema.json。只有通过比对后,才复用既有两阶段 ZIP 检查把 payload/ 解压到全新的 apps/<id>/staging。
  4. 解压复制时为每个实际 payload 文件计算 size/SHA-256,作为 ExtractResult 的已观察文件清单。InstallService 将此清单写入 installed-app.json,不信任或依赖可选的 files.json 作为新的信任根;保留 v1 files.json 的协议语义和后续修复功能边界。
  5. 每次安装先对 app root 调用既有 installer.Recover。提取成功后以已有 Switcher 激活 staging;必需的注入 health check 先通过,随后在同一个 switch health 阶段原子写入新 installed-app.json,使 health/记录写失败走既有 rollback,保留旧 current 与旧记录。成功才清理 backup/journal。返回的阶段化错误和结果要能让后台调用方区分验证、解压、切换/回滚和记录失败;UI 仍只通过 application 事件/结果更新状态,不在 Gio Layout 做 IO。
  6. 为上列顺序补齐隔离的单元/集成测试:成功安装、Catalog 选择不一致、非普通/替换的完成文件、size/SHA 不符(确认 ZIP/app.json/staging 未触及)、恶意或不匹配 app.json、完整安装记录文件清单、已有版本健康或记录写入失败后的回滚、安装前 transaction recovery。测试包只使用运行时生成的虚构 ZIP/哈希。

验收要点

  • InstallService 的唯一可信输入是已验签/过滤 Catalog 的 entry + architecture;篡改下载 metadata、request/app ID 或 completed 路径不能改变实际验证的 package identity、size 或 SHA-256。
  • 普通完成文件的 Catalog size 和 SHA-256 均在同一打开句柄上通过后,才允许 ZIP 预扫描、app.json 读取、staging 创建和 payload 写入;所有失败保留 errors.Is 可识别的根因。
  • app.json 严格符合 v1,并与 Catalog 的 ID、版本、通道、最低系统、架构、入口和管理员标记一致;安全路径、未知字段、重复/缺失入口或不匹配一律拒绝。
  • 成功链为 recover → verify → manifest compare → safe staging extract → switch → health → installed-app record → committed cleanup;记录含每个实际 payload 文件的安全相对路径、字节数和 SHA-256。
  • health 或记录写失败时,已有版本的 current 与旧 installed-app.json 恢复并保持;首次安装不留下可执行 current。已有未完成 transaction 先按恢复状态机收敛。
  • go -C core vet ./...、go -C core test -count=1 ./...、./scripts/verify_phase0.ps1、python scripts/validate_agent_context.py 与 python scripts/validate_harness_governance.py 全部通过;任务执行记录写明结果。

边界(不改什么)

  • 不修改 Catalog 签名协议、Schema 或密钥;不为 nested package signature 虚构独立验签算法。外层已验签 Catalog 是本任务唯一的密码学身份来源。
  • 不做磁盘空间预检、程序占用/退出等待、面向 UI 的完整错误码映射或下载重试策略(T-303/T-401);不强杀或自动启动包内程序。
  • 不做 Gio 安装面板、下载完成到安装调用的 UI 编排或物理断电/杀毒软件/文件锁故障注入。T-302 只提供无头 core use case;真实 Windows VM/真机故障注入转入 T-601 发布前环境验证。
  • 不改变 files.json 的 v1/v1.1 协议或实现修复功能,不引入数据库、第三方包、Gio、Windows API 或 Go 1.21+ API。

协作约束

  • 当前项目为单 Agent 串行模式;当前 Agent 独占全部 write_paths,自行完成设计、安全复核、测试和提交,不启动子 Agent。
  • 先提交本任务规格与关联协议/架构/状态文档;领取后将本文件改为 DOING,填写当前 HEAD context_ref 与 work_branch: agent/codex/T-302,再运行基线和实现。
  • T-303、T-401 及任何后置任务在本任务 DONE、验证与实现提交前不得领取或提前修改。

执行记录

  • 2026-07-18:正式落成。冻结可信输入、同句柄 size/SHA/ZIP 顺序、严格 app.json 对齐、实际 payload 文件记录、health 内记录写入回滚语义和后续任务边界。
  • 2026-07-18:领取任务,基线为 6575c9ad9b997366514277a925196b2e52f16f75,工作分支 agent/codex/T-302;下一步运行统一初始化/完整基线,再开始实现。
  • 2026-07-18:基线通过:./init.ps1 完成治理、core 架构/Go 版本闸门、Go 1.20 core vet/test、modern/Win7 test/build 与 Python harness 校验。
  • 2026-07-18:实现 core/application/install.InstallService、同句柄 Extractor.ExtractVerifiedFile、严格且有 1 MiB 上限的 app.json 校验、payload 观测 hash 清单和 InstalledAppStore.EnsureAppRoot;Catalog 依赖父 application 包的既有图标投递链会产生 import cycle,因此 use case 放在独立 application 子包,未改变 Gio/UI 边界。
  • 2026-07-18:复核成功/size+hash+选择失败/严格 manifest/非普通文件/manifest 上限/transaction recovery/health 与记录写失败回滚;go -C core vet ./...、go -C core test -count=1 ./... 和 go -C core test -count=10 ./installer ./application/install 全部通过。真实 Windows 断电、文件锁/杀毒软件故障注入未在当前环境执行,保留为 T-601 发布前验证。