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

6.3 KiB

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-501 机器指纹 machine_hash 5
T-001
DONE 2026-07-19 null 94ea334487d7180b4af0fb59c129ff678d2c4ad0 null agent/codex/T-501
docs/tasks/T-501.md
core/licensing/
app-modern/platform/windows/
app-win7/platform/windows/
docs/00-ai-start-here.md
docs/04-architecture.md
docs/06-tasks.md
docs/api.md
docs/current-state.md

问题 / 背景

MVP 许可证必须在离线验签前可靠地比较 machine_hash,但仓库尚没有 core/licensing 或平台机器标识采集实现。machine_hash 必须在 modern 和 Win7 两端得到相同的稳定值;它只能是可传递给许可证的摘要,不能使原始设备标识、MAC、卷序列号或注册表值进入许可证、存储、事件、UI 或日志。

此前协议只说“多个稳定硬件标识清洗生成 hash”,同时又把来源和加权算法列为待定。这不足以让许可证签发端、两个客户端和后续 T-502 使用同一不可歧义的值。本任务冻结 v1 的最小双来源算法;它不假装解决硬件更换容错或换绑工作流。

方案

  1. 在 Go 1.20 兼容且不 import Windows/Gio 的 core/licensing 新增纯函数 DeriveMachineHash(machineGUID string, systemVolumeSerial uint32) (string, error)。它只接受严格的 Windows GUID(去首尾空白后为 36 位 ASCII UUID,统一为小写)和 uint32 卷序列号;输出为 64 个小写十六进制字符:SHA-256("softbox.machine-hash.v1\\x00" || normalized_machine_guid || "\\x00" || 8 位小写十六进制 volume_serial)。GUID 缺失、非 ASCII、格式不合法或控制字符均返回稳定的无敏感值错误;不提供单来源、MAC 或环境变量回退。
  2. 在两端 platform/windows 提供同名 MachineHash() (string, error) composition 边界。Windows 实现仅在进程内短暂读取 HKLM\\SOFTWARE\\Microsoft\\Cryptography 的 MachineGuid,并通过 GetWindowsDirectory 定位 Windows 目录所在根卷,再以 Win7 已支持的 GetVolumeInformationW wrapper 读取卷序列号,最后调用 core 纯函数。采集或规范化失败只返回泛化 sentinel,错误文本不得包含原始标识、卷号、路径或注册表值;不写文件、不发事件、不打日志。
  3. 为非 Windows 构建提供可测试的 fail-closed stub,返回既有 ErrUnsupported。保留采集拼装的注入 seam,使 Linux 无头测试能覆盖“任一来源失败/非法值不泄漏且不降级”及与 core 输出相同的结果;Windows API 调用本身通过交叉编译验证,不将宿主机器的真实标识写进测试或测试输出。
  4. 固定 API、架构、路线图和项目快照中的 v1 来源、规范化、算法和隐私边界。明确 v1 要求两个来源都可用;设备或系统卷变化须由后续许可证 rebind 策略处理,不能在客户端悄悄采用加权、部分匹配或降级 hash。T-502 只消费此函数返回的 hash 并完成 Ed25519 许可证解析/验签/比对。

验收要点

  • 纯 core 算法在 Go 1.20 可编译,对大小写/首尾空白等价 GUID 生成确定的 64 字符小写 SHA-256;非法 GUID、空值、控制字符和不同卷号均有受测的 fail-closed 结果,不存在隐式 MAC、单来源或随机回退。
  • 双端 Windows 层均使用相同的 core 算法与相同的两个来源;不修改宽泛 Platform 接口、不把 Windows API 带入 core,也不在静态导入表中引入 Win7 不支持 API。Windows 目录/卷查询、注册表读取或 core 拒绝时不暴露任何 raw identifier,且不产生持久化副作用。
  • Linux/非 Windows 下 MachineHash 稳定返回 ErrUnsupported;可注入的采集 seam 覆盖 GUID 读取失败、卷读取失败和非法 GUID,测试断言错误文本不含伪造 raw input。测试资料、断言、文档和执行记录均不得记录真实主机标识。
  • go -C core vet ./...、go -C core test -count=1 ./...、modern 与 Win7 的 go test -count=1 ./...、双端 Windows amd64 主程序构建、./scripts/verify_phase0.ps1、python scripts/validate_agent_context.py 与 python scripts/validate_harness_governance.py 全部通过。

边界(不改什么)

  • 不实现许可证 JSON 解析、Ed25519 验签、公钥、导入/试用/换绑 UI、撤销名单、许可证存储、服务端签发或子软件 SDK;这些属于 T-502/T-503 与仓库外服务端。
  • 不收集 MAC、BIOS/CPU/磁盘序列号、用户名、网络信息或广告标识;不持久化、显示、上传或记录 MachineGuid、卷序列号或其他原始标识。
  • 不修改 Catalog、下载、安装、更新、自更新、Gio Layout、数据目录或 Windows API 兼容策略;不把开发机实际机器摘要写入源码、fixtures、文档或提交。

协作约束

  • 当前项目为单 Agent 串行模式;当前 Agent 独占全部 write_paths,自行完成规格、实现、测试、审查、状态更新和提交,不启动子 Agent。
  • 本规格提交后才领取任务:将本文件改为 DOING,记录当时 HEAD 至 context_ref 与 work_branch: agent/codex/T-501,重跑基线后再实现。完成后只更新本任务为 DONE,不提前落成或实现 T-502。

执行记录

  • 2026-07-19:正式落成。冻结 machine_hash v1 的两个必需来源、严格规范化和带域分隔的 SHA-256 输入;拒绝单来源/加权回退,避免后续许可证签发端与双客户端生成不一致的摘要。确认两套现有 x/sys/windows 版本均提供 Win7 可用的 GetWindowsDirectory 和 GetVolumeInformation。
  • 2026-07-19:领取任务,基于 94ea334487d7180b4af0fb59c129ff678d2c4ad0 在 agent/codex/T-501 执行;先重跑基线,再实现纯 core 算法和双端受限采集边界。
  • 2026-07-19:完成。新增 Go 1.20 兼容的 core/licensing.DeriveMachineHash,以严格 ASCII GUID 规范化、固定域分隔和卷序列号生成小写 SHA-256;无效输入只返回无敏感值 sentinel。双端 platform/windows.MachineHash 短暂读取注册表和 Windows 目录所在卷,所有采集/规范化失败均折叠为不泄漏原始值的错误;非 Windows stub 保持 ErrUnsupported。局部 core/双端平台测试、完整 core/双端 app 测试和 Windows amd64 构建、./scripts/verify_phase0.ps1、上下文与治理校验均通过。