Split docs/softbox-catalog-design.md into the numbered harness doc set (00-06, api.md, current-state, agent-context, tasks) following the harness_coding_docs template and soft_quay conventions. Register the nine open decision items from the design spec into the 06-tasks roadmap as W- tasks and backlog entries. Keep the original design spec as an archived design input with a header note. Recreated after the repository's previous git history was lost to an external reset; content matches the original initial commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
355 lines
21 KiB
Markdown
355 lines
21 KiB
Markdown
# soft_quay_web 设计说明(愿景 · 架构 · 协议契约)
|
|
|
|
> **定位说明(2026-07-20)**:本文是立项前的**原始设计规格**,内容已拆分进本仓库 harness coding 编号文档
|
|
> ([`01-vision.md`](01-vision.md) / [`02-requirements.md`](02-requirements.md) / [`03-tech-stack.md`](03-tech-stack.md) /
|
|
> [`04-architecture.md`](04-architecture.md) / [`api.md`](api.md) / [`06-tasks.md`](06-tasks.md) 的裁定清单)。
|
|
> 日常开发以编号文档为准,与本文冲突时以编号文档为准;本文保留作设计输入存档,不再逐项维护。
|
|
|
|
> 本文是 **soft_quay_web**(SoftBox 软件盒子的中央发布 / 登记系统;其 Web 管理端即本仓库 `soft_quay_web`)的设计规格。
|
|
> 权威协议以客户端仓库 `soft_quay/docs/api.md`、`soft_quay/schemas/*.json` 和 `soft_quay/testdata/catalog/canonical-vectors.json` 为准;本文引用而非另建一份,两者变化时同步。
|
|
> 状态:**尚未落地**,属客户端 `02-requirements.md` 第七节待确认风险。本文供另起本仓库时作为规格起点。
|
|
> 更新日期:2026-07-19
|
|
|
|
---
|
|
|
|
## 一、愿景
|
|
|
|
让 SoftBox 的软件发布者(自己)能够**集中、安全、可审计地**把自家系列软件发布给所有盒子用户:一处登记元数据、一处签名、一处托管,客户端离线即可验真。
|
|
|
|
> 发布者在一个后台里完成"登记软件 → 接收构建包 → 校验 → 签名 → 生成双通道清单 → 上传 → 发现新版本",而**签名私钥永不离开发布系统,客户端只内置公钥**。
|
|
|
|
它把整个 SoftBox 产品家族的"信任根"收敛到一处:客户端拿到的每一份清单、每一个安装包、每一张许可证,都能用内置 Ed25519 公钥离线验签,不依赖任何在线接口的可用性。
|
|
|
|
### 设计原则
|
|
|
|
- **静态分发优先**:最终产物是放在对象存储 / CDN 上的**静态签名文件**,不是运行时动态 API;客户端离线验签。
|
|
- **私钥隔离至上**:Ed25519 私钥是整个体系的信任根,只存在于受控签名服务,绝不进客户端、代码仓库或普通后台进程。
|
|
- **协议以客户端为权威源**:清单 / 包协议 Schema 和签名向量 corpus 以 `soft_quay` 为准,发布端**字节级对齐**,不另造 canonicalizer 或第二套验签域。
|
|
- **通道隔离**:modern / win7 双通道全程不交叉,Win7 用户永不拿到无法启动的现代版包。
|
|
- **可审计**:每次发布、下架、撤销、许可证签发都有不可否认的记录。
|
|
|
|
---
|
|
|
|
## 二、定位与范围
|
|
|
|
### 2.1 一句话定位
|
|
|
|
soft_quay_web = **软件登记 + 构建包接收校验 + Ed25519 签名服务 + 双通道清单生成 + 对象存储发布 + 许可证/撤销签发 + Web 管理后台**;产物是静态签名文件,信任根是私钥。
|
|
|
|
### 2.2 做什么
|
|
|
|
见第五节功能清单。
|
|
|
|
### 2.3 明确不做
|
|
|
|
- **不含子软件业务源码**——那些在各自 `app-*` 仓库。
|
|
- **不含盒子的下载 / 更新 / 安装 / 授权实现**——那些在 `soft_quay` 客户端。
|
|
- **不是动态在线 API / 应用商店服务**——无面向客户端的运行时接口、无账号会话;客户端只拉静态签名文件。
|
|
- **不生成未测试目标的占位包**。
|
|
- **不定义第二套包级验签域**——外层清单 Ed25519 签名已覆盖 package 的 `url/size/sha256/signature` 文本(见 §6.4)。
|
|
|
|
---
|
|
|
|
## 三、与客户端的信任模型(核心)
|
|
|
|
这是本系统一切设计的出发点。客户端 `soft_quay` 对远端内容的信任**仅来自 Ed25519 签名**:
|
|
|
|
```text
|
|
soft_quay_web(持私钥) soft_quay 客户端(内置公钥)
|
|
───────────────────────── ─────────────────────────
|
|
用受限规范 JSON + 私钥签名 ──► 拉取 → 去掉顶层 signature → 同规则规范化
|
|
生成 manifest-modern/win7.json → Ed25519 验签 → Schema/channel/架构过滤
|
|
上传对象存储 / CDN(HTTPS) → 缓存;验签失败则拒绝、退回上次可信缓存
|
|
包 url/size/sha256 写入清单 → 下载 → 同句柄 size→SHA-256→安全解压
|
|
```
|
|
|
|
由此推出对本系统的硬性要求:
|
|
|
|
1. **规范化必须字节级一致**:发布端与客户端对同一 JSON 输入必须产出**逐字节相同**的规范字节与签名。二者共用 `canonical-vectors.json` corpus 作为跨实现回归基线,**发布端不得用自身 canonicalizer 重新生成期望值**。
|
|
2. **私钥保管是最高安全目标**:私钥泄露 = 可伪造任意清单/包记录/许可证 = 客户端全线沦陷。
|
|
3. **客户端不做在线校验**:因此下架、撤销、版本兼容都必须能"离线表达"在签名文件里(status 字段、签名撤销名单 + 宽限期)。
|
|
|
|
---
|
|
|
|
## 四、系统架构
|
|
|
|
### 4.1 组件
|
|
|
|
```text
|
|
┌─────────────────────────────────────────────┐
|
|
子软件 app-* CI │ 构建标准 ZIP 包 + Release 元数据 │
|
|
└───────────────┬─────────────────────────────┘
|
|
│ 上传候选包
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ soft_quay_web │
|
|
│ │
|
|
│ ┌────────────┐ ┌────────────┐ ┌──────────────────────┐ │
|
|
│ │ Ingestion │──►│ Registry │──►│ Manifest Generator │ │
|
|
│ │ 包接收校验 │ │ 元数据/发布 │ │ 双通道清单组装 │ │
|
|
│ └────────────┘ │ 记录 (DB) │ └──────────┬───────────┘ │
|
|
│ │ └────────────┘ │ 待签名规范字节 │
|
|
│ │ size/SHA-256 ▼ │
|
|
│ │ ┌──────────────────────┐ │
|
|
│ │ │ Signing Service │ │
|
|
│ │ │ (Ed25519 私钥,隔离/ │ │
|
|
│ │ │ KMS/HSM,受限接口) │ │
|
|
│ │ └──────────┬───────────┘ │
|
|
│ │ │ 签名 │
|
|
│ ▼ ▼ │
|
|
│ ┌────────────┐ ┌────────────┐ ┌──────────────────────┐ │
|
|
│ │ License & │ │ Web 管理端 │ │ Publisher (发布器) │ │
|
|
│ │ Revocation │ │ soft_quay_ │ │ 上传对象存储/CDN │ │
|
|
│ │ 签发 │ │ web(UI) │ │ │ │
|
|
│ └────────────┘ └────────────┘ └──────────┬───────────┘ │
|
|
│ │ 审计日志 (append-only) │ │
|
|
└────────┼──────────────────────────────────────┼──────────────┘
|
|
▼ ▼
|
|
审计/合规存档 对象存储 / CDN(静态托管)
|
|
manifest-*.json / *.zip / icons
|
|
│ HTTPS
|
|
▼
|
|
soft_quay 客户端
|
|
```
|
|
|
|
### 4.2 组件职责
|
|
|
|
| 组件 | 职责 | 不含 |
|
|
| --- | --- | --- |
|
|
| Ingestion(接收校验) | 接收子软件 CI 的 ZIP 候选包;校验包协议结构(app.json/files.json/payload)、计算 size 与 SHA-256、比对声明版本/ID/架构 | 构建子软件、跑子软件业务 |
|
|
| Registry(登记库) | 软件元数据、发布记录、包坐标(url/size/sha256)、status、channel、Catalog 版本 | 子软件源码、客户端状态 |
|
|
| Manifest Generator | 从 Registry 按 channel 组装 `manifest-modern.json` / `manifest-win7.json`,产出**待签名规范字节** | 签名(委托签名服务) |
|
|
| Signing Service | 唯一持有 Ed25519 私钥;对规范字节签名(清单/许可证/撤销名单);理想部署为 KMS/HSM 或独立最小权限服务 | 业务逻辑、任意数据签名(只签受控结构) |
|
|
| License & Revocation | 绑定 machine_hash + products 签发许可证;维护签名撤销名单 + 宽限期 | 保存原始硬件序列号/MAC |
|
|
| Publisher(发布器) | 把签名清单、ZIP 包、图标上传对象存储 / CDN;保证原子发布(先传包再切清单) | 生成未签名/未校验产物 |
|
|
| Web 管理端(soft_quay_web) | 发布者操作界面:登记/发布/下架/撤销、发布记录浏览、许可证签发、审计查看 | 直接持有私钥(经受控签名服务接口) |
|
|
| Audit | append-only 记录每次发布/下架/撤销/签发 | 可被覆盖的可变日志 |
|
|
|
|
### 4.3 关键边界
|
|
|
|
- **签名服务是唯一持钥点**:其余组件(含 Web 后台)只能提交"受限结构 + 请求签名",拿回签名串,拿不到私钥。
|
|
- **Web 管理端不等于签名端**:soft_quay_web 负责人机交互与编排,签名经受控接口调用签名服务;私钥不进 Web 进程。
|
|
- **协议 Schema / corpus 单一权威源**:引用 `soft_quay/schemas/` 与 `canonical-vectors.json`,CI 校验发布端产物能被同一份 corpus 与 Schema 通过。
|
|
|
|
---
|
|
|
|
## 五、功能清单
|
|
|
|
### 5.1 软件登记与元数据
|
|
|
|
维护每款软件的元数据(客户端列表页唯一来源),字段与约束见 §6.1:稳定 `id`、`name`、`description`、`version`、`channel`、`status`、`category`、`tags`、`icon`、`homepage`、`tutorial`、`min_os`、`architectures`、`entry_exe`、`requires_admin`、`packages`。
|
|
|
|
### 5.2 构建包接收与校验(Ingestion)
|
|
|
|
接收子软件 CI 产出的标准 ZIP 包并校验:
|
|
|
|
- 包结构:根 `app.json` + 可选 `files.json` + `payload/`;
|
|
- `app.json` 与登记的 id/version/channel/min_os/architecture 一致;
|
|
- 计算 `size` 与 `SHA-256`,写入发布记录;
|
|
- 只接受实际支持并测试过的目标,不生成未测试平台占位包。
|
|
|
|
### 5.3 发布记录(Release)
|
|
|
|
每次发布登记包的下载坐标与校验信息:`url`(绝对 HTTPS)、`size`、`sha256`、`signature`(见 §6.4)。
|
|
|
|
### 5.4 Schema 校验(协议权威)
|
|
|
|
发布前用 `soft_quay/schemas/manifest.schema.json`、`app.schema.json` 校验:Schema 合规、无未知/重复/缺失字段、id 唯一、SemVer 合法、architectures 与 packages 键一一对应、channel 目标一致、URL 为绝对 HTTPS。
|
|
|
|
### 5.5 Ed25519 签名(核心)
|
|
|
|
- 签名域 = 移除顶层 `signature` 后的**受限规范 JSON**(§6.3);
|
|
- `signature` 为唯一标准 padded Base64;
|
|
- 与客户端共用 `canonical-vectors.json` 做跨实现回归。
|
|
|
|
### 5.6 双通道清单生成
|
|
|
|
产出 `manifest-modern.json` 与 `manifest-win7.json`,`channel` 字段隔离,互不交叉。
|
|
|
|
### 5.7 发布流水线
|
|
|
|
见 §七。
|
|
|
|
### 5.8 下架与撤销
|
|
|
|
- 软件下架用 `status`(active / deprecated / hidden),不改名;
|
|
- 维护**签名的许可证撤销名单**,客户端缓存 + 网络失败宽限期。
|
|
|
|
### 5.9 许可证签发
|
|
|
|
绑定 `machine_hash` + `products` 签发 Ed25519 许可证(§6.5);不保存原始硬件标识。
|
|
|
|
### 5.10 版本 / 协议兼容管理
|
|
|
|
| 版本类型 | 示例 | 用途 |
|
|
| --- | --- | --- |
|
|
| 产品版本 | 1.4.2 | 用户看到的软件版本 |
|
|
| SDK 版本 | sdk/core v1.2.0 | 公共能力兼容性 |
|
|
| 软件包协议 | schema_version 1 | 盒子与包的结构协议 |
|
|
| Catalog 版本 | 2026.07.16.1 | 目录发布时间 |
|
|
|
|
原则:SDK 升级不强迫历史软件立即升级;盒子至少兼容当前与前一个包协议版本;清单顶层 `min_box_version` 声明所需最低盒子版本。
|
|
|
|
### 5.11 审计
|
|
|
|
append-only 记录发布/下架/撤销/许可证签发,含操作者、时间、内容摘要、签名指纹。
|
|
|
|
---
|
|
|
|
## 六、数据与协议契约
|
|
|
|
> 以下字段与约束**必须**与 `soft_quay/schemas/*.json` 和 `api.md` 一致;此处为发布端视角的摘要,冲突时以客户端仓库为准。
|
|
|
|
### 6.1 Catalog 清单 v1(manifest-{modern,win7}.json)
|
|
|
|
顶层(`additionalProperties: false`,全部必填):
|
|
|
|
| 字段 | 约束 |
|
|
| --- | --- |
|
|
| `schema_version` | 常量 `1` |
|
|
| `channel` | `modern` 或 `win7` |
|
|
| `generated_at` | RFC3339 date-time |
|
|
| `min_box_version` | SemVer |
|
|
| `apps` | app 数组 |
|
|
| `signature` | Ed25519,`^[A-Za-z0-9+/]{86}==$` |
|
|
|
|
每个 app(`additionalProperties: false`):
|
|
|
|
| 字段 | 约束 |
|
|
| --- | --- |
|
|
| `id` | `^[a-z0-9-]+$`,永久稳定不可变 |
|
|
| `name` / `description` | 非空字符串 |
|
|
| `version` | SemVer 2.0.0 |
|
|
| `channel` | 常量 `stable` |
|
|
| `status` | `active` / `deprecated` / `hidden` |
|
|
| `category` | 非空(单一分类) |
|
|
| `tags` | 非空字符串数组(≥1) |
|
|
| `icon` | `^sha256:[0-9A-Fa-f]{64}$`(内容引用,非 URL;可选) |
|
|
| `homepage` / `tutorial` | 绝对 HTTPS(可选) |
|
|
| `min_os` | `windows-7-sp1` / `windows-10` / `windows-11` |
|
|
| `architectures` | `["386"|"amd64"]`,唯一,≥1 |
|
|
| `entry_exe` | 安全相对路径(拒绝首尾空格/尾随点/DOS 设备名/反斜杠/`:<>"|?*`/`..`;见 schema 的 pattern 与运行时校验) |
|
|
| `requires_admin` | 布尔 |
|
|
| `packages` | `{386?, amd64?}`,≥1;每个含 `url/size/sha256/signature` |
|
|
|
|
package 对象:`url`(绝对 HTTPS)、`size`(整数 ≥1)、`sha256`(64 hex)、`signature`(Ed25519)。
|
|
|
|
### 6.2 通用约定
|
|
|
|
- 传输 HTTPS,JSON UTF-8;时间 RFC3339(UTC)。
|
|
- URL 只接受无用户信息、无 fragment 的绝对 HTTPS。
|
|
- 数字仅整数 token(签名域拒绝浮点/指数/`-0`,大整数保持 token 不失精度)。
|
|
|
|
### 6.3 签名域(必须与客户端字节对齐)
|
|
|
|
1. 输入为单个 UTF-8 JSON object;拒绝重复键、尾随数据、浮点/指数数字、非法 Unicode surrogate。
|
|
2. 读取顶层 `signature`(标准 padded Base64,严格解码为 64 字节;拒绝 CR/LF、其他空白、缺失/额外 padding),从对象移除该字段。
|
|
3. 对剩余值递归规范化:对象键按 Unicode 码点升序、数组保序、字符串按 JSON 转义、数字仅整数 token、无多余空白。
|
|
4. Ed25519 对上述规范字节签名。
|
|
|
|
**跨实现回归**:`soft_quay/testdata/catalog/canonical-vectors.json` 含测试公钥(`public_key_base64`)与向量(`document` / `signed_payload_base64` / `signature` / `want_error`),覆盖 Unicode 键序、转义、`<>&`/U+2028/U+2029、合法/非法 surrogate、`-0`/大整数、嵌套 signature、Base64 变体。发布端 CI **必读该 corpus 断言**,不得自举期望值。
|
|
|
|
### 6.4 package.signature 的语义(待定,见未定项)
|
|
|
|
当前客户端**不独立验证** package 的 `signature`——其文本已被外层清单 Ed25519 签名覆盖,客户端不臆造第二套包级验签域。但 Schema 要求该字段存在且形状合法。发布端 v1 须产出形状合法的 Ed25519 串;**其独立签名域(待签名字节 + 公钥)尚未定义**,是未定项。
|
|
|
|
### 6.5 许可证(server 签发 → 客户端离线验证)
|
|
|
|
```json
|
|
{
|
|
"schema_version": 1,
|
|
"license_id": "lic-...",
|
|
"machine_hash": "...",
|
|
"products": ["product-json-parser"],
|
|
"issued_at": "2026-07-16T00:00:00Z",
|
|
"perpetual": true,
|
|
"update_policy": "updates-until-2027-12-31",
|
|
"rebind_policy": "self-service-1-per-90d",
|
|
"signature": "..."
|
|
}
|
|
```
|
|
|
|
- `machine_hash` 由客户端平台层多硬件标识清洗生成;许可证**不保存**原始序列号/MAC。
|
|
- Ed25519 签名;客户端内置公钥离线验签,保存于 `licenses/`,更新不覆盖。
|
|
- 撤销名单同样签名 + 客户端缓存 + 网络失败宽限期。
|
|
- 子软件用 SDK licensing 逻辑**独立再验证**(签名 + machine_hash + product_id)。
|
|
|
|
### 6.6 标准软件包协议 v1(ZIP,发布端负责接收校验)
|
|
|
|
```text
|
|
<id>_<version>_windows_<arch>.zip
|
|
├─ app.json # schema_version/id/name/vendor/version/channel/min_os/
|
|
│ # architecture/entrypoint/working_directory/product_id/
|
|
│ # supports_trial/requires_admin/data_policy/update_policy
|
|
├─ files.json # 解压后文件清单(v1.1 强制,v1 推荐)
|
|
└─ payload/ # 实际安装文件
|
|
```
|
|
|
|
发布端校验 app.json 与登记一致、payload 结构合法,并计算整包 SHA-256 写入清单;`data_policy` 固定 `local-app-data`、`update_policy` 固定 `managed-by-softbox`。
|
|
|
|
---
|
|
|
|
## 七、发布流水线
|
|
|
|
```text
|
|
子软件 CI 产出标准 ZIP 包 + Release 元数据
|
|
→ Ingestion 校验包结构 / app.json 一致性 / 计算 size + SHA-256
|
|
→ Registry 登记发布记录(url 坐标、status、channel、Catalog 版本)
|
|
→ Schema 校验(manifest/app schema)
|
|
→ Manifest Generator 组装 modern/win7 待签名规范字节
|
|
→ Signing Service 用 Ed25519 私钥签名
|
|
→ CI 用 canonical-vectors corpus + Schema 回归验证产物
|
|
→ Publisher 原子上传:先传 ZIP 包/图标到对象存储,再切换签名清单
|
|
→ soft_quay 客户端发现新版本
|
|
```
|
|
|
|
一个产品可产出多目标(如 `json-parser_1.4.2_windows_amd64.zip`、`..._win7_amd64.zip`、`..._win7_386.zip`);只生成实际支持并测试过的目标。原子性要求:**包必须先于清单可用**,避免客户端拿到指向尚未上传包的清单。
|
|
|
|
---
|
|
|
|
## 八、签名与密钥管理
|
|
|
|
- **私钥隔离**:理想部署为 KMS/HSM 或独立最小权限签名服务;Web 后台与业务进程只经受控接口请求签名,不接触私钥材料。
|
|
- **只签受控结构**:签名服务只对"清单 / 许可证 / 撤销名单"这几类已知结构签名,不对任意字节签名(防被当通用签名 oracle)。
|
|
- **公钥分发**:客户端内置公钥;测试用公钥见 corpus。正式公钥的内置与更新方式随密钥轮换设计裁定。
|
|
- **审计**:每次签名请求记录操作者、目标结构摘要、时间、密钥指纹。
|
|
|
|
---
|
|
|
|
## 九、技术选型建议(soft_quay_web)
|
|
|
|
> 待定,以下为建议,立项时在本仓库 tech-stack 文档固定。
|
|
|
|
- 形态:Web 管理后台 + 后端 API + 独立签名服务 + 对象存储集成。
|
|
- 后端语言:建议 Go(与客户端同栈,可**直接复用同一套 canonical/verify 实现与 Schema**,天然字节对齐;这是选 Go 的最强理由)。若选其他语言,必须用 corpus 做严格字节回归。
|
|
- 存储:发布记录用关系型库;产物用对象存储 / CDN。
|
|
- 签名:私钥入 KMS/HSM 或隔离服务;绝不入库、不进 Web 进程环境变量明文。
|
|
- 协议 Schema / corpus:以 `soft_quay` 为权威源引用(git submodule 或版本化拷贝 + CI 校验一致),不另建。
|
|
|
|
---
|
|
|
|
## 十、未定项 / 待确认(立项前必须裁定)
|
|
|
|
| 项 | 说明 |
|
|
| --- | --- |
|
|
| 签名私钥管理 | 谁持有、如何保管与**轮换**、签发流程;私钥绝不进客户端/仓库/Web 进程 |
|
|
| 密钥 ID / 轮换字段 | 清单/许可证尚无 key ID / rotation 字段;`api.md` 注明"单公钥协议升级前不得另造签名域",多公钥/轮换需先做协议升级 |
|
|
| package.signature 独立语义 | v1 客户端不独立验;若要独立包级验签,需定义待签名字节 + 公钥域(见 §6.4) |
|
|
| 正式域名与对象存储 | 清单/包的正式 HTTPS 域名、对象存储/CDN 选型、**带宽成本与防盗链**策略 |
|
|
| 图标分发字段 | 图标从内容哈希(`sha256:`)到实际下载位置/分辨率变体的**发布端映射格式**(`api.md` 待实现项) |
|
|
| 撤销名单结构 | revocation list 字段结构与**宽限期时长** |
|
|
| 跨仓库 CI 对齐 | 发布端消费同一 corpus / Schema 的 CI 证据需与 `soft_quay`(T-614)跨仓库协调 |
|
|
| 授权产品映射 | `product_id` 与许可证 `products` 的登记与映射管理 |
|
|
| 发布端形态与权限模型 | Web 后台的操作者权限、审批流(谁能发布/下架/撤销/签发)|
|
|
|
|
---
|
|
|
|
## 十一、首期落地建议
|
|
|
|
- 首期用**本地 / 静态文件模拟**:本地生成签名清单 + 本地 HTTP 静态服务,即可让客户端跑通"清单→下载→安装"闭环,不必先建正式对象存储。
|
|
- 私钥先用**离线生成的测试密钥对**(与客户端内置测试公钥、corpus `public_key_base64` 配对);正式私钥管理另裁。
|
|
- **先落协议对齐再落 UI**:第一里程碑做到"能产出被 `soft_quay` 客户端验签通过、被 corpus + Schema 回归通过的 manifest",再迭代 Web 管理界面。
|
|
- 协议 Schema 与 corpus 以 `soft_quay` 为权威源引用,避免漂移。
|
|
|
|
> 本文为设计规格;字段与协议细节以 `soft_quay/docs/api.md` 与 `soft_quay/schemas/` 为准,两者变化时同步本文。
|