2026-07-20 08:43:24 +08:00
|
|
|
|
---
|
|
|
|
|
|
id: T-503
|
|
|
|
|
|
title: 授权界面、离线导入与撤销状态
|
|
|
|
|
|
phase: 5
|
|
|
|
|
|
deps: [T-502, T-204]
|
2026-07-20 09:07:30 +08:00
|
|
|
|
status: DONE
|
2026-07-20 08:43:24 +08:00
|
|
|
|
created: 2026-07-20
|
|
|
|
|
|
issue: null
|
2026-07-20 09:07:30 +08:00
|
|
|
|
context_ref: b44531337d2fdca7c7c52fd42f66ad588ebcf9a2
|
2026-07-20 08:43:24 +08:00
|
|
|
|
claim_branch: null
|
2026-07-20 09:07:30 +08:00
|
|
|
|
work_branch: agent/codex/T-503
|
2026-07-20 08:43:24 +08:00
|
|
|
|
write_paths:
|
|
|
|
|
|
- docs/tasks/T-503.md
|
|
|
|
|
|
- core/licensing/
|
|
|
|
|
|
- core/application/
|
|
|
|
|
|
- core/application/launch/
|
|
|
|
|
|
- core/application/install/
|
|
|
|
|
|
- core/installer/
|
|
|
|
|
|
- core/storage/
|
|
|
|
|
|
- app-modern/cmd/softbox/
|
|
|
|
|
|
- app-modern/platform/windows/
|
|
|
|
|
|
- app-modern/ui/gio/
|
|
|
|
|
|
- app-win7/cmd/softbox/
|
|
|
|
|
|
- app-win7/platform/windows/
|
|
|
|
|
|
- app-win7/ui/gio/
|
|
|
|
|
|
- schemas/
|
|
|
|
|
|
- testdata/license/
|
|
|
|
|
|
- docs/04-architecture.md
|
|
|
|
|
|
- docs/06-tasks.md
|
|
|
|
|
|
- docs/api.md
|
|
|
|
|
|
- docs/current-state.md
|
|
|
|
|
|
- docs/routes.md
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 问题 / 背景
|
|
|
|
|
|
|
|
|
|
|
|
T-501/T-502 已经能在调用方给出机器摘要和公钥时,严格离线验证一份 License v1;它仍是纯验证器。仓库没有可信的许可证文件生命周期、撤销缓存、授权状态快照或 Gio 页面,也没有把已经验签的 `product_id` 接到启动边界。直接把导入的 JSON、测试 key 或“无文件即允许”的判断接到 UI/启动器,会破坏机器绑定和 fail-closed 边界。
|
|
|
|
|
|
|
|
|
|
|
|
现有 `app.json` 的 `product_id` 与 `supports_trial` 已经在受签名 ZIP 内校验,但安装记录尚未保留它们。若启动检查继续按 app ID 判断授权,客户端无法把许可证产品和已安装软件安全关联。License v1 也没有到期字段,不能把 `perpetual:false` 擅自解释成仍有效的本地试用。
|
|
|
|
|
|
|
|
|
|
|
|
## 方案
|
|
|
|
|
|
|
|
|
|
|
|
1. 在 `core/licensing` 冻结并实现 Revocation List v1。它使用调用方显式注入的授权 Ed25519 公钥(与 License v1 可以是同一受控配置,但绝不回退到测试/环境 key),只允许 `schema_version`、`generated_at`、`expires_at`、`revoked_license_ids`、`signature` 五个必需顶层字段;采用与 License 相同的受限 canonical JSON、严格 padded Base64 Ed25519 签名、UTC 秒级时间、唯一且合法的 `lic-...` ID。`expires_at` 必须晚于 `generated_at` 且间隔不超过 31 天。已验证撤销立即拒绝;未撤销且缓存未过期为 `current`;过期后最多 7 天为 `grace`;无缓存、无效缓存或超过宽限期为 `unavailable`,均不可授权。调用方注入时钟;所有错误/快照/UI 都不包含许可证 ID、签名、原始文档或机器来源。
|
|
|
|
|
|
2. 在 `core/storage` 新增受限 `licenses/` 存储。仅在 `licensing.Verifier` 成功匹配当前 machine hash 后,才把原始许可证 document 原子写入 `licenses/v1/<sha256>.license`(0600、临时文件 sync/close 后 rename);读取时只接受真实目录和该文件名模式、1 MiB 以下普通文件,并逐份重新验签。未知/链接/损坏缓存让授权状态 fail closed,不信任残留内容;相同 document 的导入幂等。撤销列表仅由已经验签的 document 原子替换 `licenses/revocations-v1.json`,与许可证分开保存。更新/安装/自更新都不得删除或覆盖此根。
|
|
|
|
|
|
3. 增加纯 core 的授权用例与受净化事件快照。它以显式的 verifier、machine hash、store、clock 和 `LicenseImportSource`/撤销来源组合,后台完成启动加载、导入与撤销更新,并只向 UI 发布 `LicenseChanged` 的固定状态码、machine hash、产品 ID、正式/非永久展示状态、撤销状态和换绑策略;不发送 license ID、签名、JSON、文件路径或原始 Windows 标识。默认 `UnconfiguredAuthorizationService` 明确发布 `authorization_unconfigured`,不创建 `licenses/`、不允许导入、不授权。UI 点击只入队请求;文件选择、文件读取、验签和写盘均在后台进行。
|
|
|
|
|
|
4. 将已通过签名 ZIP 的 `product_id` 与 `supports_trial` 从 manifest 经 `VerifiedPackage` 保存到 `installed-app.json`,并把启动的 `AuthorizationChecker` 入参改为 `product_id`。历史安装记录缺少这些字段视为授权元数据不可用而拒绝启动;`application/launch` 只接受已验证且撤销状态可用/宽限中的产品授权。`supports_trial:true` 仅表示软件支持试用,License v1 的 `perpetual:false` 仅显示为“非永久许可证”:本任务不实现本地试用计时、到期、绕过授权或自助换绑。
|
|
|
|
|
|
5. 在两套 Gio shell 增加同语义的 `LicensePanel`:左侧可进入“授权”,显示 machine hash、授权产品、正式/非永久状态、撤销当前/宽限/不可用状态,以及不修改本机绑定的换绑说明。无配置、无授权、撤销、宽限和导入失败均有明确可访问文案;只有配置就绪时显示可操作的导入按钮。按钮、键盘焦点和语义标签满足现有最小 44dp 可点击目标;Layout 只 drain 内存事件/请求,不做 I/O。现代和 Win7 command 都仅装配安全默认值;测试可注入假 source/受控测试 key 验证完整后台→relay→UI 链路,发布配置/真实信任根留给 T-603。
|
|
|
|
|
|
6. 更新 API、架构、路由、路线图和快照,使 License v1、撤销/7 天宽限、存储布局、状态码、产品映射和“不提供本地试用/换绑服务”的边界一致;新增不含私钥或生产值的静态/动态测试向量。
|
|
|
|
|
|
|
|
|
|
|
|
## 验收要点
|
|
|
|
|
|
|
|
|
|
|
|
- 许可证在写入前、每次启动加载和每次授权判断时都重新验证签名、machine hash 与 product;外来 JSON、错误 key/hash、超限/链接/未知/损坏缓存、撤销、缺失或超过宽限的撤销缓存一律不能授权,也不泄露敏感原文。
|
|
|
|
|
|
- Revocation List v1 覆盖 canonical 签名、重复/未知字段、非法 ID/时间/Base64、future/过期边界、撤销优先和恰好 7 天宽限边界;不存在本地 allow-all 或使用测试 key 的 production composition。
|
|
|
|
|
|
- 导入写入限制在受控 `licenses/v1/`,幂等、原子且 0600;列表返回深拷贝的非敏感授权事实。导入/加载/撤销失败均映射稳定状态码,不把路径、license ID、签名、JSON、GUID 或卷序列号放入 event、日志或界面。
|
|
|
|
|
|
- 已安装记录保留受验证 manifest 的 product/trial 元数据;启动以 product ID 检查且旧记录缺字段 fail closed。试用和换绑页面只展示已签名事实/下一步说明,不创建未签名试用授权、不改变 machine hash、不替换许可证。
|
|
|
|
|
|
- 两端 Gio 页面能从 `LicenseChanged` 事件刷新,未配置、无授权、撤销、宽限、导入失败和已授权状态均可见;导入点击不在 Layout 做磁盘/网络/哈希,后台结果通过现有有界 relay 唤醒 UI。
|
|
|
|
|
|
- `go -C core vet ./...`、`go -C core test -count=1 ./...`、两个 app 的 `go test -count=1 ./...`、两个 Windows amd64 主程序构建、`./scripts/verify_phase0.ps1`、`python scripts/validate_agent_context.py` 与 `python scripts/validate_harness_governance.py` 全部通过。
|
|
|
|
|
|
|
|
|
|
|
|
## 边界(不改什么)
|
|
|
|
|
|
|
|
|
|
|
|
- 不提交生产公钥、任何私钥、真实 license/revocation、机器摘要、注册码或正式下载 URL;不从环境变量、网络或 UI 文本取得信任根。
|
|
|
|
|
|
- 不实现在线账号、支付、自助换绑、人工申诉接口、许可证签发、撤销列表下载协议、试用计时/过期/本地绕过,或子软件 SDK 分发;这些需要发布端/产品协议另行确定。
|
|
|
|
|
|
- 不改 T-501 的指纹算法、Catalog 协议、下载/安装/更新/自更新事务或 Gio 之外的架构。安装记录只做携带已验证 manifest 授权元数据的最小兼容升级。
|
|
|
|
|
|
|
|
|
|
|
|
## 协作约束
|
|
|
|
|
|
|
|
|
|
|
|
- 当前项目为单 Agent 串行模式;当前 Agent 独占全部 `write_paths`,自行完成规格、实现、测试、审查、状态更新和提交,不启动子 Agent。
|
|
|
|
|
|
- 本规格提交后才领取任务:将本文件改为 `DOING`,填写当时 HEAD 至 `context_ref` 与 `work_branch: agent/codex/T-503`,重跑基线后再实现。完成时只更新本任务为 `DONE`,不提前落成后续任务。
|
|
|
|
|
|
|
|
|
|
|
|
## 执行记录
|
|
|
|
|
|
|
|
|
|
|
|
- 2026-07-20:正式落成。冻结 Revocation List v1、7 天离线宽限和受限 `licenses/` 缓存;明确产品授权必须使用受验证 manifest 的 product ID,且 License v1 不足以实现本地 trial/rebind。默认发布构建仍以未配置状态 fail closed,真实 trust root/发布配置交由 T-603。
|
2026-07-20 09:07:30 +08:00
|
|
|
|
- 2026-07-20:领取任务,基于 `b44531337d2fdca7c7c52fd42f66ad588ebcf9a2` 在 `agent/codex/T-503` 执行;先重跑基线,再实现授权存储、撤销状态、产品映射与双端 Gio 授权视图。
|
|
|
|
|
|
- 2026-07-20:完成。新增严格 Revocation List v1(共享 canonical JSON、Ed25519、31 天最大有效期、7 天离线宽限)和 `LicenseStore`(验签后 1 MiB 受限缓存、digest 文件名复核、0600 原子写入、每次读取重新验证);`AuthorizationService` 仅发布不含 license ID/JSON/签名/路径的授权快照,并在无/坏/过期撤销缓存时拒绝授权。已验证 package manifest 的 `product_id`/`supports_trial` 现在写入安装记录,启动改按 product ID 检查,历史记录缺字段返回 `authorization_unavailable`。现代/Win7 Gio shell 均增加授权页和后台导入请求接线;默认 cmd 只发布 unconfigured 状态,不使用测试 key 或 allow-all。试用/换绑只展示已签名事实,不创建本地授权或变更绑定。
|
|
|
|
|
|
- 2026-07-20:验证通过:`go -C core vet ./...`;`go -C core test -count=1 ./...`;`go -C app-modern test -count=1 ./...`;`go -C app-win7 test -count=1 ./...`;modern/Win7 Windows amd64 `go build ./cmd/softbox`(Win7 强制 Go 1.20.14);`./scripts/verify_phase0.ps1`;`python scripts/validate_agent_context.py`;`python scripts/validate_harness_governance.py`。提交前复核覆盖撤销边界、缓存 digest/布局、事件净化、旧安装记录 fail-closed 与 Gio Layout 无 I/O。
|