Repository navigation
通道密钥:派生值定为唯一真理,共享目录里的 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 同步修正三处已被本次修复推翻的表述:
- 能力表「旧通道仍用旧密钥并显式告警」→ 「v0.31.1 起残留
channel.key也不再盖过派生值」 - 跨设备同步节 → 补「即使还有
channel.key也不生效;旧钥匙只作取回备用、永不用于发包」 - 故障对照表「程序故意继续用旧密钥」→ 「派生值已接管、同步不受影响」
第 3 条尤其要紧:那句话描述的正是被拆掉的旧行为,留着会让英文读者以为旧通道仍在用
旧钥匙,与实现正好相反。