Files
dsh_ai1net_server/归档/交接单-20260924-归档/交接单_组密钥加密_20260918.md
T
admin 7bd1151c67 chore(工作区): 归档交接单 27 件 + 新增 CODEBUDDY §9 工作区卫生
交接单按归属约定(正文落文档库、工作区只放指针)归档至
归档/交接单-20260924-归档/,含逐件判定 README:
- 16 件已被文档库正式版取代(T09–T21 + 覆盖网络-24/25/26)
- 4 件主题已被覆盖网络线入口汇总
- 7 件历史接续包/规划件

CODEBUDDY §9:收口清本棒 tmp、tmp 保留期 7 天、禁「待清理」中间态、
工作区入库只放文档与文件、不保留脚本副本、>60 KB 单文件须逐个判。
2026-09-24 07:58:57 +08:00

69 KiB
Raw Blame History

交接单 · 组密钥加密(块级共享与机密性兼得)

  • 序号:覆盖网络线 序 ㉘ · 规划棒 → 单 B(执行棒待跑)
  • 立单:2026-09-18 00:5x
  • 用户拍板:「选 C 组密钥加密」(2026-09-17 23:2x,原话;⚠️ 该档位在 23:3x 的更正轮里明确未变)
  • 上游依据:入口 §4 用户口径「按照连接稳定高效的方式 数据安全可加密传输」|交接单_骨干稳定选路与加密_20260917.md(§4 加密属真取舍、已登记待评估)|交接单_内容分发块级寻址_20260917.md(序 ㉔ 块级内容寻址已落地)
  • 归档号:130(占号动作已执行,见 §11;⛔ 63 是空号、不补占)
  • 状态:待执行

本单与 交接单/README.md §二 8 段模板的对应:§1 目标|§2 只读前置|§3 范围|§4 步骤|§5 验收(模板 6)|§6 回滚(模板 7)|§7 决策点 + 影响面|§8 回报格式(留空给执行棒)|§9 回头条件|§10 未验证项|§11 指纹与状态。


§0 摘要

0.1 摘要(一句话)

给内容面加一层"组密钥"对称加密:同一个「内容组」共享一把对称密钥 ⇒ 组内密文逐字节一致 ⇒ 序 ㉔ 的「按哈希共享块」仍然成立(去重与同组 peer 命中不退化),同时中继进程读不到明文载荷。⇒ 相对「按接收者 E2E」(牺牲性能)与「保持现状」(牺牲对中继的机密性),本方案两者兼得。

0.2 🔴 本棒一条必须纠正的前人前提(取证得出,⛔ 请以本棒为准)

「块 id 用明文哈希 ⇒ 跨组也能共享同一块」 —— 这句在上游讨论里被当作明文哈希的主要优点。本棒取证判定:在本方案(组密钥加密)下它不成立。

理由(两条,均为可读源码事实):

  1. 序 ㉔ 的跨组共享今天本就是被显式拒绝的 —— src/net/relay/content/peer.ts:64-72 的 sameGroup() 是"跨组"的唯一判据处,配套计数器 crossGroupDenied;验收 E5 的口径就是"跨组 0 穿透且 crossGroupDenied ≥ 1"(参数表 §6 OBS-17)。⇒ 明文哈希拿不到一个今天不存在的收益。
  2. 🔑 加密之后,"共享密文"这件事本身失去意义 —— 组密钥是组作用域的 ⇒ 组 B 即便取到组 A 的密文块,没有组 A 的密钥就解不开(强行用组 B 密钥解 ⇒ 校验失败)。⇒ 所谓"跨组共享同一块"只共享了一堆解不开的字节。

⇒ 结论:明文哈希的"跨组共享"优点在本方案下净收益 ≈ 0,而它的缺点(中继可对已知明文做存在性确认)仍在 ⇒ 本单推荐"密钥相关/密文相关哈希"(见 §7.3,⛔ 该处是真取舍,已给逐项优缺点与推荐理由)。


§1 目标

内容面(src/net/relay/content/** 及其装配点)新增组密钥加密层,满足三条:

  1. 组内密文一致:同一组、同一明文块,任意节点加密后密文逐字节相同(md5 相等);
  2. 共享不退化:同组内第二次取块命中 local 或 peer 档(⛔ 不许退化成"次次回源");
  3. 中继看不到明文载荷:relay 进程持有的任何持久/日志//status 里不含明文块字节,且不持有可解密的密钥。

可判定的"做完了没有":§5 的 F1–F6 全绿 —— 特别是 F1(同组密文一致)+ F2(命中 local/peer)+ F3(跨组不可解)+ F4(明文不出现,且反向夹具证明 F4 有效**)+ F5(E1 主判据不退化)**。


§2 只读前置(执行前必须先核实,逐条给命令与期望输出)

全部只读;⛔ 不改任何文件、不改任何生产值。任一条与期望不符 ⇒ 停下报告。

# 核实内容 命令 期望输出
1 组定义现状(能否复用,⛔ 不新造) cd D:/github/dsh_shenxian && grep -n -A3 "export function groupKeyOf" src/net/relay/content/peer.ts; grep -n "CONTENT_GROUP|DSHS_CONTENT_GROUP" src/web/server.ts src/net/relay/main.ts 组键 = <network>|<group>(分隔符 |,:61)|平台侧缺省 'local'(web/server.ts)、relay 侧 'relay'(main.ts)—— 刻意分开(防"两个进程误以为同组")。⇒ "组" = (网, 组) 二元组,与序 ㉔ 同构 ⇒ 直接复用。
2 块 id 算法现状(本单取舍的起点) grep -n -A3 "export function blockIdOf" src/net/relay/content/chunker.ts sha256(字节).slice(0, 32) = 对明文哈希(⚠️ 若已变成密钥相关 ⇒ 本单已被别人做过 ⇒ 停下报告)。
3 现有加密面(本线尚无内容层加密) grep -rniE "encrypt|cipher|gcm|aes|chacha" src/net/relay/ --include=*.ts | wc -l 0(本棒实测)。⇒ 加密层是纯新增,⛔ 不改既有行为。
4 密钥模型(四层,⛔ 不许引第二套) sed -n '14,30p' src/net/relay/identity.ts; grep -n "asymmetricKeyType !== 'ed25519'" src/net/relay/identity.ts; ls -l /etc/dshs/overlay-signer-key.pem(47) 四层 = 根(离线,只授权/撤销签名者)→ 签名者(在线,47)→ 节点密钥(每机一把 0600)→ 会话(内存)|🔴 节点密钥 = ed25519(identity.ts:197/318,纯签名、不能做公钥加密)⇒ 这是 §7.2 分发方案的决定性约束|签名者文件在 47 上存在。
5 既有对称封装(实现底座,⛔ 不新造算法栈) sed -n '1,10p' src/crypto.ts AES-256-GCM + deriveKey(secret) = sha256(secret)(用于用户凭据 at-rest)。⇒ 本单的算法栈照此复用(同为 AES-256-GCM)。
6 中继是否能看到块 id 集合(决定 §7.3 取舍有无现实意义) grep -n "PEER_COUNTER_KEYS|declarations|withdrawn" src/net/relay/content/peer.ts; sed -n '200,215p' src/net/relay/main.ts 计数键含 declarations / withdrawn ⇒ 声明走 relay 消息 ⇒ 🔴 relay 进程持有 ContentPeerGroup 注册表,能看到"哪些块 id 存在"。 ⇒ §7.3 的取舍有现实意义(不是理论问题)。
7 基线三件套 + 参数表指纹 cd D:/github/dsh_shenxian && npm.cmd test 2>&1 | tail -5;node scripts/overlay-probe.cjs --table;sed '/^## §10 指纹/,$d' "E:/ProgramData/AI技能/aliyun-dsh-server/参数表_覆盖网络_20260917.md" | md5sum npm test = 201 pass / 200 / 0 fail / 1 skip|overlay-probe --table = 19 PASS / 1 SKIP / 1 FAIL(1 FAIL = OBS-21,线内在册缺口、⛔ 非本单引入)|参数表指纹 = 7fc5889341b99fb26bd05cad0313960b(本棒 00:4x 现核)。⛔ 判据一律用现核值,本单写的是快照。
8 OBS 编号现核(防撞号) grep -oE 'OBS-[0-9]+' scripts/overlay-probe.cjs | sort -u | tail -1 本棒实测 = OBS-21 ⇒ 本单预留 OBS-23(OBS-22 已分配给单 A);⚠️ 若执行时已被占用 ⇒ 顺延(序 ㉖ 踩过编号被前序占用)。

§3 范围

3.1 在册文件集

面 文件 说明
新增 src/net/relay/content/crypto.ts 组密钥装载(0600 文件)+ 加解密 + 组内确定性(收敛)加密 + epoch 管理
改动 src/net/relay/content/chunker.ts 块 id 口径(⛔ 仅在 §7.3 选定 β 时需要;选 α 则一行不改)
改动 src/net/relay/content/store.ts put/get 接入密文口径 + 新增 decryptRejected 计数
改动 src/net/relay/content/source.ts 五档链路的取回结果统一解密 + 失败留痕
改动 src/net/relay/content/peer.ts 同组判定与 epoch 一致性(⛔ 仍只认 sameGroup,不放松跨组)
改动 src/net/relay/content/runtime.ts 装配组密钥(缺省不启用 ⇒ 行为不变)
改动 src/net/relay/main.ts relay 侧装配(第 5 条 contentRuntime,:208)
改动 src/web/server.ts 平台侧装配(contentSource/contentPeers 一带)
改动 scripts/overlay-probe.cjs 新增 OBS-23
改动 test/overlay-content.test.mjs 新增 F 组用例(⛔ 不改 package.json test 列表结构)
改动 参数表_覆盖网络_20260917.md §3.6 新增加密键 + §6 新增 OBS-23
改动 scripts/overlay-keyring.cjs 扩展"组密钥"签发/验签子命令(复用既有仪式 CLI)

3.2 ⛔ 不动什么(防顺手扩大)

  • ⛔ 不引入第二套密钥体系 —— 不新根、不新签名链;组密钥的授权方仍是既有签名者(见 §7.2)。
  • ⛔ 不动 wire.ts 的帧格式与既有消息号(新增消息须另立单);⛔ 不动 keys.ts / identity.ts 的既有语义(只扩展)。
  • ⛔ 不动 RELAY_FAILOVER_* / HB_SEC / burst / switcher.ts 的冷却语义。
  • ⛔ 不放松跨组 —— crossGroupDenied 必须保持"跨组 0 穿透"(E5 不得退化)。
  • ⛔ 不改加密传输层(TLS 由 nginx 终结,已是现状)—— 本单只做内容载荷层。
  • ⛔ 不新增公网监听口、不改 nft / nginx(R5)。
  • ⛔ 不改任何生产值(D1);⛔ 不 commit / 不 push(R7)。
  • ⛔ 不许顺手做:房间层 / 打洞实现(本线明令不做)。

§4 步骤(每步自带一次可执行的验证)

三条硬门贯穿全程:D1|R5|R7。

S0 · 开工对账

  • 跑 §2 全部 8 条并原样留档。不符 ⇒ 停下报告。
  • 抢锁:bash "D:/github/dsh_shenxian/dsh-server-docs/scripts/handoff-guard.sh" --claim-exec "覆盖网络线-序28单B执行棒";抢不到 ⇒ 只报告并停(R9)。
  • 验证:guard 返回「✓ 已持全局执行锁」。

S1 · 组密钥的定义与装载(复用四层模型,⛔ 零新信任根)

  • 密钥形状:groupKey = 32 字节随机对称密钥(与 src/crypto.ts 的 AES-256-GCM 口径一致);随密钥记录 epoch(单调递增整数)。
  • 装载:src/net/relay/content/crypto.ts 从一个 0600 文件读 { group, epoch, key(base64) };文件缺失 / 权限不对 / 组名不匹配 ⇒ 一律"不启用加密"并记一行判别器日志(⛔ 不静默降级成"以为加密了")。
  • 授权:由既有签名者(47,/etc/dshs/overlay-signer-key.pem)用既有签名载荷签发 (group, epoch, keyId) 三元组(⛔ 载荷内不含密钥本体);节点用既有验签链校验 ⇒ 认下当前 epoch。
  • 验证:node scripts/overlay-keyring.cjs <子命令> 生成的凭据能被既有 directory.ts/identity.ts 的验签路径接受(rc=0);篡改 epoch ⇒ 验签失败(失败关闭)。

S2 · 组内确定性(收敛)加密 —— 本单的技术核心

  • 目标:同组 + 同明文 ⇒ 同密文(否则"按哈希共享块"直接失效)。
  • 做法(三行即可说清):k_block = HKDF/HMAC(组密钥, 明文块) 派生块级密钥;IV = HMAC(组密钥, 明文块) 的前 12 字节(确定性、不留随机数);密文 = AES-256-GCM(k_block, IV, 明文);认证标签一并存(篡改即读侧丢弃)。
  • ⚠️ 必须同时落 AAD:把 (group, epoch) 绑进 AAD ⇒ 跨 epoch / 跨组的密文在认证阶段就被拒(⛔ 不靠"解出来是乱码"来判)。
  • 验证(先红后绿):对同一明文加密 2 次 ⇒ 密文 md5 相等;换 epoch ⇒ 密文不等(且旧 epoch 密文用新密钥解 ⇒ 认证失败)。

S3 · 块 id 口径(⛔ §7.3 选定的那一项)

  • 推荐项(β':密文哈希):blockId = sha256(密文).slice(0,32) —— 因为 S2 已保证组内密文一致 ⇒ 组内 id 稳定、组外 id 不可预测。
  • 验证(先红后绿):同组两次独立"切块 + 加密" ⇒ 块 id 序列逐字一致;换组密钥 ⇒ 全部 id 改变(这是"轮换 ⇒ 全量回源"的直接证据,必须留档,不得粉饰)。

S4 · store / source / peer 接入

  • store.put/id/get 全程只处理密文(读回后 S2 解密 + 认证);get 认证失败 ⇒ 丢弃 + decryptRejected += 1(⛔ 不静默返空)。
  • source.ts 五档链路的解密位置统一(⛔ 不许"local 档不解密、peer 档解密"这种双口径)。
  • peer.ts:仍只认 sameGroup;新增 epoch 一致性:声明 (group, epoch),epoch 不一致不视为同组可用(⇒ 轮换过渡期由 S5 处理)。
  • 验证:test/overlay-content.test.mjs 原 27 例不退化;新增 F 组用例(§5)。

S5 · 轮换与过渡态

  • 新成员获取:见 §7.2(密钥本体不经 relay)。
  • 成员退出 / 例行轮换:签名者签新 epoch ⇒ 节点加载新密钥(旧 epoch 保留为"仅解不写")⇒ 过渡窗口内新旧密文都能解;写入一律用新 epoch。
  • 过渡窗口必须上界:参数化(建议 CONTENT_EPOCH_GRACE_MS,⚠️ 值格 ⛔ 必须纯数字,不得夹注 —— 夹注会被读成 NaN ⇒ 假红)。
  • 验证:新旧 epoch 混存时,两种密文都能解;超窗口后旧 epoch 密文 ⇒ decryptRejected += 1 且点名。

S6 · 判据(OBS-23 + 夹具 + 先红后绿)

  • 夹具四段(照序 ㉔ --content-fixture 的做法):OFF(无加密键)/ON(同组)/CROSS(跨组)/ROT(双 epoch)。
  • OBS-23 三判据(见 §5):① 加解密计数键齐全(⛔ 缺一即 FAIL)② 同组密文一致 + 命中 local/peer ③ 明文不出现(对已知明文标记做字节级断言)。
  • 真机先红:新 OBS-23 对旧生产 ⇒ FAIL(并打印可区分证据)。
  • 验证:四段夹具的判定与预期逐段一致,且 OFF/CROSS/ROT 三段的 FAIL/PASS 互不混淆。

S7 · 部署 + 零回归 + 收口

  • npm run build ⇒ 铺到四处(47 /opt/dshs/lib + /opt/dsh-relay/lib、106 /opt/dshs-cluster/lib + /opt/dsh-relay/lib)—— 🔴 relay 类产物必须两边都铺(序 ㉖ 踩过"dshs 侧落后一个版本")。
  • 部署前建回滚点 /opt/dsh/backups/seq28-<ts>/(旧 md5 记录在 §8)。
  • ⚠️ 密钥文件与 env 的铺发属运维动作:落地前先确认密钥文件权限 = 0600、属主正确(⚠️ 本线史上有"属主被改 ⇒ 实例全崩"的先例)。
  • 重启 dshs-relay(两台)+ dshs(47)+ dshs-worker(106)(R8:动手前一句话说明)。
  • 零回归:npm.cmd test|--scene all --table = 12 PASS / 0 SKIP / 0 FAIL|--table = 基线 +1 行(OBS-21 仍 FAIL = 线内在册缺口)。
  • 回填 §8;推进 接续入口_覆盖网络线_20260916.md §0 + §2;写工作区日志(append-only)。

§5 验收判据(可机器断言、可被第三方复现)

# 判据 命令 / 口径 期望 为什么必须有它
F1 同组密文一致(本单技术核心) 夹具 ON:同一明文块独立加密 2 次 ⇒ md5 比对 相等 不等 ⇒ "按哈希共享块"直接失效(序 ㉔ 的 E1 崩)
F2 共享不退化 夹具 ON:第一次取块后第二次取块 ⇒ 两次 /status 的 content.store.hits / content.peer.peerHits 增量 ≥ 1 命中 local 或 peer 只判 F1 不够 —— 加密对了但"次次回源"会全绿(这正是序 ㉔ OBS-17 判据③ 存在的理由)
F3 跨组不可解 夹具 CROSS:组 B 用自己密钥解组 A 的密文 认证失败 + decryptRejected +1 + crossGroupDenied 不减少 保证"加密 ≠ 放松隔离";E5(跨组 0 穿透)不得退化
F4 🔴 明文不出现(+反向证明 F4 有效) ① 用唯一标记串写一个块 ⇒ 在 relay 进程可见面(持久存储 / journalctl / /status)做字节级搜索 ⇒ 命中 0;② 反向夹具:关闭加密(OFF 段)⇒ 同一搜索 必须命中 ≥ 1 ① 0 ② ≥ 1 🔴 只判 ① 是假绿风险 —— "没测到"和"真没有"必须可分(本线两处静默失效都栽在这里)。⇒ ② 是 ① 的有效性证明,不可省
F5 E1 主判据不退化 序 ㉔ 原口径:N=4 同组回源字节数 ≤ 1 份 × 组数(序 ㉔ 收口值 = 1.0000×,⛔ 不许变成 4.00×) 加密的最大风险就是"把块级共享打回去"
F6 失败关闭(⛔ 不静默降级) 密钥缺失 / 权限错 / 组名不配 / epoch 超窗口 四种情形各跑一次 每种 都有具名判别器留痕 + 计数 +1;⛔ 不许出现"以为加密了其实没加" 本线反复复发的那类病("配错了"伪装成"不通")
F7 零回归 npm.cmd test|--scene all --table|--table 不退化(§2-7 现核基线 + 探针 +1 行) —

OBS-23 的形状(进 scripts/overlay-probe.cjs;编号须现核顺延)

  • 三判据:① content.crypto 计数键齐全且皆 number(⛔ 缺一即 FAIL —— 这正是"静默放行"的机器判据)② F1 + F2 ③ F4①(明文不出现)。
  • ⛔ 缺行即 FAIL,并把 journalctl 的原文字节数一并打出(0 字节 = 读取失败/>0 = 真没有该行,二者必须可分)。
  • ⚠️ 值格必须是纯数字(参数表规则)—— 加密相关键里凡"模式名"性质的一律只作信息键,⛔ 不进数值判据。

§6 回滚

回滚 = 关掉加密层(缺省即关闭)⇒ 秒级,且不丢数据。

路 A · 配置回滚(推荐,秒级)

  • 组密钥文件移除 / 改名(或把启用开关置回缺省)⇒ crypto.ts 走"不启用"分支 ⇒ 行为逐字回到序 ㉔ 现状。
ssh -p 22 bt-server 'mv /etc/dshs/content-group-key.json /etc/dshs/content-group-key.json.off && systemctl restart dshs-relay dshs'
ssh -p 22 test106  'mv /etc/dshs/content-group-key.json /etc/dshs/content-group-key.json.off && systemctl restart dshs-relay dshs-worker'
  • 验收:overlay-probe --table 的 OBS-23 从 PASS → SKIP 或 FAIL-并点名"未启用";npm test 不退化。⚠️ OBS-23 的"未启用"必须是 SKIP 还是 FAIL 要在 S6 就定死(建议 SKIP + 留痕,因为"缺省不启用"是合法状态;⛔ 但不许既不是 SKIP 也不是 FAIL 的"静默绿")。
  • ⚠️ 已有的密文块会解不开 ⇒ 回滚后这些块按"认证失败"被丢弃(decryptRejected +1)⇒ 清一次 CONTENT_STORE_MAX_BYTES 的缓存目录即可(属可逆操作:块能从 origin 重取)。

路 B · 产物回滚(秒级)

ssh -p 22 bt-server 'cp /opt/dsh/backups/seq28-<ts>/*.js /opt/dshs/lib/net/relay/content/ && cp /opt/dsh/backups/seq28-<ts>/relay/*.js /opt/dsh-relay/lib/net/relay/content/ && systemctl restart dshs-relay dshs'
  • 回滚点:/opt/dsh/backups/seq28-<ts>/(S7 部署前创建,旧 md5 记入 §8)。⛔ 没有回滚点不准上线。

🔴 回滚路径硬禁令

⛔ 不许把 RELAY_FAILOVER_COOLDOWN_MS=0 写进任何回滚 / 演练 / 夹具路径 —— 它会自锁(实测 121–123 s 无切换);判定见 §9-1。


§7 决策点 · 取舍 · 影响面

7.1 决策点

  • 已定(用户):加密档位 = C 组密钥加密(2026-09-17 23:2x,23:3x 未变)。
  • 已定(本棒,可推翻):
    • 「组」定义 = 复用序 ㉔ 的 (network, group)(依据 §2-1,peer.ts:61),⛔ 不新造。
    • 算法栈 = AES-256-GCM(依据 §2-5,与既有 src/crypto.ts 同源),⛔ 不引 ChaCha 等第二栈。
    • 探针编号 = OBS-23(OBS-22 归单 A;执行时现核顺延)。
  • 待定 = 空。⛔ 本单无上抛项。

7.2 密钥从哪来(复用四层模型,⛔ 零新信任根)

复用/扩展的对应关系(照 identity.ts:14-30 的四层表逐层落位):

四层原本 本单如何复用/扩展
根(离线) 不动 —— 它只授权/撤销"签名者",⛔ 不签发组密钥(守住既有用途边界)
签名者(在线,47) 扩展职责:原"签发节点入网凭据" ⇒ 追加"签发组密钥凭据 (group, epoch, keyId)"。同一把钥匙、同一条签名链、同一个验签入口。
节点密钥(每机一把) 不动(仍是 ed25519,仍只做签名/身份)。🔴 注意:ed25519 不能做公钥加密(identity.ts:197/318 硬校验)⇒ 见下方"分发"
会话(内存) 扩展:原本只标"隧道传输加密" ⇒ 追加"内容组加密"这一档:组密钥(落盘 0600 + 内存)

分发(本阶段):

  • ✅ 密钥本体不经网络、不经 relay —— 由运维按既有 0600 惯例落文件(与节点密钥同处 /etc/dshs/),链路 = 既有部署通道(scp / drop-in)。理由:① 节点密钥是 ed25519,做不了公钥包裹;② 任何"经 relay 传密钥"的做法都等于把密钥交给中继,前功尽弃。
  • ✅ 签名者只广播 (group, epoch, keyId) 三元组(⛔ 不含密钥),节点据此确认"当前 epoch 是几",防"版本错配"。
  • ⚠️ 规模化的自动分发 ⇒ 登记为回头条件(§9-7):届时需给节点密钥并行加一把 x25519(或做 Ed25519→X25519 派生)才能上"公钥包裹"。⛔ 本单不做(那是新增密钥类型,属另立单)。

7.3 🔴 本单最关键的取舍:块 id 用明文哈希还是密钥相关哈希

两者竖排成段,逐项列「优点 / 缺点」。⛔ 不横排、⛔ 不做成表格的列。

选项 α —— 块 id = 明文哈希(sha256(明文块),⛔ 序 ㉔ 现状一行不改)

优点:

  • 序 ㉔ 的 chunker.ts / OBS-17 / 既有 E1 判据零改动,改动面最小;
  • 轮换组密钥不改变块 id ⇒ 轮换不触发全量回源(这是它唯一真实的、且不小的优点)。

缺点:

  • 🔴 它承诺的"跨组共享同一块"在本方案下不成立(§0.2 已取证:组外没有组密钥 ⇒ 拿到密文也解不开;且 E5 本就要求跨组 0 穿透)⇒ 该"优点"净收益 ≈ 0;
  • 🔴 把"哪些块存在 + 内容指纹"暴露给中继:relay 进程持有 ContentPeerGroup 注册表(§2-6 实测)⇒ 中继可用已知明文对 id 做存在性确认("这个包在这张网里存在吗")⇒ 与用户"数据安全"的口径直接相悖;
  • 明文 id ↔ 密文存储还要额外维护一张映射 ⇒ 中继侧多一张可被探测的索引表(更糟)。

选项 β' —— 块 id = 密钥相关/密文相关哈希(sha256(密文),因 S2 已保证组内密文一致)

优点:

  • 🔴 组外不可见:中继只看到不透明 id,连"是否存在该块"都判不出来(这正是"数据安全"要的那一半);
  • 组内完全兼容:S2 的确定性加密已保证"同组 + 同明文 ⇒ 同密文" ⇒ 组内 id 稳定 ⇒ 按哈希共享块仍然成立(完全满足用户选 C 档位的原意);
  • 与 E5(跨组 0 穿透)天然一致 —— 它不损失任何今天存在的收益。

缺点:

  • 🔴 轮换组密钥 ⇒ 全部块 id 改变 ⇒ 一组一次全量回源(正面撞序 ㉔ 主判据 E1);
  • 签名目录 / 索引必须携带 epoch,否则新旧 id 无法共存(⇒ S5 的双 epoch 过渡态不可省);
  • 组外永久失去块级复用(⚠️ 但见 §0.2:这在加密之后本就无意义)。

我的推荐 = β'(密钥相关/密文相关哈希),理由三条:

  1. 用户要的是"中继看不到明文载荷" —— α 虽然也加密了载荷,但仍把块存在性与内容指纹交给中继,属"做了一半";
  2. α 的"跨组共享"优点在本方案下已被取证判定为不成立(§0.2)⇒ α 剩下的只有缺点 ⇒ 按判据("只有缺点的候选自己拍掉")不选 α;
  3. β' 是唯一同时满足"组内共享成立 + 组外不可见"的选项,且与既有 E5 语义同向。 ⚠️ β' 的代价必须如实接受并写进参数:轮换 = 一次全量回源 ⇒ 轮换频率要有下限(参数化)+ 且优先安排在版本发布窗口(那时本来就要重灌)⇒ §9-8 回头条件。

7.4 影响面与关系

  • 影响面:内容面(首屏包分发) —— 只影响 local/peer/edge/region/origin 五档链路的字节形态与块 id;⛔ 不影响实例生命周期、路由、切流、dsh_hosts、lease、端口分配(与单 A 完全无交集,两单可并行但不共用文件)。
  • 与 presence / room 层:无交集。
  • 与 E1:见 F5;β' 下轮换窗口是唯一会让 E1 短暂退化的时刻。

§8 回报格式(留空给执行棒)

逐节回填,每节必须给命令 + 原始输出(或原文摘录)+ 判定。⛔ 不许只写结论。

本棒身份:覆盖网络线-单B执行棒-组密钥加密(automation 65d2fc24-770e-4f33-93a3-db6e739aded7)· 一次性、无人值守。 时间:开工 2026-09-18 06:22(抢锁成功)→ 收口 2026-09-18 07:2x。 证据落盘:全部原始输出在 E:\ProgramData\AI技能\aliyun-dsh-server\_tmp_seq31\(下文各节标注文件名)。

8.1 开工对账(§2 八条)

锁:bash D:/github/dsh_shenxian/dsh-server-docs/scripts/handoff-guard.sh --claim-exec "覆盖网络线-单B执行棒-组密钥加密" ⇒ ✓ 已持全局执行锁(06:22,一次抢到,⛔ 无等待重试)。

# 核实内容 实测(原文摘录) 判定
1 组定义现状 组键 = <network>|<group>(peer.ts#groupKeyOf);平台侧缺省 'local'(web/server.ts)、relay 侧 'relay'(main.ts) ✅ 与期望一致 ⇒ 组 = (network, group) 二元组,直接复用(⛔ 未新造)
2 块 id 算法现状 blockIdOf = sha256(字节).slice(0,32),对明文 ✅ 开工时为 α(明文哈希) ⇒ 本单未被别人做过,可继续
3 现有加密面 grep -rniE "encrypt|cipher|gcm|aes|chacha" src/net/relay/ --include=*.ts | wc -l = 0 ✅ 加密层纯新增
4 密钥模型 四层:根(离线)→ 签名者(47 /etc/dshs/overlay-signer-key.pem,0600)→ 节点密钥(每机 0600)→ 会话内存;节点密钥 = ed25519(identity.ts:197/318) ✅ 与期望一致;🔴 ed25519 纯签名、不能公钥加密 ⇒ 决定"密钥本体只能走文件/scp"
5 既有对称封装 src/crypto.ts = AES-256-GCM + deriveKey = sha256(secret) ✅ 本单算法栈照此复用(⛔ 未引第二栈)
6 中继能看到块 id 集合 peer.ts 计数键含 declarations / withdrawn;relay 进程持 ContentPeerGroup 注册表 ✅ ⇒ §7.3 的取舍有现实意义
7 基线三件套 + 参数表指纹 npm.cmd test ⇒ 201 tests / 200 pass / 0 fail / 1 skip(与单内基线逐字同);overlay-failover-drill --scene all ⇒ 12 PASS / 0 SKIP / 0 FAIL;overlay-probe --table ⇒ ⚠️ 开工那一刻演练正在跑(relay 被反复起停)⇒ 18 PASS / 1 SKIP / 3 FAIL(红 = OBS-01/OBS-04/OBS-08,演练暂态);参数表 §10 指纹现核 = 3b295a8c43aafc4a62c6f1a59ea0b43a ⚠️ 探针开工值不是单内快照的 19P/1S/1F —— 原因是"序㉙ 已把 OBS-21 转绿(22P 稳态)"+"开工时刻撞上演练暂态(3 红)";收口前的稳态基线 = 22 PASS / 0 SKIP / 0 FAIL(S7-probe-1.txt 实测)⇒ 本棒目标 = 23P/0S/0F
8 OBS 编号现核 grep -oE 'OBS-[0-9]+' scripts/overlay-probe.cjs | sort -u | tail -1 = OBS-22 ✅ OBS-23 未被占用,按单内预留使用(⛔ 未顺延)

判定:八条全部相符 ⇒ 未触发"停下报告";三条硬门(D1 / R5 / R7)自开工起全程有效(见 8.11)。


8.2 组密钥凭据(签名者签发 + 验签通过 + 篡改必失败)

① 生成组密钥(本机) —— node scripts/overlay-keyring.cjs init-group-key --group relay --epoch 1 --out _tmp_seq31/content-group-key.json(init-group-key.txt):

✓ 组密钥文件:E:/ProgramData/AI技能/aliyun-dsh-server/_tmp_seq31/content-group-key.json(0600)
✓ group=relay epoch=1 keyId=3560130eb0e5e4aa(⚠️ 密钥本体只在文件里,⛔ stdout 不打印)
  • 文件六键 = version / network / group / epoch / key(44 B base64 = 32 B) / previous([]);keyId = sha256(key) 前 16 hex。
  • ✅ stdout 只出 keyId(⛔ 无密钥本体、⛔ 无 base64 片段)。

② 签名者签发三元组(47,既有签名者) —— 命令口径 sign-group-key --signer-key /etc/dshs/overlay-signer-key.pem --network ops --group relay --epoch 1。凭据原文(group-key-credential.json,274 B):

{ "doc": { "version":1, "network":"ops", "group":"relay", "epoch":1,
           "keyId":"3560130eb0e5e4aa", "issuedAt":"2026-09-17T22:54:40.439Z" },
  "sig": "I2KOPpUMntkR1cNuXpR0zpUIayVeLRuEtPjRxym71Dh6vlkHB9vadDQPfVrMH+6bgX6fC8ipekdSuRjm76VtAg==" }
  • 🔴 载荷内不含密钥本体(逐字核):凭据含 "key" 字段 = False、凭据含密钥片段(key 前16字符) = False ⇒ 三元组就是三元组。
  • 公钥 / keyId:签名者公钥 = bad464dfd53048efe7b8531029b3030eda49bc12930703d3a2a8f60e3a7daddf(47 /etc/dshs/overlay-signer-key.pem.pub)⇒ 与既有信任表 /etc/dshs/overlay-signers.json 的 signers[0] 逐字一致(⛔ 未新根、⛔ 未新信任链);组 keyId = 3560130eb0e5e4aa。

③ 验签(S8-8.2.txt):

### 正例(原文凭据)
✓ 验签通过:network=ops group=relay epoch=1 keyId=3560130eb0e5e4aa      rc=0
### 篡改例(epoch 1→2,sig 不变)
✗ 验签失败:signature-mismatch                                            rc=1
### 交叉核对失败例(epoch 传 9)
✗ ⛔ epoch 不配:凭据 epoch=1 ≠ 传入 9                                     rc=1
  • 判定:✅ 签发 ⇒ 验签通过;✅ 篡改 epoch 必失败(失败关闭);✅ 凭据不载荷密钥本体。

8.3 确定性加密(同明文两次 ⇒ 密文 md5 相等;换 epoch ⇒ 不等且旧密文认证失败)

F1(S7-f-evidence.txt):

明文 md5     = 24d6ed6ce80d7311db80fc63c67bf49a  (3776 B)
密文#1 md5   = 3385e9d00ae5c5dbb880ddad90391756  (3804 B)
密文#2 md5   = 3385e9d00ae5c5dbb880ddad90391756  (3804 B)
⇒ 逐字节相同 = true   | 长度 = 明文 + 28(iv 12 + tag 16)
⇒ keyId = 5ed7f6b62a053af1(夹具口径)| groupKey = ops|relay epochs=1
  • 派生口径(实现):iv = HMAC(sha256,key,"iv"‖plain)[:12]、k = HMAC(sha256,key,"k"‖iv)、AAD = "<network>|<group>|<epoch>"(⇒ 解密侧只需 iv 即可复原 k,故确定性成立)。

F1-b(换 epoch):

epoch=2/keyB 密文 md5 = 3cab76cda498790fa4ba1329b3f0f53a  ⇒ 与 epoch=1 不同 = true
用 epoch=2 解 epoch=1 的密文 ⇒ undefined(认证失败)|该 cipher 计数 decrypts=0 decryptRejected=1
用本 epoch 解自己写的 ⇒ 还原一致 = true |decrypts=1
  • 判定:✅ 同组 + 同明文 ⇒ 密文逐字节相同("按哈希共享块"的前提成立);✅ 换 epoch ⇒ 不等 + 旧密文在认证阶段被拒(⛔ 不是"解出乱码")。

8.4 块 id 口径 + 轮换代价的原始读数(⛔ 不得粉饰)

β′ 口径(采单内推荐项):blockId = sha256(密文).slice(0,32)(落地为前 16 hex 展示)。原始读数(S7-f-evidence.txt):

块数 = 4(块大小 1024 B)
α  ids(明文哈希) = 1d9e81b64e2fc8a2 3c929675a21f4ac9 9c43515ef7057734 28d85d5bd0b6c6f0
β′ ids(密文哈希) = 9fc1bb4b8d0ec432 a8f5dea758c1d709 66f4e823723fae3c ebe427b90b28ce55
⇒ α 与 β′ 逐位不同   = true
⇒ β′ 两次独立切块一致 = true(组内稳定 ⇒ 按哈希共享块成立)
⇒ 换组后 β′ 全变     = true
⇒ contentId(整份):α = 1cd1b222c4eb46dd7203538baa1497e8 | β′ = 1fd60cf381a79bea8f6ae4a0cefe3152
  • 🔴 轮换代价(如实留档,⛔ 不粉饰):换组密钥(或换 epoch)⇒ 该组 100% 块 id 改变 ⇒ 全员一次全量回源。这是 β′ 的固有代价,不是缺陷;本棒未测得"轮换后首轮回源字节数"(§10-2 未验证项 ⇒ 记 8.13)。
  • ⚠️ 与 E1 的关系:稳态下不退化(见 8.9 F5);唯一退化时刻 = 轮换窗口(单 §7.4 已预告)⇒ 轮换须排进发布窗口(§9-8)。

8.5 store / source / peer 接入(含 decryptRejected 计数)

面 落法 证据
store 只改文档(put/id/get 全程只处理密文字节,⛔ 未改既有语义) 单测 F1-d:缺省(不传 transforms)⇒ 与序㉔ 逐字一致
source 唯一解密点 = crypto.decodeBlock;调用位 ① source.ts 五档链的统一返回点 ② chunker.ts#reassemble 的夹具/工具路径(⛔ 无"local 档不解密"的双口径) F2:put → 链上取回两次 ⇒ local 命中递增且解密成功(local=6)
peer 仍只认 sameGroup;新增 epoch 一致性:epoch 不一致 不作候选 + 单独计数(⛔ 不混进 crossGroupDenied) F3-b / F3-c 两例 ✅;crossGroupDenied 语义未变
计数 十键常量 CONTENT_CRYPTO_COUNTER_KEYS = encrypts / decrypts / decryptRejected / epochs / epoch / epochExpired / detChecks / detMismatches / plainScans / plainLeaks 探针 OBS-23 判据① 判别器齐全(⛔ 缺一即 FAIL)
  • decryptRejected 读数:夹具 CROSS 段 = 1(跨组被拒);夹具 ROT-EXPIRED 段 = 1(超窗口被拒);🔴 线上稳态 = 0(/status.content.crypto 实测 decrypts:3, decryptRejected:0)⇒ 未触发 §9-6 回头条件。
  • ✅ F4-c:不启用加密 ⇒ crypto 键整体缺席(⛔ 不是补零)⇒ 探针才能如实记 SKIP。

8.6 夹具六段(OFF / ON / CROSS / ROT + ROT-EXPIRED / SCAN-MISSING)+ 先红后绿

单内写"四段",本棒按 §5 的判据面扩到六段(补 ROT-EXPIRED = 超窗口、SCAN-MISSING = 扫描腿缺失),⛔ 均为夹具、⛔ 不触真机。原文见 S7-fixtures.txt。

段 预期 实测原文(尾段) 判定
OFF(无加密键) SKIP + 留痕 ⚠️ FIXTURE SKIP OBS-23 ⛔ 未启用组密钥加密(content.crypto 块缺席)⇒ SKIP + 留痕(缺省不启用是合法状态,⛔ 非静默绿) ✅
ON(同组) PASS FIXTURE PASS … detChecks=1 detMismatches=0|decrypts=6 decryptRejected=0 local+peer 命中=5|plainScans=1 plainLeaks=0|epoch=1/1 epochExpired=0 ✅
CROSS(跨组) FAIL FIXTURE FAIL … decryptRejected=1(须 0) ✅
ROT(双 epoch) PASS FIXTURE PASS … epoch=2/2 epochExpired=0|decrypts=4 ✅
ROT-EXPIRED FAIL FIXTURE FAIL … epochExpired=1 ✅
SCAN-MISSING FAIL FIXTURE FAIL … plainScans=0(阈值 ≥ 1) ✅

先红后绿(代码层):把 F1 的确定性 iv 改成随机 iv ⇒ 10 例红(F1/F1-b/F1-c/F2/F4/F5 一族全线点名)⇒ 还原 ⇒ 内容面 44/44 全绿(S7-content-test.txt); npm test 201 tests / 200 pass / 0 fail / 1 skip ⇒ 原 27 例零退化(F 组 16 例为纯追加)。 判定:✅ 六段逐段与预期一致,且 OFF(SKIP)/CROSS(FAIL)/ROT(PASS)三段互不混淆。


8.7 真机先红(新 OBS-23 对旧生产)

  • 对未启用加密的旧形态(S6-probe-oldprod.txt / S7-probe-1.txt): SKIP OBS-23 ⛔ 未启用组密钥加密(content.crypto 块缺席)⇒ SKIP + 留痕 ⇒ ⚠️ 本棒与单文有一处必须点明的偏差:单 §4/S6 写"对旧生产 ⇒ FAIL",但 §5 的 OBS-23 形状又写"缺省不启用 = SKIP + 留痕(合法状态)"。本棒按 §5 实现 = SKIP(更保守:既不静默绿、也不误报红)。⇒ "先红"改由下面那条腿取得(见 8.13-①)。
  • 真机先红(实取,S7-probe-redmode.txt):把 47 的密钥文件权限由 0600 改 0640 ⇒
    FAIL OBS-23 … 密钥文件 /etc/dshs/content-group-key.json 权限须=600:47=640 106=600
    ❌ 1 项红:OBS-23
    
    rc = 1;全量仅 1 红(⛔ 无波及);点名可区分(直接打出"47=640 106=600",⛔ 不是笼统失败)。原文字节数 = 5,827 B(⛔ 非 0 ⇒ "没采到"与"确无"可分)。
  • 复绿:还原 0600 ⇒ S7-probe-restored.txt = 23 PASS / 0 SKIP / 0 FAIL(rc=0)✅

8.8 部署对账 + 密钥文件权限取证

① 产物(18 文件 × 四处):npm run build rc=0 ⇒ 铺 /opt/dshs/lib(47)+/opt/dsh-relay/lib(47)+/opt/dshs-cluster/lib(106)+/opt/dsh-relay/lib(106)。

  • 本机 md5(deploy-local-md5.txt,18 行;另三处逐文件 diff = 0 不一致):
文件 md5 文件 md5
content/chunker.d.ts 7fb3fb442cbaddc62a3ae432a7f9e889 content/source.d.ts 39063c59528ad5ccc11aa3724118b3e5
content/chunker.js 0edd97f2bb59fc1b4f3ae8996dfed467 content/source.js 2aaae850c8a5d08dce5a39644190f08f
content/crypto.d.ts cac34d85ed9b5a368a3c67f7aa742b75 content/store.d.ts 3f74bbc26c2657cf7434473cf6a2d449
content/crypto.js 4893aa35c0e727c7d54c3f2720e24359 content/store.js 1a4a95ed509937a2eba559c702ecd8aa
content/peer.d.ts 5ed40ea5e7585fda8b00a38281d345e9 identity.d.ts fc3e1a74e0bdc84f8b19b7585808a799
content/peer.js 52e141133a5b7b554ee63b1dd410e01d identity.js 77eadb32405fdeb3d0911f00e48b1d81
content/runtime.d.ts 921239ed101615eb6d314902cef56c60 index.d.ts 6782b7ab87aeb33521428d360824b3a1
content/runtime.js 7fe548c42b71b9e4785f139561fe8925 index.js 0b33a15f20c40f66796b2291c672e660
main.js 205f65b5252faccd9fc1f4c7d12f2d1f web/server.js 88259233ec5fac88a3085b5180fb538b

✅ 不一致条数 = 0(四处 × 18 = 72 个点位全部同值;🔴 relay 类产物两边都铺,⛔ 未出现序㉖ 那类"dshs 侧落后一版")。

  • 四处 node --check / require 自检通过。

② 密钥文件(两机同名同权限):

47  : 600 root:root 148 /etc/dshs/content-group-key.json
106 : 600 root:root 148 /etc/dshs/content-group-key.json
(md5 两机同 = 42d01c359abd59136d707ae89fb6ee7b)

③ 启用开关(relay 侧 drop-in,两机同铺):41-content-crypto.conf(2134 B、纯 LF、md5 1b2b530a7e3892fe54fd0d294ba694c1);只加 1 行 env:

[Service]
Environment="DSHS_CONTENT_GROUP_KEY_FILE=/etc/dshs/content-group-key.json"

⛔ 不含任何既有生产键;⚠️ 注释里刻意不写 RELAY_FAILOVER_COOLDOWN_MS=0 字面量(否则 §9-1 的 grep -c 判据会脏)。

④ 重启与时刻:daemon-reload + restart dshs-relay ⇒ 47 = 06:55:59 / 106 = 06:56:02;两机日志均出 [content-crypto] 组密钥已装载 file=/etc/dshs/content-group-key.json group=relay epoch=1 epochs=1 keyId=3560130eb0e5e4aa perms=0600 ✓; /status.content.crypto = {encrypts:3, decrypts:3, decryptRejected:0, epochs:1, epoch:1, epochExpired:0, detChecks:1, detMismatches:0, plainScans:1, plainLeaks:0}。 环境收口(按序㉙ 顺序):restart dshs-worker → restart dshs → 一次真实访问(mksess.cjs guest + POST /api/dsh/enter,Host = alotbuy.com)⇒ 端点表 19000/21000 全 True(孤儿端口未复发)。

⑤ 回滚点:/opt/dsh/backups/seq28-20260918-065104/(47 与 106 各 4 处旧产物;旧 md5 全留档 —— backup47.txt / backup106.txt;⚠️ 其中 content/crypto.* 与 47/106 平台的 content/runtime.* 开工为 MISSING ⇒ 回滚 = 删文件,见 §6 路 B)。


8.9 判据 F1–F7(逐条原文)

# 判据 原文(命令 ⇒ 输出摘录) 判定
F1 同组密文一致 node _tmp_seq31/f_evidence.mjs ⇒ 密文#1 md5 = 密文#2 md5 = 3385e9d00ae5c5dbb880ddad90391756、逐字节相同 = true ✅ 通过
F2 共享不退化 探针夹具 ON ⇒ decrypts=6 decryptRejected=0 local+peer 命中=5;单测 F2 共享不退化… local 命中递增且解密成功 ✅ 通过
F3 跨组不可解 F3 跨组不可解:组 B 用自己的密钥解组 A 的密文 ⇒ 拒 + decryptRejected +1;夹具 CROSS ⇒ decryptRejected=1;F3-b 跨组判定不被放松 ✅ ✅ 通过(crossGroupDenied 语义未变)
F4 明文不出现(+反向证明) ① 密文里扫标记 = 命中 0(样本 3804 B);② 反向:不加密字节里扫同标记 = 命中 ≥ 1;正向 plainScans=1 plainLeaks=0、反向 plainScans=1 plainLeaks=1(⛔ 非 0 ⇒ 这条腿有牙) ✅ 通过(① ② 都在,⛔ 未踩 §9-4)
F5 E1 主判据不退化 F5/E1 口径:加密前后"回源份数"不变(同组 N 次取用 ⇒ 全部 local 命中、零回源);断言 4 轮 × 3 块,全部 local 命中(= E1 的"回源 1 份"口径) = source.local = 12、F2-b 同一份内容重复 put ⇒ cache 不翻倍 ✅ 通过(⛔ 未从 1.0000× 退回 4.00×)
F6 失败关闭(具名) 七例逐例具名:no-file / bad-perms / parse-error / group-mismatch / bad-key / bad-epoch / prev-key-bad(+正例);例:bad-perms ⇒ 权限 666(必须 0600)、group-mismatch ⇒ 文件 group=relay ≠ 运行时 group=local ✅ 通过
F7 零回归 见 8.10 ✅ 通过

8.10 零回归三件套

全部带 --table "E:/ProgramData/AI技能/aliyun-dsh-server/参数表_覆盖网络_20260917.md";收口稳态(S7-npmtest.txt / S7-drill.txt / S7-probe-terminal.txt):

件 命令 基线(开工现核) 收口实测 判定
单元测试 npm.cmd test(Node 22) 201 tests / 200 pass / 0 fail / 1 skip 201 / 200 / 0 / 1 ✅ 逐字不退化
演练 node scripts/overlay-failover-drill.cjs --scene all --table "<参数表>" 12 PASS / 0 SKIP / 0 FAIL 12 PASS / 0 SKIP / 0 FAIL ✅
探针 node scripts/overlay-probe.cjs --table "<参数表>" 稳态 22 PASS / 0 SKIP / 0 FAIL 23 PASS / 0 SKIP / 0 FAIL(rc=0) ✅ 净 +1 行(新增 = OBS-23 PASS)

OBS-23 终态原文(关键尾段):

PASS OBS-23 组密钥加密(GCM 确定性)判别器 齐全|F1 确定性 detChecks=1(阈值 ≥ 1) detMismatches=0(须 0)
|F2 共享 decrypts=3(阈值 ≥ 1) decryptRejected=0(须 0) local+peer 命中=2(阈值 ≥ 1)
|F4① 明文扫描 plainScans=1(阈值 ≥ 1) plainLeaks=0(须 0)|epoch=1/1 epochExpired=0
|密钥文件 /etc/dshs/content-group-key.json 权限须=600:47=600 106=600

8.11 边界自证

边界 证据 判定
⛔ 未改任何生产值(D1) 新 drop-in 只 1 行 env;⛔ 未动 RELAY_FAILOVER_* / HB_SEC / burst / PRESENCE_* / DSHS_CONTENT_STORE_* / DSHS_OVERLAY_*;40-content-probe.conf 两机 md5 同 = 9e7ccf9a8e59fc88367cea0a07c3d8b8(未被动) ✅
⛔ 未新增公网监听口 仅新增文件落点 /etc/dshs/;ss -lntp 面未见新口(relay 仍只绑回环 20080) ✅
⛔ 未改 nft / nginx 全程零 nft / nginx 命令(R5) ✅
⛔ 未放松跨组 F3-b / F3-c ✅;crossGroupDenied 语义未变;E5 无退化 ✅
🔴 RELAY_FAILOVER_COOLDOWN_MS=0 计数 grep -rIls "RELAY_FAILOVER_COOLDOWN_MS=0" /etc/systemd/system /etc/dshs | wc -l ⇒ 47 = 0 / 106 = 0 ✅ = 0
🔴 密钥本体 ⛔ 不经网络、不经 relay 只走 0600 落文件 + scp;中继侧只广播 (network, group, epoch, keyId) 三元组(凭据逐字核:不含 key);relay 消息面 ⛔ 零新增密钥字段 ✅
🔴 未改 bwrap 参数(47 = bubblewrap 0.4.0) 本单未触碰任何 banner/沙箱参数(文件集见 §3.1) ✅
⛔ 未 commit / 未 push(R7) git rev-parse HEAD = 04776af4b130014b0f4db10de89b0ae982300afc;git ls-remote origin refs/heads/master = 同值(⇒ 未推);git status --porcelain | wc -l = 35 ✅
⛔ 未建 04 孪生 / 未补占空号 本单不落 04 孪生(与本线全部 交接单_*.md 一致);63 仍为空号 ✅

8.12 指纹

  • 参数表 §10 口径指纹:3b295a8c43aafc4a62c6f1a59ea0b43a(序㉚ 收口)→ 08c2835a7e9f0faef20f9c1d3e619d55(本棒收口;变更 = §3.9 新增 6 键 CONTENT_KEY_FILE / CONTENT_KEY_FILE_MODE / CONTENT_EPOCH_GRACE_MS / CONTENT_CRYPTO_DET_MIN / CONTENT_CRYPTO_DECRYPT_MIN / CONTENT_CRYPTO_SCAN_MIN + §6 新增 OBS-23 一行;口径 = sed '/^## §10 指纹/,$d' 参数表_覆盖网络_20260917.md | md5sum)。
  • 本单 §8 前前缀(口径 sed '/^## §8 回报格式/,$d' <本单> | md5sum)= 149596460288ca1a5b7abddc490f7909 —— ✅ 回填后复跑逐字不变(§8 是截断点)。
  • 全文 md5 / 行数:⛔ 不内嵌(自指)⇒ 现算 md5sum "E:/ProgramData/AI技能/aliyun-dsh-server/交接单_组密钥加密_20260918.md";回填前快照 = 0412daf3dc8edf96c2fbe87bdefbdb29(356 行)。
  • 归档号:130(本单孪生号;执行为 §11.1 记录,⚠️ 执行棒收官建档案时须重新现核取号)。

8.13 如实留档(必填,哪怕"没有")

① ⚠️ 单文一处内部不一致(本棒按 §5 实现,与 §4/S6 的措辞不同):§4/S6 写"新 OBS-23 对旧生产 ⇒ FAIL",而 §5 的 OBS-23 形状写"缺省不启用 = SKIP + 留痕(合法状态)"。本棒取了 SKIP(理由:既不静默绿、也不把"合法的没启用"误报成红)。⇒ "真机先红"改由权限腿取得(0600→0640 ⇒ FAIL 且点名 47=640 106=600,rc=1,全量仅 1 红),复绿后 23P/0S/0F。🔴 这条不是缺陷,是单文两处口径需统一 —— 建议下一棒在单里定稿(倾向保留 §5 的 SKIP 口径 + 把"先红"腿写清为权限腿)。

② ⚠️ 签名动作的命令未逐字留痕:sign-group-key 用的是 47 上既有签名者私钥,但 47 上没有 /opt/dshs/scripts/overlay-keyring.cjs、/tmp 亦无残留 ⇒ 本棒只能自证"凭据不载荷密钥本体 + 验签通过 + 篡改必失败",无法自证"私钥未出机"。⇒ 建议:把"组密钥签发"固化成 47 上的正式子命令(⛔ 不靠临时投放脚本)。另立小单,本棒不动。

③ ⚠️ 未测得"轮换后首轮回源字节数"(§10-2):本棒只证得"换组 ⇒ 100% 块 id 改变"(定性),未造 4 组 × 1 份包的轮换前后 E1 读数(定量)⇒ §10-2 仍未验证,⛔ 未编数。

④ ⚠️ 未测得加密性能开销(§10-1:N ≥ 5、p50/p95):⛔ 无数据,⛔ 不报单次。

⑤ ⚠️ OBS-23 修过一处真缺陷(本棒发现并已修):密钥文件权限检查门原为 cryptoFx === undefined ⇒ 只给 --content-fixture(不给 --content-crypto-fixture)时会偷偷 ssh 到生产机(与"夹具模式绝不 ssh"的契约面不一致)。已改为门 = !fixture。⇒ 属本单范围内的真缺陷修复,已复测六段夹具全符合预期。

⑥ ⚠️ 两处环境态本就存在、本棒未动:47 users/ 权限、106 残留 scope dsh-100002-844b8c29.scope(属既有 user 4092b965 的实例,转由认领机制管理);OBS-22 的 47 认领面 journalctl 原文 0 字节 = 读取失败(⛔ 不是"确无 summary")。

⑦ ✅ 临时 session 已清干净:DELETE FROM sessions WHERE user_agent='poc-curl2' ⇒ DELETE 3,残留 0。

⑧ ✅ _tmp_seq31/ 为本次临时证据目录(含夹具、脚本、原始输出),⛔ 未删除(待用户确认后再清);夹具 fx-*.json 仅本机,⛔ 未铺任何服务器。


8.14 序 ㉛ 执行棒回报(2026-09-18 07:33–07:5x · §10 未验证项补齐 + 口径定稿 + §8.13-② 结清)

🔬 开工门:sed '/^## §8 回报格式/,$d' <本单> | md5sum = 149596460288ca1a5b7abddc490f7909 ⇒ 逐字一致,✅ 通过(本条写在 §8 之后 ⇒ ⛔ 不影响该不变量)。 📁 证据目录 = 工作区根 _tmp_seq32/(脚本 .mjs/.sh + 原始输出 .txt,逐项对应下方);⚠️ 临时产物,⛔ 未删除(待确认后再清)。 ⚠️ 本棒性质 = 补测 + 小额固化 ⇒ ⛔ 未扩范围、⛔ 未改生产值、⛔ 未动代码(src/** 零改动)。

8.14.1 主题 ① 加密性能开销(§10-1)—— ✅ 有原始读数

口径:同一台机、同一份首屏包 11,363,655 B / 11 块;先预热 2 次再测,n = 11;每轮 = 切块 + 加密 + put + get + 解密 全链总墙钟;两臂跑同一夹具;分位取 nearest-rank(⛔ 不插值、⛔ 不报单次)。

臂 p50 p95 吞吐 密文固定开销 origin 命中
纯明文 22.636 ms 23.020 ms 502.02 MB/s — 0
组密钥加密 42.782 ms 43.599 ms 265.62 MB/s +28 B/块 0
增量 / 比值 +20.146 ms / 1.890× +20.579 ms / 1.894× — — —
  • 🔴 口径边界(⛔ 不许当端到端承诺):单核、页缓存全热、两臂均零回源 ⇒ 这是"加密层自身成本"的参照读数,⛔ 不是线上 E1 读数。
  • 原始输出 = _tmp_seq32/p01-perf.txt(脚本 p01-perf.mjs)。

8.14.2 主题 ② 轮换代价量化(§10-2)—— ✅ 结清 §8.13-③

夹具:4 组 × 4 节点,包 5,255,225 B / 6 块;组成员按需取块(local → peer → origin,命中即缓存);另设「不轮换」对照腿(⛔ 无对照腿 ⇒ 无法断言"回源是轮换引起的")。

阶段 回源字节 相对包 local/peer/origin 块 id 改变
冷启动(轮换前) 21,021,572 B 4.0001 × 包 = 1 × 包/组 0 / 72 / 24 —
轮换后首轮 21,021,572 B(比值 1.0000) 同值 — 24 / 24 = 100%
轮换后第二轮 0 — — —
对照腿(不轮换)第 3 轮 0 — — —
稳态(两腿) 0 — — —
  • ⇒ "轮换 = 一次完整冷启动(1 × 包/组)"已量化(与 §10-2 预期逐字吻合);"回源由轮换引起"可断言(对照腿第 3 轮 = 0)。
  • 🔴 附带代价(本棒实测,原本未在册):旧 epoch 密文变垃圾且不自清 —— storeBytes 5,255,393 → 10,510,786(≈ 翻倍)。⇒ 回头条件 8(轮换须约在发布窗口)多了一条理由:不只是回源,还有存储占用翻倍直到旧块被逐出。
  • 原始输出 = _tmp_seq32/p02-rotate.txt(脚本 p02-rotate.mjs;⚠️ v1 夹具曾因"包预置进 local"造假绿 postOverPre = NaN,已重写后取上表读数)。

8.14.3 主题 ③ 低熵明文的定性结论(§10-3)—— ✅ 定性成立,⛔ 不写"很安全"

模型 读数 结论
A:持组密钥者 字典枚举 1000 条 = 11.644 ms(≈ 85,878 条/秒)、命中 1、恢复正确(域 2³² ⇒ 全量 ≈ 14.7 h) 枚举可行 ⇒ 低熵明文对持钥者无保护
B:无钥者 / 中继 自造字典命中 0 / 1000;持有 1 对明文-密文也预测不出别的(0 / 999) 枚举不可行
B 的旁路:相等性泄露 10,000 块 / 100 值域 ⇒ 仅 100 种密文(收敛加密的固有性质) 重复率 / 相等性可观测
反向腿(高熵对照) 1000 / 1000 无碰撞 ⛔ 证明"没测到 ≠ 真没有"
  • 🔴 定稿口径:"中继看不到明文" 成立 / "内容不可推断" 不成立(收敛加密对低熵必然泄露相等性)。⛔ 不写"很安全"。
  • ⚠️ 夹具口径修正(本棒发现):初版语料用 LCG 低位取模(x % 100)只出 37 个不同值 ⇒ 已改 xorshift32 取高位 (x >>> 8) % 100 ⇒ 100 / 100,口径干净后取上表读数。
  • 原始输出 = _tmp_seq32/p03-lowentropy.txt(脚本 p03-lowentropy.mjs)。

8.14.4 主题 ④ CONTENT_EPOCH_GRACE_MS 三档混存夹具(§10-6)—— ✅ 三档曲线齐

夹具:混存 20 块(写 epoch 4 + 历史 1 / 2 / 3),retiredAt = T0 / T0+2h / T0+12h,时钟注入 now(⛔ 不许真等)。

档 取值 归零时刻 窗口内 epochExpired 超窗行为
小 60 s t = +24 h 0 逐 epoch 点名 + decryptRejected +1
中(当前值) 24 h t = +48 h 0 同上
大 7 d t = +8 d 0 同上
  • 曲线形状 = 阶梯衰减(最晚 retiredAt + 本值 = 归零);写 epoch 恒 5 / 5 可解(⛔ 旧密文过期不影响写侧)。
  • ⇒ 定稿见 §11.5 定稿三(当前值 24 h 维持,⛔ 本棒未改任何值)。
  • 原始输出 = _tmp_seq32/p04-epoch.txt(脚本 p04-epoch.mjs)。

8.14.5 主题 ⑤ 单文口径定稿 —— ✅ 写进 §11.5(⛔ §4 / §6 正文零改动)

  • 定稿一:OBS-23 对"缺省不启用"取 SKIP + 留痕;§4/S6 那句「对旧生产必 FAIL」作废;"真机先红"腿由「权限腿」承载。
  • ⚠️ §8.13-① 的口径张力至此结清(⛔ 未打穿 §8 前前缀:修正一律写在 §8 之后)。
  • 权限腿读数(单 B 实测,本棒复核同值):0600 → 0640 ⇒ OBS-23 FAIL 并点名 47=640 106=600、rc=1、全量仅 1 红 ⇒ 还原即复绿。⇒ §10-4(0600 运维一致性)由此覆盖(逐机实读权限 + "权限错必失败关闭"两条腿都有)。

8.14.6 主题 ⑥ 组密钥签发固化(结清 §8.13-② 缺口)—— ✅ 已落正式路径

项 证据
落点(两处) /opt/dsh-relay/scripts/overlay-keyring.cjs(就地更新:旧版 12,915 B、grep -c sign-group-key = 0)+ /opt/dshs/scripts/overlay-keyring.cjs(此前不存在 = §8.13-② 的缺口)
字节一致 两处 md5 均 f1855eaef30f71fbca00ac0a92865c0e(与本机同值)
在机验证 sign-group-key ⇒ rc = 0,凭据 274 B(ops/relay epoch=1 keyId=3560130eb0e5e4aa),密钥本体出现 0 次|verify-group-key ⇒ rc = 0|篡改 epoch ⇒ signature-mismatch rc = 1|--epoch 9 交叉核对 ⇒ rc = 1
回滚点 47 /opt/dsh/backups/seq31-20260918-073857/
⛔ 未重启 纯 CLI,⛔ 未重启任何 unit(⛔ 未中断任何用户)

8.14.7 零回归三件套(三者均带 --table "E:/ProgramData/AI技能/aliyun-dsh-server/参数表_覆盖网络_20260917.md")

项 结果 与开工基线
npm.cmd test(Node 22) 201 tests / 200 pass / 0 fail / 1 skipped ⇒ 注入脚本「结论:全部合格 ✅��� 逐字相同 ✅
overlay-failover-drill.cjs --scene all --table 12 PASS / 0 SKIP / 0 FAIL(rc = 0) 同值 ✅
overlay-probe.cjs --table 23 PASS / 0 SKIP / 0 FAIL(rc = 0) 与开工基线 23P/0S/0F 一致 ✅
  • 🔬 本棒三件套的"先红 → 后绿"过程(如实留档):环境收口期间(重排实例落点)曾出现 19P/1S/3F(OBS-01 used=1 / OBS-04 identityOk=1 / OBS-08 端点表 0 条)与 22P/1S/0F(OBS-09 SKIP,因实例暂落端口 21001)。未编数、未凑绿 —— 依据序 ㉙ 留档的机制(孤儿端点条目不自愈,靠"重新登记同一端口"覆盖),用 /api/dsh/stop + /api/dsh/enter 把实例落回 21000 ⇒ OBS-09 转 PASS(w-106:39027=401)、OBS-21 复绿(两路径 count = 3)⇒ 23P/0S/0F。
  • 原始输出 = _tmp_seq32/base-npmtest.txt / final-drill.txt / final-probe-1..4.txt。

8.14.8 恢复形态核实 + 参数表 & 日志

  • 参数表:§3.9 CONTENT_EPOCH_GRACE_MS 行说明列补序㉛ 三档曲线|§6 OBS-23 行末补「口径定稿」|§7 补「序㉛ 复算:仍为 待测 0 项(⛔ 未新增键、⛔ 未改任何值)」|新增 §11.3 补记(六条)。
  • 🔴 §10 指纹:08c2835a7e9f0faef20f9c1d3e619d55 → 52e59df229102d5afa8c8bf01a960398(本棒收口现算 + 已同步;口径 = 先 sed '/^## §10 指纹/,$d' 截断再 md5)。⛔ 本轮零键零值变更 ⇒ 探针取阈值不受影响。
  • 本单全文 md5:⛔ 不写死 ⇒ 现算 md5sum "E:/ProgramData/AI技能/aliyun-dsh-server/交接单_组密钥加密_20260918.md"。

8.14.9 边界自证(本棒)

边界 证据 判定
⛔ 未 commit / 未 push(R7) HEAD 仍 04776af;⛔ 未执行任何 git commit / git push ✅
⛔ 未改任何生产值(D1) 两机 env / drop-in 零改动;⛔ 未动 RELAY_FAILOVER_* / HB_SEC / burst / PRESENCE_* / DSHS_CONTENT_* ✅
⛔ 未新增公网监听口、⛔ 未改 nft / nginx(R5) 全程零 nft / nginx 命令;OBS-11 多出 0 ✅
🔴 RELAY_FAILOVER_COOLDOWN_MS=0 ⛔ 全程未出现在任何夹具 / 脚本 / 演练路径(本棒新增文件逐字核 = 0) ✅ = 0
⛔ 未改 bwrap 参数(47 = 0.4.0) 本棒未触碰任何沙箱参数 ✅
🔴 密钥本体 ⛔ 不经网络 / 不经 relay 签发只在 47 本机(凭据 274 B、不含密钥本体、⛔ 未 scp 任何密钥) ✅
🔴 Ed25519→X25519 派生 ⛔ 本棒未执行(不在写死主题内 ⇒ 未扩范围) ✅ 未越线

8.14.10 ⚠️ 如实留档(本棒"没有"的部分,必填)

① ⚠️ §10-5(Ed25519→X25519 派生预研)本轮未做:它不在本棒写死的六项主题内(用户明令「⛔ 不许顺手扩大范围」)⇒ 无读数、⛔ 未编数。它服务的对象是 §9 回头条件 7("规模上来了要自动分发组密钥")⇒ 触发条件 = 规模上来,⛔ 不是当前执行项。 ② ⚠️ §10-4 未单独跑"铺发后逐机 ls -l / stat":读数改由 OBS-23 权限腿承载(47=600 106=600 + 0600→0640 ⇒ FAIL 并点名)⇒ 判据覆盖成立,但取证形态不同(探针读 vs 手工 stat)。 ③ ⚠️ 性能读数是本机、单核、页缓存全热(§8.14.1 口径边界)⇒ ⛔ 不得外推为线上 E1 承诺。 ④ ⚠️ 轮换夹具的存储翻倍(§8.14.2)是发现的新代价,⛔ 未做"旧块何时被逐出"的量化(⇒ 记为待观察,⛔ 不冒充已验证)。 ⑤ ✅ 临时 session 已清干净:DELETE FROM sessions WHERE user_agent='poc-curl2' ⇒ 「DELETE 行数 = 1」、残留 = 0。 ⑥ ✅ _tmp_seq32/ 为本棒临时证据目录,⛔ 未删除(待用户确认后再清)。


§9 回头条件(一出现必须回头)

  1. 🔴 ⛔ 任何一棒都不许把 RELAY_FAILOVER_COOLDOWN_MS=0 写进回滚 / 演练 / 夹具路径(自锁:实测 121–123 s 无切换)。本单验收里 grep -c 该值必须 = 0。
  2. E5 退化(跨组穿透 > 0,或 crossGroupDenied 语义被放松)⇒ 立刻回滚 + 报告。
  3. F1 不成立(同组两次加密密文不同)⇒ 停下报告("按哈希共享块"已失效,继续做只会放大回源)。
  4. F4 只绿其①、②未做(没有反向夹具证明"没测到 ≠ 真没有")⇒ 判据不算通过,停下补证。
  5. 需要超出 §3.1 在册文件集(尤其要动 wire.ts 帧格式 / keys.ts 既有语义)⇒ 停下报告(R7)。
  6. decryptRejected 在稳态下持续增长(⇒ 有节点密钥/epoch 不一致)⇒ 停下报告,⛔ 不许"重启一次看看"。
  7. 规模上来了要"自动分发组密钥" ⇒ 停下报告(需先给节点密钥加 x25519 或做 Ed25519→X25519 派生 ⇒ 属新增密钥类型,另立单)。
  8. 轮换窗口出现在非发布期(⇒ 一次全量回源砸在用户活跃时段)⇒ 停下报告,把轮换改约到发布窗口。
  9. 性能不达标(加密后 E1/吞吐显著变差)⇒ 先量化再决定,⛔ 不许凭感觉回滚。

§10 未验证项(一个都不许编数)

# 未验证项 为什么重要 怎么验(口径)
1 加密的性能开销 决定"兼得"是否真的兑现 同一台机、同一份 10.8 MB 首屏包,测 N ≥ 5 次的"切块 + 加密 + put + get + 解密"总墙钟与纯明文对照;⚠️ 记 p50 / p95,⛔ 不报单次
2 轮换的实际代价 β' 的核心代价是"轮换 = 一次全量回源" 造 4 组 × 1 份包,轮换前后各跑一次 E1 口径 ⇒ 记录回源字节(预期:轮换后首轮 ≈ 1 份 × 组数,之后回落到 1×)
3 收敛加密对低熵内容的安全性 收敛加密对低熵明文有已知的确认攻击面(可枚举) 用低熵样本(如小文本块)验证:能否由密文反推明文集合;口径 = 只做定性结论,⛔ 不写"很安全"
4 0600 密钥文件的运维一致性 本线有"属主被改 ⇒ 实例全崩"的先例 铺发后逐机 ls -l + stat;并验"权限错时必须失败关闭而不是静默明文"
5 Ed25519→X25519 派生可行性(为回头条件 7 预研) 决定"自动分发"能不能不新增密钥类型 纯本地实验:从 ed25519 私钥导出 raw scalar ⇒ 构造 x25519 KeyObject ⇒ ECDH 往返成功?(⚠️ 只做实验、⛔ 不进本单代码)
6 双 epoch 过渡窗口的取值 太长 ⇒ 旧密钥留太久(安全);太短 ⇒ 老节点掉线 用 CONTENT_EPOCH_GRACE_MS 三档(小/中/大)各跑一次混存夹具,记录"旧密文可解的比例随时间"

§11 指纹与状态

11.1 归档号(本棒已原子占号)

  • 现核最大号(⛔ 不写死;取号命令):ls 04-调整方案/ | grep -oE '^[0-9]+' | sort -n | tail -1 ⇒ 本棒实测 = 128。 ⚠️ prompt 写的"归档编号至 112"已过期(现核 128)⇒ 引用时必须复跑取号。
  • 占号动作(已执行):mkdir 04-调整方案/.lock-130 ⇒ 成功 ⇒ 本单归档号 = 130(单 A = 129)。
  • 🔴 63 是空号,⛔ 不补占(已核:ls 04-调整方案/ | grep -E '^63' = 空)。
  • ⚠️ 本棒不落 04 孪生(本线 交接单_*.md 均无 04 孪生;带孪生的是方案类文档 —— 逐条比对 16 份单 = 0 命中)⇒ 占号窗口在落单后即释放,执行棒收官建档案时须重新现核取号。

11.2 指纹

  • §8 前前缀(口径:sed '/^## §8 回报格式/,$d' <本单> | md5sum):见下方"落单后实测"。
  • ⚠️ §8 回填后前缀指纹不变、全文 md5 会变。
  • 落单后实测(2026-09-18 00:5x,序 ㉘ 规划棒):
    • §8 前前缀 = 149596460288ca1a5b7abddc490f7909(⚠️ 本值稳定 —— §11 在截断点之后)
    • 全文 md5(§11 本段回填前快照) = ef7aad0cf65f4d83221f46e60caa96ed
    • 全文 md5(§11 本段回填后最终) = 见下方 §11.4(写完本段后现算)
    • 行数 = 344(快照时刻;回填 §8 后会增长)

11.3 状态

  • 本单状态:✅ 已执行收官。拆两棒:单 B 执行棒(2026-09-18 06:22–07:2x)= 代码 + 部署 + 判据 F1–F7 ⇒ 证据见 §8.1–§8.13;序 ㉛ 执行棒(2026-09-18 07:33–07:5x)= §10 未验证项补齐 + 口径定稿 + §8.13-② 缺口结清 ⇒ 证据见 §8.14 / 定稿见 §11.5。
  • 本棒(序 ㉘ 规划棒)边界自证:⛔ 零代码改动 · ⛔ 零服务器改动 · ⛔ 未 commit / 未 push · ⛔ 不改任何生产值 · ⛔ 未实现任何加密(本单只出方案 + 判据)。
  • 两单关系:单 A(归档号 129)与单 B(130)文件集零交集,可并行;⚠️ 但同一时刻只挂一个接续棒(用户 2026-09-18 00:3x 明令)⇒ 执行顺序由下游定序理由决定(见入口 §2 的定序段)。

11.4 全文 md5 的口径(⛔ 不写死)

  • 全文 md5 每改一次本单都会变(回填 §8 尤其)⇒ 权威取数 = 现算: md5sum "E:/ProgramData/AI技能/aliyun-dsh-server/交接单_组密钥加密_20260918.md"
  • 落单快照(§11.4 本段写入前的最后一次现算,2026-09-18 00:5x)= 40d35357a501596dcd1e6d7e70e8b371(349 行)。 ⚠️ 本行写入后该值即失效 ⇒ 引用前必须复跑上面的命令。
  • §8 前前缀 不受影响(§8 是截断点)⇒ 仍是 §11.2 的 149596460288ca1a5b7abddc490f7909。

11.5 🔴 口径定稿(2026-09-18,序 ㉛ 执行棒)—— 本行起具最高效力

效力范围:本单 §4 / §6 的正文一个字都没改(⛔ 不能改 —— 见下"为什么")。凡 §4 / §6 / §5 之间口径打架处,一律以本节的裁决为准。

定稿一:OBS-23 对"缺省不启用"取 SKIP + 留痕(⛔ 不是 FAIL)

  • 裁决:§5 与 §6 的形状正确,§4/S6 里那句「新 OBS-23 对旧生产** ⇒ FAIL」作废。**
  • 理由:① "缺省不启用"是合法状态(本单的设计目标就是"缺省不启用 ⇒ 行为逐字回到序㉔"),把它判红 = 把合法态误报成故障 ⇒ 长期挂假红会磨掉判据灵敏度(新红项会被"反正是红的"淹没 —— 这是本线已复盘过的教训);② 探针实现里 SKIP 行不进红计数,且必打留痕(打印"⛔ 未启用组密钥加密(content.crypto 块缺席)⇒ SKIP + 留痕"),⛔ 既不是静默绿、也不是假红。
  • 🔴 "真机先红"这条腿由「权限腿」承载(单 B 已实测):把 CONTENT_KEY_FILE 从 0600 改 0640 ⇒ OBS-23 FAIL 并分别点名两台机的实读权限(47=640 106=600)、rc=1、全量仅 1 红 ⇒ 还原即复绿。⇒ 判据有牙,且"有牙"这件事不依赖"是否启用加密"。
  • ⛔ 仍然禁止:出现"既不是 SKIP 也不是 FAIL"的第三态(静默绿)。

定稿二:β′(块 id 挂密文)的既有代价,本轮已量化,⛔ 不再当"未验证"

  • 轮换代价 = 一次完整冷启动:4 组 × 4 节点夹具实测 —— 冷启动回源 21,021,572 B = 4.0001 × 包 = 1 × 包/组,轮换(epoch 1→2,块 id 改变 100%)后首轮同值(比值 = 1.0000),第二轮起 0;对照腿(不轮换)第 3 轮 = 0 ⇒ "回源是轮换引起的"可断言。⚠️ 附带代价 = 旧 epoch 密文变垃圾且不自清(storeBytes 5,255,393 → 10,510,786)。
  • 加密性能开销:同一份 11,363,655 B 首屏包、n = 11 ⇒ 总墙钟 p50 22.636 → 42.782 ms、p95 23.020 → 43.599 ms(1.890× / 1.894×,绝对 +20.1 / +20.6 ms);吞吐 502.02 → 265.62 MB/s。⚠️ 口径 = 单核、页缓存全热的本机读数 ⇒ 是"加密层自身成本"的参照,⛔ 不是端到端承诺。
  • 低熵明文的定性结论:持组密钥者可枚举恢复(1000 条域 11.644 ms、命中 1);**中继(无密钥)**枚举不可行(自造字典命中 0/1000,持有 1 对明文-密文也预测不出别的),但能观测相等性 / 重复率(10,000 块 / 100 值域 ⇒ 仅 100 种密文)⇒ ⛔ 不写"很安全":安全边界 = "中继看不到明文"成立,但"内容不可推断"不成立。
  • ⇒ 这三条不再是 §10 的"未验证项"(原始读数见参数表 §11.3 补记 + 本单 §8.14;脚本原文 = 工作区 _tmp_seq32/p01-perf.txt / p02-rotate.txt / p03-lowentropy.txt / p04-epoch.txt)。

定稿三:CONTENT_EPOCH_GRACE_MS 的选值口径

  • 判定式 = "轮换后还允许旧密文被解多久";实测归零时刻 = 最晚 retiredAt + 本值(小 60 s ⇒ 24 h;中 24 h ⇒ 48 h;大 7 d ⇒ 8 d,阶梯衰减、窗口内 epochExpired=0)。
  • ⛔ 太短 ⇒ 尚未换完钥的节点掉线;太长 ⇒ 旧密钥留太久(安全)。⚠️ 轮换时点必须约在发布窗口(§9-8)。⇒ 当前值 24 h 维持(⛔ 本棒不改任何值)。

为什么 §4 / §6 正文不能改(写下来,防下一位读者"好心改回去")

  • 本单的 §8 前前缀 149596460288ca1a5b7abddc490f7909 是一条不变量("§8 是截断点")—— 它的作用是证明"计划在接受执行后没被人偷改"。动 §4/§6/§5 里任何一个字都会打穿这条证据链。⇒ 凡口径修正,一律写在 §8 之后(本节 + §8.14),并在引用时指明"以 §11.5 为准"。