# 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 __windows_.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/` 为准,两者变化时同步本文。