任务:持久化格式与算法升级改造
2026-10-13 立项。目标是满足已确认需求"格式与算法升级"一节。项目未发布,不需要兼容现有数据;现有测试全部通过是回归基线,新增格式验收不得放宽旧断言。改造期间不加功能;具体边界见 ADR 005。
现状
- 15 个生产
src文件引用 bincode 1.3(含错误转换和测试模块),位置编码没有字段标签,不能跳过未知字段。文件:cipherfs-core的error.rs、index/{membership,records,retirement,segment,snapshot}.rs、storage/metadata_store.rs、vault.rs、vault/{authority_keys,devices,maintenance,recovery,snapshot,sync}.rs;cipherfs-sync的pairing.rs。另有集成测试core/tests/pages.rs、sync/tests/retirement_gc.rs;锁文件还通过未使用的async-session和 Iroh umbrella 的 RPC 依赖闭包引入它。 VersionId、RetirementId、快照 frontier 和上传清单 commitment 使用带现有派生标签的 BLAKE3,原像为 bincode;版本验证会解码后重编码。普通成员/退休记录由段外签名认证;设备请求、恢复 enrollment 与 RecoveryEnvelope 有独立元组签名。配对 transcript 用 ticket-secret keyed BLAKE3 MAC/SAS,不是独立 Ed25519 签名。上传清单当前清空授权证明后重编码取 commitment。- 容器头是手写的:段
CFSSEG01固定头 104 bytes;快照CFSSNP01/CFSSNP02固定头 112 bytes,载荷为 AEAD 密封的 bincode,末尾 Ed25519 签名。02 还有未加密但受签名/AAD 认证的 authority 长度和原始段向量;快照写 02,上传清单复用 01。恢复对象是 bincode RecoveryEnvelope,keys 单独非对称密封。 metadata.redb有 31 张表:18 张使用 bincode 后本机密封;config 整体未密封,含各自加密的 wraps;还有明文 bincode、原始加密签名段、密封密钥、BE 整数/位置和空标记。settings 没有 schema 号。CAS 另有 3 张表,含整数和手工位置编码,同属本机数据库改造范围。- 对象名
i/<device>/<seq>-<hash>-<len>、p/<hh>/<hash>-<len>、n/<frontier_hash>-<hash>-<len>、u/<hash>-<len>中哈希和设备 ID 是裸 hex,隐含 BLAKE3 和 Ed25519;v/、s/是随机 16-byte ID,r/的 epoch 是派生身份,不能都视为公钥。 - 内容层已有算法号:
ContentBody.suite是(u8, u8),分别编码 EncryptionAlgorithm/HashAlgorithm,XChaCha20Poly1305=1、Aes256Gcm=2、ChaCha20Poly1305=3,Blake3=1、Sha256=2、Sha512=3。内容当前只接受 Blake3;注册号不等于已启用。保留全部既有数值。 - 密钥派生标签已带版本(
cipherfs index v6、cipherfs version id v1等),不改。
设计决定
- 编码用 protobuf,proto3,包名
cipherfs.v1,文件crates/cipherfs-core/proto/cipherfs.proto。生成的 Rust 代码提交进仓库(crates/cipherfs-core/src/wire/generated.rs),由scripts/projects.sh proto重新生成;生成器是工作区内一个小 crate,用 prost-build 加 protox(纯 Rust,不需要 protoc 二进制)。build.rs不得在构建时调用任何外部工具。不使用宿主 protoc;proto 注释必须单独写在声明上一行,废弃字段号 reserved。 - 身份即字节。定义两个通用封装:
HashedBytes { bytes body }(身份由 body 派生)和SignedBytes { bytes body; Signature signature }。各被哈希或签名的记录按原有载体携带封装;段中的版本、成员、退休等保留原始 record body,配对消息与恢复对象保留各自原始 body;写入方编码一次,哈希和签名只算在body上,读取方先验再解码body,数据库、重开、快照与转发保留原始字节及未知字段。普通记录仍由段签名认证,不新增独立记录签名;配对保持原有 MAC。VersionId的派生标签保持cipherfs version id v1,原像改为 body 字节。删除所有"解码后重编码再比对"的代码。 - 算法标识。新增
Digest { HashAlgorithm algorithm; bytes value }、Signature { SignatureAlgorithm algorithm; bytes value }、PublicKey { PublicKeyAlgorithm algorithm; bytes value },签名 Ed25519=1、公钥 Ed25519=1/X25519=2,以及KdfAlgorithm(Argon2id=1)。Rust 侧对应类型实现Display/FromStr,文本形式b3.<base58>、sha256.<base58>、sha512.<base58>、ed25519.<base58>、x25519.<base58>,base58 用比特币字母表,前导零字节编码为前导1。解析时校验长度:b3/sha256 为 32 字节、sha512 为 64、ed25519 公钥 32/签名 64、x25519 公钥 32。CLI、Web、日志、FFI 的 JSON 一律用文本形式。 - 算法注册表写在
docs/algorithms.zh.md(加英文版),hash、signature/public-key(两套枚举)、kdf、aead、chunking;每条有数值 ID、文本标识、状态 active/deprecated、deprecated_after。实现遇到未知或过期算法返回明确错误,不可降级。 - 仓库钉算法。
Genesis记录增加AlgorithmPolicy { hash, signature, kdf, repeated aead };应用任何记录前检查其算法在策略内,不符合拒绝;bootstrap 先核对 TrustAnchor 绑定的 Genesis,再检查同段算法,拒绝不落库候选钥或 frontier。运行中切换策略的记录类型只在 ADR 里定义,不实现。 - 格式号与能力通告。段和快照头的 magic 升为
CFSSEG02、CFSSNP03,去掉对 01/02 的解码;头后紧跟大端format: u32(当前 1);头中的公钥、签名、AEAD 号及 format 都被签名/AAD 认证。MemberRecord增加max_format: u32;入会原像绑定该值,已有本人 MemberUpdate 路径支持可选能力更新,值至少为 1。写入方取本机实际上限与全部未撤销成员最新通告的最小值;空成员集初始写 1。读到高 format 明确报错并停止应用该设备后续段,其他无依赖设备继续;未知 record 类型同样拒绝,不得过滤后推进 frontier。 - 本机数据库。metadata 与 CAS 数据库值改 protobuf;settings 存
schema: u32,只有明确新建库可初始化。打开时低 schema 逐级事务迁移、高 schema 报错,已有库缺失视为损坏;保持各表既有加密边界。VaultConfig改 protobuf 并加kdf算法字段。 - 对象名。哈希和设备 ID 改为文本形式:
i/ed25519.<b58>/<seq:016x>-b3.<b58>-<len>、p/<b58 前两字符>/b3.<b58>-<len>、n/ed25519.<b58>-b3.<b58>-<len>、u/b3.<b58>-<len>,v/<b58>、s/<b58>保留 namespace 内随机 16-byte ID,r/b3.<epoch-b58>-b3.<b58>-<len>标注派生身份。n/ 改为设备前缀时,frontier 原始 bytes/digest 继续在认证载荷内承诺和验证。解析时未知算法前缀的对象忽略并记日志,不可作为权限或 GC 删除依据。 - 测试向量。
crates/cipherfs-core/tests/vectors/放固定输入与期望字节、ID、签名的黄金文件,覆盖版本记录、段、快照、配置、每种文本形式。测试比对字节,不仅比对结构;包含未知字段的认证 body 经重开和快照后须逐字保留。
交付顺序
每步结束时 scripts/projects.sh test 工作区完整验证全部通过后单独提交,再进下一步;定向测试不能代替整套门槛。
- ADR 005(中英文):决策、备选方案、不实现的切换机制设计;ADR 004 标注"由 005 扩展到所有结构"。
- 算法注册表文档和
Digest/Signature/PublicKey类型及文本形式,先不改线上格式。 - proto 定义、生成器 crate、
scripts/projects.sh proto,提交生成代码。 - 被哈希/签名记录改为封装形式,
VersionId和各签名改为算 body 字节。 - 段与快照换 protobuf 载荷,新 magic 与 format 号,成员能力通告,写出格式取最小值。
- metadata/CAS redb 值、
VaultConfig、本机加密封装算法号、schema 与迁移框架。 - 对象名改造,目录存放点与 S3 存放点的解析、列举、GC 同步更新。
- 删除直接依赖及测试调用;移除未使用 async-session,将仅使用 net API 的 Iroh umbrella 换为已锁定
iroh-net 0.28.2,保留发现特征与协议,重新解析 lock,使间接依赖也不再出现 bincode。 - 测试向量;更新
requirements.zh.md(如有偏差)、IMPLEMENTATION_AUDIT.md(新增一节,附运行日志)、architecture、rust-api、各 crate README;TODO.md删除本项。
约束
- 宿主只跑
scripts/projects.sh check|clippy|format;完整测试用scripts/projects.sh test(容器)。不在宿主安装任何第三方工具、包或工具链。 - 私钥、库密钥、凭据和用户明文不进入日志、测试向量和文档;向量只含公开标识/签名/密文及合成元数据。
- 文档保持简短:README 只写当前行为,证据写进审计。文档中不出现其他项目名称。
- 每一步单独提交,提交信息说明改了哪一层。
验收
scripts/projects.sh test全部通过;scripts/projects.sh check和clippy无新警告。rg -n bincode crates/ Cargo.toml Cargo.lock无结果,含生成代码、集成测试和间接依赖。- 测试向量存在且通过;手动改任一向量的一个字节测试失败。
- 两台设备(容器内现有 P2P 测试夹具)一台把
max_format通告为 1、另一台支持 2 时,写出格式为 1;伪造 format 为 3 的段及该设备后续段不推进 frontier,其他无因果依赖设备的合法段继续应用;拒绝的 bootstrap 不落库候选钥。 - 审计新增小节列出上述运行证据。