Skip to content

v0.31.1 派生值定为唯一真理 —— 共享目录里的 channel.key 不再能盖过它

Latest

Choose a tag to compare

@jiabaobei jiabaobei released this 18 Oct 04:05
· 1 commit to main since this release

通道密钥:派生值定为唯一真理,共享目录里的 channel.key 不再能盖过它

真机事故(2026-10-12 → 10-18,同步停摆 6 天)

v0.31 已把密钥改成派生,却保留了 ensure_key() 的「通道目录里有 channel.key 就优先用它」。

云盘通道是多端共享的可写目录。一台未升级的 v0.30.x 设备发现 channel.key 不在,
就重新随机生成一把写回去(真机实测 2026-10-12 22:31 产出指纹 cc5d)——全通道随即被
这把没有任何包用过的钥匙劫持,outbox 里 33 个包集体「口令不匹配」。

用户看到的就是「明明修好了,怎么又犯」。

根因

v0.31 换掉了密钥的源头,但留下了密钥的覆盖开关。

派生的全部意义是「各端各算、逐字节相同、不需要传文件」;只要还允许共享目录里的一个
可变文件盖过它,这个意义就不成立。

改动

位置 改动
channel.ensure_key() 恒返回派生值,不再读通道目录里的文件(create 保留兼容;派生不落盘)
channel.candidate_keys()(新增) 解包时按「派生值 → channel.key → channel.key.legacy」依次尝试
FolderTransport.fetch() 未给口令走候选序列;显式 --passphrase 仍为严格单钥
CLI 取回侧 不再把通道密钥当显式口令传入(否则绕过序列,修复形同虚设)
错误提示 删掉指向 show-passphrase 的误导引导

旧钥匙永不用于发包,只用来解开未升级客户端封的包——让它们的包能解开,比让整条
通道卡住有用。

验证

  • 真机:把滞留 6 天、解不开任何包的 channel.key(指纹 cc5d)移出通道目录留档为
    channel.key.stale-20261012 后,membridge sync 取回 111 条(恰为 10-09 那三个用
    旧钥匙封的包 99+9+3,证明回落序列生效),记忆库 625 → 736;再跑一次取回 0 条,幂等。
  • 复发模拟:把毒密钥放回去,membridge channel 指纹恒为 d1c4(派生值)、显式告警
    但不再接管;sync 照常工作。修复前此场景必然全线失败。
  • 测试:新增 test_stale_channel_key_file_never_overrides_derived(守卫「文件不得盖过
    派生值,但旧钥匙仍须保留为备用」)与 test_fetch_still_reads_packages_sealed_with_stale_key
    (端到端「旧钥匙封的包必须仍能取回」)。原
    test_old_channel_key_file_still_wins_and_reports_mismatch 断言的正是这个 bug 的行为,
    已由前者取代。
  • 回归:全量测试与干净基线(git worktree 拉 HEAD)逐条比对 新增回归 0
    (152/172 vs 151/171;20 个失败两版完全一致,均为环境类:rclone / OneDrive / 坚果云 /
    schtasks 受沙箱限制)。

升级须知

已升级的设备无需任何操作。通道里若仍有 v0.30.x 设备,它们写的 channel.key 现在只会
产生一条告警,不再影响同步——确认那台设备也升级后,可把 channel.key 从通道目录移除。

文档同步(中英双档)

README / README_EN 同步修正三处已被本次修复推翻的表述:

  1. 能力表「旧通道仍用旧密钥并显式告警」→ 「v0.31.1 起残留 channel.key 也不再盖过派生值」
  2. 跨设备同步节 → 补「即使还有 channel.key 也不生效;旧钥匙只作取回备用、永不用于发包」
  3. 故障对照表「程序故意继续用旧密钥」→ 「派生值已接管、同步不受影响」

第 3 条尤其要紧:那句话描述的正是被拆掉的旧行为,留着会让英文读者以为旧通道仍在用
旧钥匙,与实现正好相反。