Prototype signed catalog fallback (T-101)

This commit is contained in:
ila
2026-07-16 16:13:18 +08:00
parent 45c242ecec
commit 0d6ed05d7e
15 changed files with 959 additions and 10 deletions
+1
View File
@@ -103,6 +103,7 @@ soft_quay/
- 软件主键是永久稳定的 `id`(小写英文/数字/短横线),不用名称;下架用 `status`,不用名称前缀。
- 清单验签失败时**拒绝**,回退到最后一次验证成功的缓存,绝不接受未验证的新内容。
- Phase 1 清单签名原型会拒绝重复字段、尾随 JSON 与非整数数字;签名域为移除顶层 `signature` 后的受限规范 JSON,细节见 [api.md](api.md)。
- channel 分 `modern` / `win7`,更新器必须校验 channel + min_os,禁止交叉升级。
### 4.2 本地动态数据(JSON + 原子写入)
+11
View File
@@ -58,6 +58,17 @@
- 下架:显式 `status: deprecated | hidden`,不用名称前缀。
- 过滤:按 `min_os` 与 `architectures` 过滤;不兼容软件可见说明但不可下载。
### 1.1 Phase 1 签名域原型
T-101 验证采用以下签名域,供客户端与后续发布器实现对齐:
1. 输入必须是单个 UTF-8 JSON object;重复字段、尾随 JSON、浮点/指数数字直接拒绝。
2. 读取顶层 `signature`(标准 Base64 编码的 64 字节 Ed25519 签名),然后从对象中移除该字段。
3. 对剩余值递归规范化:对象键按 Unicode 字符串升序排列;数组保持原顺序;字符串按 JSON 转义;数字仅允许 JSON 整数并保持其合法十进制写法;不保留无意义空白。
4. Ed25519 直接签名/验证上述规范 JSON 字节。
这是 Phase 1 风险原型结论。T-201 正式接入时必须与 `softbox-catalog` 发布端做跨实现向量测试,再冻结 Schema、密钥 ID/轮换字段和版本兼容策略;在此之前不得另造签名域。
## 2. 标准软件包协议 v1(ZIP)
```text
+10 -10
View File
@@ -13,36 +13,36 @@
## 当前快照
- 日期:2026-07-16
- 阶段:M1 已完成(Phase 0 工程骨架、状态/事件模型、双 Gio 窗口、平台 stub 与双目标验证闸门全部落地)
- 阶段:M2 进行中(Phase 1 的 T-101 清单验签与缓存回退原型已完成)
- 技术栈:根 Go workspace 纳入 core/app-modern/app-win7 三模块;`app-win7/go.work` 隔离 Go 1.20.14 构建;modern Gio v0.10.1 与 win7 Gio v0.6.0 已实际接入
- 生产代码:core 已有状态模型与事件 runtime;modern/win7 均可打开最小 AppShell,并有平台接口、Windows 实现与非 Windows stub
- 测试:core 覆盖状态/事件 runtime;两个 app 覆盖 AppShell 布局尺寸与平台 stub 契约;Phase 0 脚本统一执行全部验证
- 数据:无;Catalog 清单与测试样例待建
- 生产代码:core 已有状态/事件 runtime 与 Catalog Ed25519 验签、受限规范 JSON、验证后缓存回退原型;modern/win7 均可打开最小 AppShell
- 测试:core 覆盖状态/事件、Catalog 篡改/伪造/歧义输入、缓存回退和文件缓存恢复;两个 app 覆盖 AppShell 与平台 stub
- 数据:`testdata/catalog/` 已有公开虚构的合法 payload、篡改 payload 与伪造签名样例
- 标准启动路径:`./init.sh` / `./init.ps1`(同步依赖、执行完整 Phase 0 闸门、打印双目标构建命令)
- 标准验证路径:`bash scripts/verify_phase0.sh` / `./scripts/verify_phase0.ps1`
- 版本管理:git 已初始化,main 分支,远端 origin 为 Gitea `opc/soft_quay`;harness 文档已提交
- 当前 blocker:无;下一步按路线图落成并领取 T-101
- 当前 blocker:无;下一步按路线图落成并领取 T-102
## 当前目录要点
| 路径 | 状态 | 说明 |
| --- | --- | --- |
| `docs/` | 已有 | harness coding 文档集(本次初始化完成) |
| `docs/tasks/` | 已有 | 任务目录;T-001 已完成,T-002 待按路线图落成 |
| `docs/tasks/` | 已有 | Phase 0 四项与 T-101 已完成;T-102 待按路线图落成 |
| `scripts/` | 已有 | harness 治理、core 边界、Go 版本检查与 Phase 0 双平台验证入口 |
| `core/` | 已建 | Go 1.20 兼容共享模块;已有 domain 状态模型与 application 事件 runtime |
| `core/` | 已建 | Go 1.20 兼容共享模块;已有状态/事件与 Catalog 验签/缓存原型 |
| `app-modern/` | 已建 | Go 1.25.0 + Gio v0.10.1;可打开 Modern AppShell |
| `app-win7/` | 已建 | Go 1.20 + Gio v0.6.0;可打开带 Legacy 标识的 AppShell |
| `schemas/` | 待建 | 协议 JSON Schema(T-201) |
| `testdata/` | 待建 | 假数据测试样例(T-101 起) |
| `testdata/` | 已建 | 当前包含 Catalog 假数据与恶意样例;后续任务继续扩展 |
## 任务状态
任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要:
- 已完成:Phase 0 的 `T-001`、`T-002`、`T-003`、`T-004`。
- 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`。
- 正在进行:无。
- 下一个可领取任务:按路线图落成并领取 `T-101 签名清单验签与缓存回退原型`。
- 下一个可领取任务:按路线图落成并领取 `T-102 ZIP 安全解压原型`。
## 当前可运行内容
+65
View File
@@ -0,0 +1,65 @@
---
id: T-101
title: 签名 Catalog 验签与缓存回退原型
phase: 1
deps: [T-002]
status: DONE
created: 2026-07-16
issue: null
context_ref: 45c242ecec153aa2d409a46883da9845503f185c
claim_branch: null
work_branch: agent/codex/T-101
write_paths:
- docs/tasks/T-101.md
- core/catalog/
- testdata/catalog/
- testdata/README.md
- docs/api.md
- docs/04-architecture.md
- docs/current-state.md
---
## 问题 / 背景
Catalog 是后续下载与执行链路的信任根。当前协议只约定 Ed25519 和顶层 `signature`,尚未验证签名域、JSON 歧义处理、缓存替换与断网回退行为。若这里错误,篡改清单可能进入安装链路。
## 方案
1. 在 `core/catalog` 建立 Ed25519 verifier,签名域为“移除顶层 `signature` 后的受限规范 JSON”。
2. 解析时拒绝重复字段、尾随 JSON、非对象根、非整数数字、缺失/非法长度签名,避免不同解析器对同一文档产生歧义。
3. 建立 Fetcher/Cache 接口与 Loader:远端验签成功后才写缓存;远端获取或验签失败时只使用再次验签通过的缓存。
4. 提供文件缓存原型,使用同目录临时文件 + current/backup 改名,并能读取中断后遗留的 backup。
5. 在 `testdata/catalog` 放置假数据与恶意样例;测试使用明确标注的专用测试密钥,不提交生产私钥。
6. 将签名域与原型结论同步到 `docs/api.md`、`docs/04-architecture.md`。
## 验收要点
- 合法测试清单验签通过。
- 篡改内容复用合法签名、伪造签名、重复字段、浮点/指数数字均被拒绝。
- 远端验签失败不会覆盖最后有效缓存;断网可返回再次验签通过的缓存。
- 缓存自身被篡改时拒绝,不得当作离线可信数据。
- 文件缓存更新与 backup 恢复测试通过。
- `cd core && go vet ./... && go test -count=1 ./...` 通过,Phase 0 双目标闸门继续通过。
## 边界(不改什么)
- 不实现 HTTPS 客户端、Catalog 字段 Schema、OS/架构过滤或 UI 接入(T-201)。
- 不定稿密钥轮换、撤销名单和 package 独立签名域。
- 不引入第三方 JSON canonicalization 或加密依赖。
- 不提交生产私钥、真实 URL 或真实签名。
## 协作约束
未启用 Gitea;本任务在 `agent/codex/T-101` 分支串行执行。签名格式结论标记为 Phase 1 原型,T-201 正式接入时再与发布端共同冻结。
## 执行记录
- 2026-07-16:在 `core/catalog` 建立受限 JSON 解析/规范化与 Ed25519 verifier;拒绝重复字段、尾随值、无效 UTF-8、非对象根、非整数数字和非法签名编码/长度。
- 2026-07-16:签名域确定为移除顶层 `signature` 后的受限规范 JSON;对象键排序、数组保序、字符串标准 JSON 转义、数字仅允许整数。该结论已同步 `docs/api.md` 与 `docs/04-architecture.md`,并明确为 T-201 前的 Phase 1 原型。
- 2026-07-16:建立 Fetcher/Cache/Loader;远端只有验签成功才写缓存,获取失败或验签失败时只返回再次验签成功的缓存;缓存写失败作为非致命 warning 暴露。
- 2026-07-16:建立 FileCache 临时文件 + current/backup 切换原型,并支持 current 缺失时读取中断遗留 backup。
- 2026-07-16:新增公开虚构的 Catalog testdata;测试密钥由测试代码中的明确测试 seed 运行时派生,仓库不含生产私钥。
- 定向测试通过:合法、篡改、伪造、重复字段、浮点、指数、缺签名、尾随 JSON、无效 UTF-8、断网回退、坏缓存、缓存写失败与中断 backup 恢复。
- 验证通过:Go 1.20.14 `go vet ./...`、`go test -count=1 ./...`。
- 验证通过:`./scripts/verify_phase0.ps1`,包含 core 边界/版本检查和 modern/win7 双目标构建。
- 验证通过:`python scripts/validate_harness_governance.py`。