Files
dsh_ai1net_server/docs/覆盖网络/参数表_覆盖网络_20260917.md
T

189 KiB
Raw Blame History

参数表 · 覆盖网络(唯一一张)

线:覆盖网络线 | 序:⑤(参数表 · 观测 · 权限评估)| 产出:执行棒 2026-09-17 唯一来源:覆盖网络_应用场景与待完善清单_20260916.md §五 第 5 项 + §四 P1 行 + 交接单_443兜底_20260917.md §8.8 本表要做的事:把散在 6 份文档里的输入参数 + 代码里已固化的常量收成一张可复算的表(每行带来源等级与来源定位),并填掉 443 单留下的 45% 口径空位。 ⛔ 本表不引入新模型、不引第三方依赖、不做架构改动;性质是「固化」,不是重新推演。 🔴 待测 项一个都不许编数 —— 编出来的数会让整张表失去"可复算"的资格。待测 行的值列留空。


§0 来源等级(四档,⛔ 不许混用)

等级 含义 判据
实测 本机 / 远端命令原文能复现的数 本表给出一条能跑的命令
推导 由表内其它行按公式算出来的数 本表给出复算式,独立手算能得同值
估值 文档里的行业口径 / 旧记录 / 推演假设 指到文件:行,且不得被当成实测引用
待测 本单取不到真值 值留空,写明"由序⑥ 用 3–5 台真机换掉"

⚠️ 旧记录里两个数本次已复核判为不可用(⛔ 别再引用): ① 47 规格"1.8 GB / 2 核"(09-08)⇒ 本次实测 1870 MB / 2 核(量级巧合,但必须用实测值复算); ② 跨云带宽 ~22 KB/s(覆盖网络_千台全场景推演_20260916.md:38)⇒ 本次实测上行下界 ≥ 192 KB/s(≈ 9×),原值作废。


§1 用法约定(机器可读 —— 探针脚本靠它取阈值)

  • 参数行格式:| `KEY` | 值 | 单位 | 等级 | 来源定位 | 复算式 |
  • 观测阈值行格式:| `OBS-NN` | 指标 | 阈值(引用键或字面值) | 判据 |
  • ✅ scripts/overlay-probe.cjs 的每一个阈值都从本表读,⛔ 脚本内不许有魔数(判据 = 交接单 §6 E6)。
  • 探针运行方式(一条命令,cwd = 工作区根): cd "E:/ProgramData/AIProject/aliyun-dsh-server" && node "D:/github/dsh_shenxian/scripts/overlay-probe.cjs"

§2 运行坐标

键 值 单位 等级 来源定位 复算式
SSH_TARGET_47 bt-server — 实测 ~/.ssh/config(HostName 47.77.182.89) ssh -p 22 bt-server hostname
SSH_TARGET_106 test106 — 实测 同上(HostName 106.54.21.172) ssh -p 22 test106 hostname
SSH_PORT 22 — 实测 交接单 §2 注(bt-server 配置写的 32022 已陈旧) ssh -p 22 bt-server true
W47_HOSTNAME iZrj99af19cibck1ge93tqZ — 实测 本次 S0 P6 ssh -p 22 bt-server hostname
W106_HOSTNAME VM-0-8-opencloudos — 实测 本次 S0 P8 ssh -p 22 test106 hostname
RELAY_STATUS_URL http://127.0.0.1:20080/status — 实测 relay ExecStart --port 20080 ssh -p 22 bt-server 'curl -s 127.0.0.1:20080/status'
PORTAL_URL http://127.0.0.1:3080/ — 实测 平台门户(⚠️ http2 on ⇒ 必须 --http1.1,否则假 404) ssh -p 22 bt-server 'curl -s --http1.1 -H "Host: ai1net.com" 127.0.0.1:3080/'
PORTAL_HOST_HEADER ai1net.com — 实测 同上 同上
RELAY_NETWORK_ID ops — 实测 /etc/dshs.env DSHS_OVERLAY_NETWORK_ID ssh -p 22 bt-server 'grep NETWORK_ID /etc/dshs.env'
SSH_TIMEOUT_MS 20000 ms 推导 探针脚本单次 ssh 的超时(⚠️ 放这里是为了让 overlay-probe.cjs 零魔数;实测单次 ssh 往返 ≈ 1 s,取 20× 余量) —

🆕 序⑦ 真机演练坐标(scripts/overlay-failover-drill.cjs 用它取数 ⇒ 脚本内零魔数)。⚠️ 序㊳ 起 RELAY_UNIT_NAME 也被探针 OBS-08 读取(⇒ 已改名、从 DRILL_ 前缀剥离):

键 值 单位 等级 来源定位 复算式
RELAY_UNIT_NAME dshs-relay — 实测 两台机的 relay 单元名(systemctl cat dshs-relay)。🆕 序㊳ 由 DRILL_RELAY_UNIT 改名 —— 它记的是"两台机的 relay 单元名"这一事实、被演练脚本与探针 OBS-08 共用 ⇒ 原 DRILL_ 前缀与用途不相称(⚠️ 旧键名已不再被任何脚本读取,⛔ 别在脚本里写回旧名) ssh -p 22 bt-server systemctl cat dshs-relay
DRILL_MANAGER_UNIT dshs — 实测 47 的 Manager 单元名(演练只读它的 journal 判别器) 同上
DRILL_POLL_MS 500 ms 推导 演练轮询 journal 的间隔(≤ RELAY_FAILOVER_CHECK_MS 的 5×,保证不漏一次切换)。🆕 序⑨:2000 → 500 —— 原 2000 让读数带 0–2000 ms 系统性高估,使「是否超 RELAY_FAILOVER_DEADLINE_MS」两端都可能误判(序⑨ RC-4)⇒ 这是测量修正,不是调参。⚠️ 改前/改后的读数不可混比 —
DRILL_SAMPLE_N 5 次 推导 🆕 序⑨:overlay-failover-drill.cjs --sample 的默认采样轮数(口径 = 每轮都做完整归零:两台 relay start → restart dshs → 等新的 AUTH OK host=ops/manager → 杀入口 → 等新 [relay-switch]) node scripts/overlay-failover-drill.cjs --sample 5
DRILL_COOLDOWN_OBSERVE_MS 90000 ms 推导 幕 3 的「冷却期内不回跳」观察窗;⛔ 必须 < RELAY_FAILOVER_COOLDOWN_MS,否则判据不成立 —
DRILL_SWITCH_MATCH_106 106.54.21.172 — 实测 断言 [relay-switch] 的目标是不是 106(用目录里那个 host,⛔ 不是 ssh 别名 test106) curl -s https://ai1net.com/dshs-overlay/bootstrap
DRILL_KILLED_MATCH ai1net.com — 实测 幕 1 被杀入口的标识(= 目录 relays[] 首位的 host);脚本据此判定「Manager 当前通道是否就是将被杀的那台」——不在 ⇒ 记 SKIP 而不是假装 PASS 同上
DRILL_DETECT_BUDGET_MS 120000 ms 推导 幕 1/幕 2 的观察窗;⛔ 必须 ≥ 静默失效检测时延(半开检测 = 2.5 × HB_SEC,即 75 s)—— 首轮实测用 1× deadline(30 s) 必然漏判 —

§3 输入参数(清单 §三 点名的 5 类;散落点收口)

散落点计数(P10,⛔ 只计数不重读):调研_游戏网络特征…:19 处 / 千台全场景推演…:18 / 瓶颈落地方案…:9 / 游戏专项…:7 / 清单…:7 / 百台规模推演…:2 / 骨干层方案…:2 ⇒ 合计 64 处命中,正是"不可复算"的成因。

3.1 设备占比(分层)

键 值 单位 等级 来源定位 复算式
DEV_L1_PUBLIC_IP 10 台 估值 覆盖网络_百台规模推演_20260916.md:53 —
DEV_L2_HOLE_PUNCHABLE 30 台 估值 覆盖网络_百台规模推演_20260916.md:54 —
DEV_L3_RELAY_ONLY 45 台 估值 覆盖网络_百台规模推演_20260916.md:55(原文区间 40–55,取中值) (40 + 55) / 2
DEV_SHARE_L3 0.45 — 估值 覆盖网络_百台规模推演_20260916.md:119「L3 占 45%(v1 只算 15%)」+:132 45 / 100(口径 = 全网节点数的比例)
FLEET_TARGET 1000 台 估值 覆盖网络_千台全场景推演_20260916.md(标题与全文口径) —
FLEET_RELAY_DEMAND 450 台 推导 本表 §5 FLEET_TARGET × DEV_SHARE_L3 = 1000 × 0.45

3.2 打洞率

键 值 单位 等级 来源定位 复算式
HOLE_PUNCH_RATE_INDUSTRY 0.90–0.94 — 估值 覆盖网络_全球架构复盘_20260916.md:38(业内 UDP 打洞 ≈ 94%) —
HOLE_PUNCH_RATE_BY_CLASS 90% / 40% / 10% — 估值 方案规划方法_覆盖网络线提炼_20260916.md:45(三档标注实例) —
HOLE_PUNCH_RATE_LOCAL 分层(n=3 台):① 云机×云机(47↔106)—— 均为 L1 直连,不是打洞场景;② 国内家宽/办公 NAT(本机)↔ 47 / ↔ 106 —— 对端 → 本机方向 10/10 收包 = 可打洞;本机 → 云机方向取不到(云安全组拦 UDP 入站)。两对中两对可打洞 — 实测 本单 §8.4 E6 + overlay-holepunch.cjs --stun(⚠️ 首选路径的 47 UDP 观察器实测不可用:零收包 ⇒ 降级为第三方 STUN,见 §8.4 OBS-4D) 判定式=对(47,本机)、对(106,本机) 各「至少一个方向成功」⇒ 2/2;⚠️ 样本 n=3 对,云节点占 2/3,不代表家宽场景

3.3 每玩家带宽

键 值 单位 等级 来源定位 复算式
PER_PLAYER_BW_TEXT 0.5 KB/s 估值 覆盖网络_调研_游戏网络特征与群聊上限_20260916.md:15 —
PER_PLAYER_BW_BATTLE 2–5 KB/s 估值 同上 :15(另一口径 :43「单玩家 2–20 KB/s」) —
PER_PLAYER_BW_SIEGE 10–20 KB/s 估值 同上 :15 —
PER_PLAYER_BW_LOCAL 9.8(n=10 玩家档)/ 3.9(n=50 玩家档)—— 判据见来源列;聚合天花板 ≈ 200–350 KB/s KB/s 实测 本单 §8.4 E7 + overlay-wan.cjs --players(合成 200 B 消息 × 5/20/50 msg/s × 10/50 玩家 × 60 s,经 relay 真机间) 判据=p95 ≤ 2×p50 且丢包 = 0;⚠️ 原始丢包含尾部在途伪影(≈rate × RTT);扣掉后满足的最高档 = 10 玩家 @50 msg/s = 9.77 KB/s/玩家(50 玩家档最高 3.91)。边界:本值 = 传输层上限;游戏协议的真实需求仍是估值(PER_PLAYER_BW_TEXT/BATTLE/SIEGE)——⛔ 不得读成"实测出的游戏需求"

3.4 消息频率 / 扇出

键 值 单位 等级 来源定位 复算式
MSG_RATE_GLOBAL 50 msg/s 估值 覆盖网络_千台全场景推演_20260916.md:75 —
FANOUT_AVG 50 — 估值 同上 :75("均扇出 50") 50 × 50 = 2500 投递/秒
ROOM_FANOUT_LIMIT_100 100 人 估值 覆盖网络_调研_游戏网络特征与群聊上限_20260916.md:97(≤100 纯扇出即可) —
PRESENCE_FANOUT_1000ROOM 16700 次/秒 推导 覆盖网络_千台全场景推演_20260916.md:79 千人房 presence ≈ 1000 × 16.7 次/秒(原文直接给 16,700)

3.5 心跳 / 链路质量

键 值 单位 等级 来源定位 复算式
HB_SEC 15 s 实测(代码常量=运行真值) src/net/relay/server.ts:60 DEFAULT_HB_SEC = 15;运行期由 HELLO_ACK 下发(server.ts:816) grep -n DEFAULT_HB_SEC src/net/relay/server.ts
HB_SEC_DOC 20 s 估值(文档口径,与代码不一致) 覆盖网络_千台全场景推演_20260916.md:39「心跳间隔 20 s(沿用现有隧道自愈定时器)」 ⚠️ 该行属旧隧道时代口径 —— 现已换 relay(hbSec=15)⇒ 文档待更正,参数以 HB_SEC=15 为准
HALF_OPEN_MS 37500 ms 推导 src/net/relay/client.ts:602 max(3000, HB_SEC × 1000 × 2.5) = 15 × 1000 × 2.5
IDLE_TIMEOUT_MS 45000 ms 实测(代码常量) src/net/relay/server.ts:58 grep -n DEFAULT_IDLE_TIMEOUT_MS src/net/relay/server.ts
JITTER_LIMIT_MS 20 ms 估值(业界口径) 覆盖网络_调研_游戏网络特征与群聊上限_20260916.md:32+覆盖网络_千台全场景推演_20260916.md:90 — ⚠️ 序㉖ 起该键语义扩展:既当"达标限值"(序⑥ 原义),又当**"抖动劣化即切"的阈值**(`p95(
JITTER_LINK_MEASURED 3(判定值 = `p95( ΔRTT );另记 mdev = 1.37–1.38 ms、avg` = 148.3 ms、丢包 1.3–1.7%) ms 实测
RELAY_RTT_W106 336 → 保留数值,但加口径修正:该值是心跳往返(server.ts:810:测 RTT / 察觉半开 / 保 NAT 表项),含应用层处理与验签,⛔ ≠ 网络 RTT ms 实测(口径受限) 本单 §8.4 E5(b) 三方对比:ICMP 148 ms | TCP 握手 median 153 ms(min 138.5)| relay 心跳 344–376 ms ⇒ 差 2.3×,触发"加口径备注"判据 判据(S3(b)):relay rttMs 与 ICMP 差距 > 2× ⇒ 加备注 + 在 OBS 侧登记"relay rttMs 不得当链路 RTT 用"
RELAY_RTT_MANAGER 7 ms 实测 /status → sessions[hostId=manager].rttMs 同上
RELAY_TTFB_S 0.68 s 实测 本次 S0 P7a:47 → relay 回环口 → w-106 任一端口,%{time_starttransfer}(6 次样本 0.676–1.083) ssh -p 22 bt-server 'curl -s --http1.1 -o /dev/null -w "%{time_starttransfer}\n" http://127.0.0.1:44133/'
WAN_UP_BOUND_KBPS 192 ⇒ 已被上行实测取代(值作废,保留作历史) KB/s 实测(下界) 作废 原 S0 P7 样本被 405 提前截断(131072 B / 0.685 s,含 RTT)⇒ 只是下界,且下界偏低 64× 用 WAN_STEADY_THROUGHPUT 的上行值 12213 作准
WAN_STEADY_THROUGHPUT 352(106→47)/ 12213(47→106) —— 取绑定方向(对端→中继机,即"内容从 worker 上来"那条)= 352 KB/s 实测 本单 §8.4 E4 + overlay-wan.cjs --download|--upload(各 5 样本) 口径三要素:① 谁到谁 = 106 的 w-106p:19777 ↔ 47 本机 relay 回环端点;② 经 relay(非裸链路);③ 稳态段 30 s(弃前 3 s,爬升段字节单独计)。中位数 352(min 345.6 / max 352);上行侧 12213(min 11784 / max 12452)。⚠️ 非对称的成因 = 106(云轻量)公网出带宽封顶 ≈ 2.8 Mbps,实测三向互证:/status 会话计数 in=1.905 GB + 106 侧进程 rchar=1.905 GB + 下载恒定 165×64 KB/30 s
WAN_UP_BOUND_KBPS 192 ⇒ 已被上行实测取代(值作废,保留作历史) KB/s 实测(下界) 作废 原 S0 P7 样本被 405 提前截断(131072 B / 0.685 s,含 RTT)⇒ 只是下界,且下界偏低 64× 用 WAN_STEADY_THROUGHPUT 的上行值 12213 作准

序㉖ 新增:抖动观测与容量余量键(OBS-19 / OBS-20 的唯一取数来源 —— 代码侧 src/net/relay/jitter.ts jitterThresholds(),⛔ 模块内零魔数)

键 值 单位 等级 来源定位 复算式
JITTER_ENABLE 1 — 推导 序㉖:总开关(0 ⇒ 采样与排序全部失效、逐字回到改造前行为)—— 探针拿它当"装了但没生效"的判别器 src/net/relay/jitter.ts jitterThresholds()
JITTER_SAMPLE_MAX 32 个 推导 序㉖:每个 url 保留的 RTT 样本上限(环状丢弃最老)。32 × 心跳 15 s ≈ 8 min 滑窗 同上
JITTER_MIN_SAMPLES 3 个 推导 序㉖:参与排序 / 劣化判定所需的最小 |ΔRTT| 样本数。不足 ⇒ 该 url 判为未知(⛔ 不当 0 用 —— 否则"没测过"会被误排成"最稳") 同上
JITTER_HIST_MAX_MS 200 ms 推导 序㉖:直方图上界(≥ 它 落末桶)。取 10 × JITTER_LIMIT_MS ⇒ 超标样本全落末桶仍可计数 JITTER_LIMIT_MS × 10 = 20 × 10
JITTER_HIST_BUCKETS 8 个 推导 序㉖:直方图桶数(固定值;探针 OBS-19 拿它校验"口径一致",⛔ 不是实现细节) 同上
JITTER_SAMPLE_GAP_MS 20000 ms 推导 🔴 序㉖:同一份缓存读数的最小采样间隔 —— 必须 > 心跳周期 HB_SEC(15 s),否则同一个 RTT 值被反复记录 ⇒ 差分恒 0 ⇒ jitter 被系统性低估到 0(判据假绿)。⚠️ relay 侧(server.ts)不用它:它在 PONG 到达那一刻采样,本来就是新测量 HB_SEC × 1000 + 5 s = 15000 + 5000
RELAY_UTIL_MAX_PCT 70 % 推导 序㉖ · E4:利用率软门 —— used / max × 100 ≥ 它 ⇒ 拒绝新接入(⛔ 不驱逐任何在线节点)。与"骨干留 30%+ 余量"(B 档口径)对齐。⚠️ 0 ⇒ 软门关闭(逐字回到改造前:只有硬容量门 at-capacity)。🔴 与 refused(硬门)分开计为 utilRefused —— 混计就分不清"真装满了(要扩容)"与"为保余量提前拦(按设计工作)" 100 − 30(余量口径 = B 档 30%+)

3.6 presence(节点在线态 · 序⑲)—— 帧率类参数

键 值 单位 等级 来源定位 复算式
PRESENCE_BATCH_MS 1000 ms 实测(代码常量=运行真值) src/net/relay/server.ts:106 DEFAULT_PRESENCE_BATCH_MS = 1_000 grep -n DEFAULT_PRESENCE_BATCH_MS src/net/relay/server.ts
PRESENCE_GRACE_MS 10000 ms 实测(代码常量=运行真值) src/net/relay/server.ts:89 DEFAULT_PRESENCE_GRACE_MS = 10_000 grep -n DEFAULT_PRESENCE_GRACE_MS src/net/relay/server.ts
PRESENCE_OFFLINE_DEBOUNCE_MS 30000 ms 实测(代码常量=运行真值) src/net/relay/server.ts:97 DEFAULT_PRESENCE_OFFLINE_DEBOUNCE_MS = 30_000 grep -n DEFAULT_PRESENCE_OFFLINE_DEBOUNCE_MS src/net/relay/server.ts
PRESENCE_TTL_MS 45000 ms 推导(代码常量 × 心跳) 服务端 src/net/relay/server.ts:604;客户端保守档 src/net/relay/client.ts:329 DEFAULT_PRESENCE_TTL_MS = 45_000(两处同值,实测一致) HB_SEC × 1000 × DEFAULT_PRESENCE_TTL_FACTOR = 15 × 1000 × 3(因子 = 3 见 server.ts:115)
PRESENCE_SUB_MAX 0 个 实测(代码常量;0 = 不限) src/net/relay/server.ts:122 DEFAULT_PRESENCE_SUB_MAX = 0 grep -n DEFAULT_PRESENCE_SUB_MAX src/net/relay/server.ts
PRESENCE_GATE_WINDOW_MS 60000 ms 推导(= 12 × 轮询周期) 序㉑:OBS-16 的观测窗口 —— 必须 ≥ 4 × 轮询周期,否则"轮询还在跑"与"轮询停了"在计数上分不开 RELAY_STATUS_POLL_MS × 12 = 5000 × 12(轮询周期 = 代码常量 src/web/server.ts:76)
PRESENCE_GATE_HITS_MAX 1 次 推导 序㉑:门窗口内 /status 命中增量上限 = 探针自身那一次读数(窗口 = (第二次采样, 第三次采样])⇒ 多出来的每一次都算"Manager 还在拉" 探针一次采样 = 1 次读
CONTENT_BLOCK_SIZE 1048576 B 推导 序㉔:块级切分的块大小 —— 单份首屏合并脚本 11,363,655 B(S0 P1)⇒ 约 11 块/份;取 2 的幂便于手算复现。代码常量 src/net/relay/content/chunker.ts DEFAULT_BLOCK_SIZE 1024 × 1024
CONTENT_STORE_MAX_BYTES 67108864 B 推导 序㉔:块缓存上限 —— S0 P5:MEM_BUDGET_MB = 1002,块缓存另立预算(⛔ 不挤 relay 的 MEM_PER_HOST_MB),取 ≈ 6.4%;够整份装下 6 份 10.8 MB 包。env CONTENT_STORE_MAX_BYTES 可覆盖(relay 侧另有 DSHS_CONTENT_STORE_MAX_BYTES,见下条);代码缺省 store.ts DEFAULT_MAX_BYTES 64 × 1024 × 1024
CONTENT_GROUP local — 实测 序㉔:本节点在内容面上的组名(分组隔离 E5 的维度)—— 同 (网, 组) 才允许互相取块。env CONTENT_GROUP 可覆盖(relay 侧另有 DSHS_CONTENT_GROUP,见下) 装配层缺省 'local'(src/web/server.ts)
CONTENT_TIER_ORDER local,peer,edge,region,origin — 推导 序㉔:内容源优先级链(照抄 SCCM / DO 口径,交接单 §7)—— ⛔ 顺序是硬约束:越靠前越省带宽 chunker → source.ts DEFAULT_TIER_ORDER
CONTENT_TIER_HITS_MIN 1 次 推导 序㉔ · OBS-17:local + peer 两档命中计数之和的下界 —— 同组内 ≥ 1 次"不回源即取到"才算内容寻址真的在工作(0 ⇒ 全是回源 ⇒ E1 未达成) 探针两次 /status 的计数增量
DSHS_CONTENT_GROUP relay — 实测 序㉔ · relay 侧组名(src/net/relay/main.ts)—— ⚠️ 与平台侧 CONTENT_GROUP 同名不同进程:relay 是独立单元、读不了平台的 env ⇒ 双前缀。生产值在 drop-in dshs-relay.service.d/40-content-probe.conf(取 relay,与平台侧 local 刻意分开 ⇒ 避免"两个进程误以为在同一组") 缺省字面量 'local'(main.ts)
DSHS_CONTENT_STORE_MAX_BYTES 未设 B — 序㉔ · relay 侧块缓存上限(src/net/relay/main.ts);未设 ⇒ 走代码缺省 DEFAULT_MAX_BYTES(64 MiB),与平台侧同值 缺省 = store.ts DEFAULT_MAX_BYTES
DSHS_CONTENT_PROBE_BLOCK seq24-content-probe-block — 推导 序㉔ · 活性自证:relay 启动时把该文本当一个真实块写入内容寻址存储并立即走优先级链读回 ⇒ local 档命中 +1。⛔ 不是凑绿:块确实在存储里、确实被读出(store.hits 同步 +1),后续同 id 请求真能命中。🔴 它正是 OBS-17 判据③的输入源 —— 缺它则 relay 侧命中恒 0 ⇒ 活性判据必红(服务行为本身不变) 无(⛔ 不设 = 不投探针块);生产 drop-in = 40-content-probe.conf
DSHS_REHYDRATE_STAGGER_MS 500 ms 推导 序㉕:「逐步拉起」逐条认领之间的最小时间差 —— 防"重启后 N 个既有实例被同时处置"的启动风暴(E2 的可测阈值)。🔴 代码缺省,⛔ 47/106 的 env 里未设(D1 自证:grep -c REHYDRATE /etc/dshs*.env = 0)|代码位置 src/supervisor/orchestrator.ts rehydrateStaggerMs() 500(代码缺省)
DSHS_REHYDRATE_PROBE_MS 2000 ms 推导 序㉕:单条实例的 TCP 探活超时。🔴 判据是"端口不通",⛔ 不是"启动了却不认识" —— 端口在听 ⇒ 判有效实例、保留(与重启前稳态一致);连不上 ⇒ 才是档案 30 说的孤儿 ⇒ 按旧行为停掉。写成后者会把好实例一起杀掉。|代码位置 rehydrateProbeMs() / probeAdopted() 2000(代码缺省)

⚠️ 这五个键只驱动在线态事件(谁在线 / 何时改口),⛔ 与 RELAY_FAILOVER_* / HB_SEC / burst 无耦合 —— 序⑲ 全程一字未动(E11 的 D1 自证)。 ⚠️ PRESENCE_TTL_MS 是安全网(兜"漏掉 close 事件"那条路),⛔ 不是常规下线路径:常规下线走 PRESENCE_GRACE_MS + PRESENCE_OFFLINE_DEBOUNCE_MS = 40 s。 🔴 序㉑(P-1 修复)口径变更:PRESENCE_TTL_MS 不再当"订阅新鲜度"用 —— 客户端的门(presenceFresh())判据改成「订阅已建立 ∧ 链路活着」(任何入站帧,含心跳),上界 = max(本 TTL, 半开阈值)。理由:presence 是变化驱动的,稳态零帧是设计属性,拿"载荷年龄"当判据会把"没有变化"读成"没有数据"(实测降幅因此只有 1.10×)。本键值未改,只是判据收窄为"入站静默上界"的一员。 ⚠️ 口径来源 = D3(最终一致:允许 5–15 s 陈旧),⛔ 三个阈值均按代码常量取,不许改口径去凑判据(E5)。 🔴 值格必须纯数字(夹注 / 混写单位 ⇒ 探针 NaN ⇒ 假红,序⑫ 已踩)。

3.7 候选链观测(序㉗)—— E3「路径多样性」的可断言面

🔑 立项依据 = 序㉖ §8.1-⑦ 第 ④ 条登记的「E3 未取得机器断言面」;用户拍板 2026-09-17 23:1x「2、B」= 允许改 src/worker/relay-tunnel.ts / src/web/server.ts 以暴露候选数。 🔴 观测行格式(探针判据的锚点,⛔ 改它 = 破坏判据):[overlay-candidates] scope=<s> resolves=<n> count=<n> hosts=<n> source=<s> detail=<s> urls=<u|u> 🔴 值格必须纯数字(CAND_MIN / RELAY_CAND_OBS_MS);两个 unit 列表是字符串、逗号分隔、⛔ 不许带空格。

键 值 单位 等级 来源定位 复算式
CAND_OBS_PREFIX [overlay-candidates] — 实测 代码仓 src/worker/relay-tunnel.ts#CAND_OBS_PREFIX(全仓唯一写死处,⛔ 探针侧不硬编码) grep -n "CAND_OBS_PREFIX = " src/worker/relay-tunnel.ts
CAND_MIN 2 条 实测 🔴 用户口径「每连接候选数 ≥ 2」(2026-09-17 23:1x「2、B」)= OBS-21 的判据阈值 本行即真值 ⇒ ⛔ 不来自节点数推导(改了它 = 改用户口径)
RELAY_CAND_OBS_MS 300000 ms 推导 序㉗:观测行周期重发间隔(⛔ 只重发上次快照、零网络 I/O);0 ⇒ 不重发。取 300 s(与 DEFAULT_DIRECTORY_REFRESH_SECONDS 同量级 —— 观测粒度不必细于目录刷新) src/worker/relay-tunnel.ts#candidateObsMs() 默认值
CAND_OBS_UNITS_47 dshs=manager — 实测 47 上唯一的 relay 客户端单元(C1 Manager)。⚠️ 格式 unit=scope:探针按单元读 journalctl、再核对 scope 与这里一致(⛔ 不一致即 FAIL —— 那是接线错,不是观测错)。🔴 dshs-worker ⛔ 不在列:47 的 worker 没有 DSHS_RENDEZVOUS_URL(grep -c = 0)⇒ 它根本不建 RelayTunnel、不是 relay 客户端(w-47 的跨机面走 Manager 本机 LocalRendezvous)⇒ 列进去会读到"永远无观测行" = 假红(本序真机首轮已踩到并修正) ssh -p 22 bt-server "grep -c DSHS_RENDEZVOUS_URL /etc/dshs-worker.env"(期望 0)
CAND_OBS_UNITS_106 dshs-worker=worker — 实测 106 上一个 relay 客户端单元及其 scope(worker w-106) ssh -p 22 test106 "systemctl cat dshs-worker | grep -c DSHS_OVERLAY"

⚠️ CAND_OBS_UNITS_* 只列该机实际在跑的 relay 客户端单元 —— 多列一个不存在的单元 ⇒ 探针读到"该路径无观测行"⇒ 假红;少列一个 ⇒ 漏判一条连接路径(⛔ 两种都是判据污染)。


3.8 退出路径守卫(序㉘ → 单 A)—— 「退出不停实例」的可断言面

🔑 立项依据 = 序㉘ 单 A(候选 B,用户 2026-09-17 23:3x「选 B」):三处 teardown() 一起改 ⇒ 退出进程不再停实例。 🔴 本节的 6 个键只被探针读取 —— ⛔ 不写进任何 systemd drop-in / env(本单无新生产键)。 🔴 判据锚点 = 部署产物 lib/supervisor/{orchestrator,remote-spawner,leased-spawner}.js 里三处 teardown() 的 8 行窗口(≡ grep -A7 "async teardown")—— ⛔ 改守卫标记 = 破坏判据。 🔴 禁词格的值必须逗号分隔、⛔ 不许出现 |(| 是表格列分隔符,写进去会把整张表切开)。

键 值 单位 等级 来源定位 复算式
TEARDOWN_GUARD_MARKER guard: teardown-must-not-stop-instances — 实测 三处 teardown() 体内的守卫标记(探针侧 ⛔ 不硬编码) grep -c "guard: teardown-must-not-stop-instances" src/supervisor/orchestrator.ts src/supervisor/remote-spawner.ts src/supervisor/leased-spawner.ts ⇒ 3 处各 1
TEARDOWN_FORBIDDEN_TOKENS this.stop(,inner.stop(,killInstance(,/stop — 实测 OBS-22 的禁词表(字面量、⛔ 非正则 —— 避免 ` ()` 与列分隔符冲突);判据 = teardown 窗口内任一字面量命中即 FAIL
TEARDOWN_LIB_47 /opt/dshs/lib/supervisor — 实测 47 上真正在跑的 supervisor 产物目录(dshs 与 dshs-worker 同用) ssh -p 22 bt-server "systemctl cat dshs-worker | grep -o '/opt/dshs/lib'"
TEARDOWN_LIB_106 /opt/dshs-cluster/lib/supervisor — 实测 106 上真正在跑的 supervisor 产物目录 ssh -p 22 test106 "systemctl cat dshs-worker | grep -o '/opt/dshs-cluster/lib'"
TEARDOWN_UNIT_47 dshs-worker — 实测 47 上唯一会执行 LocalSpawner.teardown() 的单元。🔴 dshs ⛔ 不在列:47 是 DSHS_DEPLOY_MODE=cluster ⇒ Manager 构造的是 LeasedSpawner(RemoteSpawner)、根本不构造 LocalSpawner(因此不打 [rehydrate]、其 teardown() 在 47 上是死代码)—— 2026-09-18 序㉚ 实测:journalctl -u dshs | grep -c '[rehydrate]' = 0(全量历史) ssh -p 22 bt-server "systemctl show dshs -p Environment | grep -c DEPLOY_MODE=cluster"(期望 1)
TEARDOWN_UNIT_106 dshs-worker — 实测 106 上唯一会执行 LocalSpawner.teardown() 的单元 ssh -p 22 test106 "journalctl -u dshs-worker --no-pager | grep -c '\[rehydrate\]'"(期望 >0)

🔴 口径(2026-09-18 序㉚ 实测修正上游假设):「同一语义三份实现,逐台断言」的执行点是两台 dshs-worker,⛔ 不是 dshs(Manager)—— Manager 在 cluster 形态下走 RemoteSpawner,远端实例的寿命从来不受 Manager 退出影响。⇒ 判「退出路径不杀实例」必须逐台 worker 判。

3.9 组密钥加密(序㉘ → 单 B)—— 「中继看不到明文载荷」的可断言面

🔑 立项依据 = 用户 2026-09-17 23:2x 拍板「C 组密钥加密」(同组共享一把对称密钥 ⇒ 组内密文一致 ⇒ 按哈希共享块不退化,同时中继读不到明文)。产物 = 交接单_组密钥加密_20260918.md(§8 前前缀 149596460288ca1a5b7abddc490f7909)。 🔴 算法栈 = AES-256-GCM,与 src/crypto.ts 同源(⛔ 不引第二栈);确定性做法 = iv = HMAC(key,"iv"‖明文) 前 12 字节、k = HMAC(key,"k"‖iv)、AAD = "<network>|<group>|<epoch>"(⇒ 跨组 / 跨 epoch 在认证阶段就被拒)。 🔴 块 id 口径 = β′(密文哈希):blockId = sha256(密文) 前 32 hex —— 单内 §7.3 已判 α(明文哈希)「只有缺点 ⇒ 自己拍掉」。 🔴 密钥本体 ⛔ 不经网络、⛔ 不经 relay —— 只走既有 0600 落文件 + scp / drop-in 通道;中继侧只广播 (group, epoch, keyId) 三元组(载荷内不含密钥)。 🔴 值格必须纯数字;CONTENT_KEY_FILE 是路径字符串(探针按字符串读,⛔ 不参与数值比较)—— 凡"模式名 / 路径"性质的一律只作信息键。

键 值 单位 等级 来源定位 复算式
CONTENT_KEY_FILE /etc/dshs/content-group-key.json — 实测 组密钥落点(两机同名;与节点密钥同目录同权限惯例)。⚠️ 启用由 env 显式指定:relay 侧 DSHS_CONTENT_GROUP_KEY_FILE、平台侧 CONTENT_GROUP_KEY_FILE(⛔ 不设 ⇒ 不启用,行为逐字回到序㉔) ssh -p 22 bt-server "ls -l /etc/dshs/content-group-key.json"(须 -rw------- root root)
CONTENT_KEY_FILE_MODE 600 — 实测 🔴 密钥文件必须的权限(stat -c %a 原文比对,非"至少")。本线史上有"属主/权限被改 ⇒ 实例全崩"的先例(单 B §10 未验证项 4) ssh -p 22 bt-server "stat -c %a /etc/dshs/content-group-key.json"(期望 600)
CONTENT_EPOCH_GRACE_MS 86400000 ms 实测 双 epoch 过渡窗口上界 = 24 h。超窗口的历史 epoch 只解不写 → 不再解(epochExpired +1 并点名)。env:DSHS_CONTENT_EPOCH_GRACE_MS / CONTENT_EPOCH_GRACE_MS(⛔ 生产未设 ⇒ 用本值 = 代码缺省 crypto.ts#DEFAULT_EPOCH_GRACE_MS)。🔬 序㉛ 实测三档混存夹具(20 块 = 写 epoch 4 + 历史 1/2/3,retiredAt 错开在 T0 / T0+2h / T0+12h;时钟注入 now) ⇒ 旧密文可解比例是阶梯衰减(不是一刀切),全程归零时刻 = 最晚 retiredAt + 本值:小档 60,000 ms ⇒ 24 h 归零|本档 86,400,000 ms ⇒ 48 h 归零|大档 604,800,000 ms(7 d)⇒ 8 d 归零;窗口内 epochExpired=0,超窗后逐 epoch 点名且 decryptRejected 同步 +1。⇒ 选值口径 = "轮换后还允许旧密文被解多久":太短 ⇒ 尚未换完钥的节点掉线;太长 ⇒ 旧密钥留太久(安全)。⚠️ 轮换时点必须约在发布窗口(单 §9-8) grep -n "DEFAULT_EPOCH_GRACE_MS = " src/net/relay/content/crypto.ts(数值=代码缺省;曲线=_tmp_seq32/p04-epoch.txt)
CONTENT_CRYPTO_DET_MIN 1 次 实测 OBS-23 判据② 的 F1 阈值:启动自证至少完成 1 次"同明文两次加密 ⇒ 比字节"(detChecks ≥ 本值 且 detMismatches = 0) 本行即真值(⛔ 不来自推导)
CONTENT_CRYPTO_DECRYPT_MIN 1 次 实测 OBS-23 判据② 的 F2 阈值:至少 1 次"加密 → 入库 → 经优先级链取回 → 解密"闭环成功(decrypts ≥ 本值 且 decryptRejected = 0) 本行即真值(⛔ 不来自推导)
CONTENT_CRYPTO_SCAN_MIN 1 次 实测 OBS-23 判据③ 的 F4① 阈值:至少 1 次字节级明文标记扫描(plainScans ≥ 本值 且 plainLeaks = 0)。⚠️ 只判 ①("没扫到")是假绿风险 ⇒ 反向证明在夹具 OFF 段(--content-crypto-fixture)—— 那条腿必须命中 ≥ 1 本行即真值(⛔ 不来自推导)

🔴 本节的 6 个键只被探针读取 —— CONTENT_KEY_FILE / CONTENT_KEY_FILE_MODE / CONTENT_CRYPTO_* ⛔ 不写进任何 drop-in / env;只有 DSHS_CONTENT_GROUP_KEY_FILE(relay)与 DSHS_CONTENT_EPOCH_GRACE_MS(可选)是生产 env(后者未设 ⇒ 用代码缺省)。 ⚠️ OBS-23 的对抗面(⛔ 别用它们凑绿):epochExpired 稳态须 0;decryptRejected 稳态须 0(持续增长 ⇒ 命中单 B §9-6 回头条件,停下报告,⛔ 不许"重启一次看看")。


3.10 节点一键加入与分组准入(序㊱ · P1)—— 「一条命令接入 + 白名单派生」的可断言面

🔑 立项依据 = 用户 2026-09-18 09:07 原话「b 要实现开启一台结点服务器,就能连上覆盖网络,这样做这个网络才有价值,结点可以设置分组只允许那些结点加入,既可以成为大覆盖网络也可以建立独立覆盖网络」。产物 = 交接单_节点一键加入与分组准入_20260918.md(归档号 131;§8 前前缀 085e28b56a08aa1f01999bdb3e238911)。 🔴 本节新增 0 个生产 env —— 下面 8 个键只被探针 / 控制面 CLI 读取,⛔ 不写进任何 drop-in / env(与 §3.8 / §3.9 同纪律)。🔴 P1 的硬门 = 零新增暴露面:⛔ 不改 nft·nginx、⛔ 不开新监听口、⛔ 不动既有 50-overlay-dialers.conf。 🔴 手写 drop-in 是应急通道,⛔ 不删 —— NODES_EMERGENCY_DROPIN 那一行就是它;派生(derive --apply)只多一条产生白名单的路,⛔ 不取代它。 🔴 值格必须纯数字或纯路径(夹注 ⇒ NaN ⇒ 假红);⛔ 值内不得含 |、*、反引号。

键 值 单位 等级 来源定位 复算式
NODES_REGISTRY_DIR /var/lib/dshs/overlay — 实测 控制面权威注册表的目录(用户既有口径「归属 / 租约 / 骨干资格只能控制面写」)。⚠️ 属状态、⛔ 不是配置 ⇒ 放 /var/lib/dshs/(与 users/ 同族),⛔ 不放 /etc/ ssh -p 22 bt-server "ls -ld /var/lib/dshs/overlay"
NODES_REGISTRY_FILE /var/lib/dshs/overlay/nodes.json — 实测 注册表本体(键 = 逻辑名 <network>/<hostId>,与 relay 会话表同口径)。⛔ 只有控制面写(双写 = 脑裂)。🔴 控制面 CLI 覆盖本路径的键 = --registry(⛔ 不是 --file —— 那是 apply 的申请单入参) ssh -p 22 bt-server "cat /var/lib/dshs/overlay/nodes.json"
NODES_CONSUMED_DIR /var/lib/dshs/overlay/consumed — 实测 一次性台账目录(每张邀请一个 <nonce>.used)。🔴 占位走 O_CREAT|O_EXCL(内核原子)⇒ 两台机器同时用同一张邀请恰好一台成功(JOIN 的 J1 判据) ssh -p 22 bt-server "ls /var/lib/dshs/overlay/consumed | wc -l"
NODES_SELFCHECK_FILE /var/lib/dshs/overlay/selfcheck.json — 实测 overlay-node-admit.cjs selfcheck --json --out <本路径> 的读数(OBS-25 的数据源)。⛔ 由人/棒显式跑,探针只读 ⇒ 无副作用(⛔ 探针不代跑自检) node scripts/overlay-node-admit.cjs selfcheck --json
NODES_EMERGENCY_DROPIN /etc/systemd/system/dshs-relay.service.d/50-overlay-dialers.conf — 实测 🔴 手写白名单 = 应急通道(控制面不可用时照旧生效)。派生落盘写同路径同格式文件 ⇒ 两者可互相替换、语义等价(⛔ 不删这一个) ssh -p 22 bt-server "ls -l /etc/systemd/system/dshs-relay.service.d/"
INVITE_TTL_MIN 30 min 实测(= CLI 缺省) 邀请凭据的有效期缺省。短窗 ⇒ 凭据泄露窗口小;⚠️ 要覆盖「签发 → 节点开机 → 跑 join」的全过程 本行即真值(⛔ 不来自推导)
JOIN_STEPS_MIN 4 步 实测 OBS-25 判据①:join 编排的四步必须全在且全绿(verify-invite → node-key → local-config → register)。⛔ 少于 4 = 有一步被静默跳过 grep -c "verify-invite|node-key|local-config|register" src/net/relay/join.ts
JOIN_NAMED_FAILURES_MIN 4 条 实测 OBS-25 判据④:selfcheck 的具名负腿条数下限(坏签名 / 过期 / 错网 / 无受信签名者;实际 5 条 = 再含"没给通道") node scripts/overlay-node-admit.cjs selfcheck --json | grep -c '"leg"'

⚠️ 一条必须写明的口径:NODES_REGISTRY_FILE 不存在 ⇒ OBS-24 取 SKIP + 留痕("这套准入还没开始用"是合法状态);而存在却读不出来 / 解析不了 ⇒ FAIL 并点名。两者不可混 —— 本线「"没装"与"没采到"必须可分」的又一次落地。 ⚠️ OBS-25 的私钥权限腿只在 Linux 可判:Windows 的 stat.mode 不支持 Unix 权限(恒 666/444)⇒ 读数里 nodeKeyModeOk: null = 本平台不可判,探针留痕但不计绿,⛔ 不许当 PASS;真机腿 = 在 47 上跑 selfcheck 取 600。 ⚠️ 一条真机实测教训(序㊱ 首轮 · 键名混用):控制面 CLI 的 --file 已被 apply 占用(申请单入参),⛔ 不能兼作注册表文件的覆盖键 —— 否则 apply --file <申请单> 会把注册表文件也指到申请单 ⇒ 收单在第 4 步(loadReg)炸成「注册表 …/app.json 形状非法」,而前 3 步副作用已发生(nonce 已被原子占位)⇒ 对外表现为「收单失败 + 同一张邀请再也用不了」。注册表覆盖键 = --registry(两键分离);判据 = test/overlay-join.test.mjs E10=CLI 级真回环(🔴 调库式的 E9 抓不到这一类参数解析缺陷)。

3.11 直连(打洞)与 P2P(序㊵ · P2/S5)—— 「用户可设置 + 默认开 + 提示 + 乙」的可断言面

🔑 立项依据 = 用户 2026-09-18 12:22 原话「1 用户可设置,默认开启提示用户 2 乙」。产物 = 交接单_覆盖网络直连与P2P_20260918.md(归档号 132;§8 前前缀 8925c2460810c6dc5cdbc075b6cdaed2)。 🔴 本节新增 1 个生产 env 候选 —— 只有 DIRECT_ENV_KEY(DSHS_OVERLAY_DIRECT),且它缺省即可用(缺省 = 开)⇒ 无需写进任何 drop-in;其余键只被直连 CLI / 探针读取。 🔴 值格必须纯数字或纯路径(夹注 ⇒ NaN ⇒ 假红);⛔ 值内不得含 |、*、反引号。 🔴 UDP 端口区间与 LISTEN_ALLOWED_RANGES(TCP)不同族 ⇒ ⛔ 别把 21100–21116 加进那个键(探针的 OBS-11 走 ss -lntp,加进去会让"允许集"凭空多出永远不存在的 TCP 口)。

键 值 单位 等级 来源定位 复算式
DIRECT_ENV_KEY DSHS_OVERLAY_DIRECT — 实测 直连总开关的 env 键名。⚠️ 与 CONTENT_GROUP_KEY_FILE 不同:本键缺省即可用(缺省 = 开)⇒ 不写进任何 drop-in 也生效;取值非法(既不在开集也不在关集)⇒ 判据取 null(⛔ 不静默当开/当关) grep -rn "DSHS_OVERLAY_DIRECT" src/net/relay/direct/index.ts
DIRECT_DEFAULT_ENABLED 1 布尔 实测 用户口径②「默认开启」的落地值:1 = 开(⛔ 不是"缺省关、用户主动开")。源码真值 = direct/index.ts#DEFAULT_DIRECT_ENABLED grep -n "DEFAULT_DIRECT_ENABLED = " src/net/relay/direct/index.ts
DIRECT_OFF_VALUES 0,false,off,no — 实测 显式关的取值集(大小写不敏感);开集 = 1,true,on,yes。不在此两集且非空 ⇒ FAIL 并点名(⛔ 不许静默取缺省) 本行即真值
PUNCH_PORT_BASE 21100 端口 实测 打洞 UDP socket 的端口基址(沿用序⑥ S4 观察器用过的 21100 段 ⇒ ⛔ 不新造魔数)。⚠️ UDP,与 LISTEN_ALLOWED_RANGES(TCP)不同族 grep -n "PUNCH_PORT_BASE = " src/net/relay/direct/punch.ts
PUNCH_PORT_SPAN 16 个 实测 打洞端口区间跨度 ⇒ [21100, 21116)(同一台机可能有多个并发探测) 本行即真值
PUNCH_DEADLINE_MS 3000 ms 实测 单次打洞探测的收包窗口;窗内收不到对端的包 ⇒ 判死(⛔ 不无限重试)。⚠️ 真机腿的"对端"须是有固定公网出口的节点(乙-1) 本行即真值
DIRECT_COOLDOWN_MS 300000 ms 实测 判死后该候选的冷却(5 min)。🔴 必须 > 0 —— 0 会让"判死"退化成每次重试都真打一遍(重试风暴);本线已把 RELAY_FAILOVER_COOLDOWN_MS=0 列为硬禁令 ⇒ 这里是构造期断言(DirectCooldown 收 <= 0 直接抛) 本行即真值
DIRECT_PUNCH_MIN_OK 1 次 实测 OBS-28 判据① 的判别器阈值:punchOk ≥ 本值(打洞成功路径必须真的发生过,⛔ 不许"看起来能打"当绿) 本行即真值
DIRECT_CAND_ACCEPT_MIN 2 条 实测 OBS-27 判据①:同网 + 白名单内的候选必须被接受 ≥ 本值 本行即真值
DIRECT_CAND_REJECT_MIN 3 条 实测 OBS-27 判据②:必须被显式拒绝的负腿条数下限(自检实跑 9 条:跨网 / 替第三人申报 / 白名单外 / 本机不在白名单 / 坏形状 / 夹带凭据字段 / 主机名当地址 / 地址超限 / 过期),且每条都要带具名 reason node scripts/overlay-direct-probe.cjs selfcheck --json | grep -c '"reason"'
DIRECT_HINT_MIN_PARTS 3 段 实测 OBS-26 判据③:提示文案必须含的三段(①谁会连进来 ②怎么关 ③关掉不影响什么)。⛔ 只写"已启用直连"不算 本行即真值
DIRECT_SELFCHECK_FILE /var/lib/dshs/overlay/direct-selfcheck.json — 实测 overlay-direct-probe.cjs selfcheck --json --out <本路径> 的读数(OBS-26/27/28 的共同数据源)。⛔ 由人/棒显式跑,探针只读(⛔ 探针不代跑自检,也⛔ 不 ssh 去开 UDP 口) node scripts/overlay-direct-probe.cjs selfcheck --json

⚠️ 一条必须写明的口径:DIRECT_SELFCHECK_FILE 不存在 ⇒ OBS-26/27/28 三条一起 SKIP + 留痕("真机还没跑过直连自检");存在却读不出来 / 解析不了 ⇒ 三条一起 FAIL 并点名。⛔ 两者不可混(本线「"没装"与"没采到"必须可分」)。 ⚠️ OBS-26/27/28 都没有"缺省不启用"档(与 OBS-23 不同):直连缺省即开 ⇒ 只有 PASS/FAIL,⛔ 不许出现"既非 SKIP 也非 FAIL"的静默绿。 🔴 准入判定必须复用既有白名单:network.ts#isAllowedDialer 是唯一出口,它的语义与 server.ts 的两处闸门(注册闸门 :1182、DIAL 闸门 :1491)逐字等价;test/overlay-direct.test.mjs#T4 是这条的机器守卫(读 server.ts 源码核对 + 断言直连模块里⛔ 不出现 dialers.get()。 🔴 打洞只用 Node 内建 dgram:OBS-28 顺带断言 lib/net/relay/direct/*.js 的模块说明符只有 node:dgram 与同模块内相对路径(⛔ 无第三方依赖、⛔ 无内核驱动、⛔ 无虚拟网卡)。

§4 代码已固化常量(实测=直接可 grep 的源码真值)

键 值 单位 等级 来源定位 复算式
RELAY_PORT 20080 — 实测 src/net/relay/server.ts:52+单位文件 ExecStart grep -n DEFAULT_RELAY_PORT src/net/relay/server.ts
RELAY_BIND 127.0.0.1 — 实测 主单元 ExecStart(无 --host ⇒ 默认回环) ssh -p 22 bt-server "ss -lntp | grep 20080"
RELAY_INSTANCE_BASE 19000 — 实测 主单元 ExecStart --base 19000 同上
RELAY_INSTANCE_SPAN 3000 — 实测 主单元 ExecStart --span 3000 ⇒ 可声明窗口 [19000, 22000) 同上
AUTH_DEADLINE_MS 5000 ms 实测 src/net/relay/server.ts:54 grep
AUTH_WINDOW_MS 60000 ms 实测 src/net/relay/server.ts:56 grep
CAPACITY_RETRY_AFTER_MS 5000 ms 实测 src/net/relay/server.ts:63 DEFAULT_CAPACITY_RETRY_AFTER_MS grep
SHUTDOWN_GRACE_MS 300 ms 实测 src/net/relay/server.ts:69 grep
MAX_STREAMS_PER_PORT 64 条 实测 src/net/relay/server.ts:71 grep
QUEUE_MAX_BYTES 1048576 B 实测 src/net/relay/server.ts:73(1 << 20) grep
WS_PEER_HIGH_WATER 262144 B 实测 src/net/relay/server.ts:76(256 * 1024) grep
CLIENT_HIGH_WATER 262144 B 实测 src/net/relay/client.ts:230 grep
CLIENT_QUEUE_MAX 1048576 B 实测 src/net/relay/client.ts:229 grep
DIRECTORY_REFRESH_SECONDS 300 s 实测 src/net/relay/directory.ts:60 grep
DIRECTORY_FETCH_TIMEOUT_MS 5000 ms 实测 src/net/relay/directory.ts:521 grep
DIRECTORY_MAX_ENTRIES 8 条 实测 src/net/relay/directory.ts:63 MAX_ENTRIES grep
DIRECTORY_MAX_ENTRY_LEN 512 字符 实测 src/net/relay/directory.ts:64 MAX_ENTRY_LEN grep
DIALER_POOL 64 口 实测 src/net/relay/dialer.ts:55 DEFAULT_POOL+/etc/dshs.env DSHS_RELAY_DIAL_POOL grep+ssh -p 22 bt-server 'grep DIAL_POOL /etc/dshs.env'
DIAL_PORT_BASE 25000 — 实测 /etc/dshs.env DSHS_RELAY_DIAL_PORT_BASE 同上
DIAL_PORT_SPAN 1000 — 实测 /etc/dshs.env DSHS_RELAY_DIAL_PORT_SPAN(⚠️ 实绑只到 base+POOL) 同上
DIAL_POOL_BOUND 25000–25063 — 实测 47 上 ss -lntp(64 口全绑)+journal [relay-dialer] 本机落点池就绪:64 个口 ssh -p 22 bt-server "ss -lntp | grep -c 250"
W47_AGENT_PORT 19100 — 实测 /etc/dshs.env DSHS_CLUSTER_AGENT_URL=http://127.0.0.1:19100。🆕 序㊲ 起被探针引用(OBS-09 的派生用它把 endpoints[] 切成 agent 面 / 实例面) ssh -p 22 bt-server 'grep AGENT_URL /etc/dshs.env'
LOCAL_INSTANCE_PORT 20000 — 实测 47 上 ss -lntp(127.0.0.1:20000)。⚠️ 序㊲ 起探针⛔ 不再引用(旧 OBS-09 拿它当"本机取样点";新口径只从 /status.endpoints[] 派生 —— 47 的 worker 不是 relay 客户端 ⇒ 本机实例面结构性不在端点表里)⇒ 本键只作快照 / 对账 ssh -p 22 bt-server "ss -lntp | grep 20000"
PEER_AGENT_PORT 19000 — 实测 /status → endpoints[]/online[](对端 w-106 的 ports=19000/21001;⚠️ 实例那一半会漂移 —— 见下一行的「实测替换」注)。🆕 序㊲ 起被探针引用(同 W47_AGENT_PORT:agent 面是配置常量、⛔ 不随实例替换漂移 ⇒ 适合当切分依据) ssh -p 22 bt-server 'curl -s 127.0.0.1:20080/status'
PEER_INSTANCE_PORT 21001 — 实测 🔁 实测替换 21000 → 21001(2026-09-18 10:2x · 收口复核棒现测;curl -s 127.0.0.1:20080/status ⇒ endpoints[] = w-106 port=19000 + w-106 port=21001,两条都 online=true)。🔴 漂移机理(本棒取证):实例端口由 spawn.ts#findFreePortInRange 从 DSHS_INSTANCE_PORT_BASE(=21000) 上扫取第一个空闲口;「访问时自然替换」在旧 scope 端口尚未释放时发起 ⇒ 落点顺延到 21001(旧 scope 收掉后再替换则回落 21000)⇒ 本值只是"某次现测"的快照,⛔ 不是稳定常量。🔴 序㊲ 起探针⛔ 不再引用本键(OBS-09 的在册判据已改为从 /status.endpoints[] 派生,正是为了不再被这个漂移值坑)⇒ 本键降级为快照 / 对账用 同上(ssh -p 22 test106 "ss -lntpH | grep 2100")
RELAY_FAILOVER_MIN_ATTEMPTS 3 次 实测 src/net/relay/switcher.ts#relayFailoverThresholds(env 可覆写;置 0 = 总开关关闭) grep -n RELAY_FAILOVER_MIN_ATTEMPTS src/net/relay/switcher.ts
RELAY_FAILOVER_GRACE_MS 15000 ms 实测 同上(与 MIN_ATTEMPTS 取或:106 侧首连窗口长,只看次数会误切) grep
RELAY_FAILOVER_COOLDOWN_MS 300000 ms 实测 同上(与 DIRECTORY_REFRESH_SECONDS 对齐;冷却期内不回跳) grep
RELAY_FAILOVER_DEADLINE_MS 30000 ms 实测 同上(验收判据:从不健康到切换完成的允许上限) grep
RELAY_FAILOVER_CHECK_MS 2000 ms 实测 同上(健康巡检周期;独立于目录刷新周期) grep
RELAY_FAILOVER_UP_TIMEOUT_MS 12000 ms 实测 同上(只用于换址:新通道必须真到 up 才算「建起来了」)。⚠️ 序⑨:死候选提前失败后它不再是恒等代价(实测死候选 ≈ 0.1–1 s 就返回,慢候选仍享受完整 12 s) grep
RELAY_GRACEFUL_BURST_MS 15000 ms 🆕 实测 序⑨ 参数表化:client.ts 的 gracefulBurstMs("计划内下线 ≠ 故障"的快速重试窗口)原先是全仓唯一一个不可配的时延常量(只有 ?? 15_000 一处),现可经 env 覆写;⛔ 默认值语义逐字不变。⚠️ 它就是检测段 15.0 s 地板的真身:窗口内 attempts 恒为 0 ⇒ unhealthy() 只能靠 graceMs 成立(序⑨ §1.2-RC-2)⇒ 改它 = 改"计划内重启不触发切流"的窗口,先回写交接单(D3) grep -n gracefulBurstMsDefault src/net/relay/client.ts
RELAY_FAILOVER_EXEMPT 1 — 实测 同上(🆕 序⑧:一跳豁免总开关,1=开 / 0=关;RELAY_FAILOVER_MIN_ATTEMPTS=0 是另一层,⛔ 别混) grep -n RELAY_FAILOVER_EXEMPT src/net/relay/switcher.ts
DRILL_COOLDOWN_MS 90000 ms 推导 🆕 序⑧:演练期冷却覆盖值(经 dshs.service.d/zz-drill-override.conf 注入)——⛔ 生产默认恒为 RELAY_FAILOVER_COOLDOWN_MS=300000。取值口径:必须 ① ≫ 静默失效检测时延(实测 ≈ 29 s,否则"冷却未到期"判据根本来不及观测)② ≪ DIRECTORY_REFRESH_SECONDS(300 s)(让冷却先过期、目录巡检后到) —
DRILL_NO_SWITCH_OBSERVE_MS 45000 ms 推导 🆕 序⑧:幕 4b 的**"预期不切换"观察窗**;⛔ 必须 ≥ 失效检测时延(否则 D6 现场还没形成)且 < DRILL_COOLDOWN_MS(否则会跨过冷却期满、把"自然回归"误判成"切了") —

🆕 序⑦ 新增 5 键(中继失败切流):默认值 = 源码真值(可直接 grep); ⛔ 值格必须纯数字(夹注 ⇒ NaN ⇒ 假红,见 §8.8-2 教训); 🔑 回滚开关 = RELAY_FAILOVER_MIN_ATTEMPTS=0 ⇒ 监管器永不触发,行为回到「原地退避重试」的现状(§7 回滚第 1 层)。

🆕 序⑧ 新增 3 键(切流冷却语义拆分):RELAY_FAILOVER_EXEMPT(代码侧,默认 1)+ DRILL_COOLDOWN_MS / DRILL_NO_SWITCH_OBSERVE_MS(只在演练脚本里用)。 🔑 本单首选回滚点 = RELAY_FAILOVER_EXEMPT=0 ⇒ 只关掉豁免、序⑦ 的切流能力全部保留; ⛔ 它不是 MIN_ATTEMPTS 的同义词(后者关整个监管器)。 ⚠️ 演练期改冷却只能经 DRILL_COOLDOWN_MS 注入 drop-in;⛔ RELAY_FAILOVER_COOLDOWN_MS 的代码默认值不动(D9)。

⚠️ 键名口径:LOCAL_* = 中继机(47)本机的实例/代理口;PEER_* = 对端节点(今天= w-106)的等价口。 (键名里不带 106 是为了让 overlay-probe.cjs 的零数字纪律成立 —— 见交接单 §6 E6。)


§5 本单新增:45% 口径填值(443 单 §8.8 留下的空位)

443 单 §8.8 登记行原文:「兜底启用后,relay 容量须按 45% 的节点走中继核算(异构纪律,⛔ 不是同构的 15%)」。 口径落点(按交接单 §4.1-3):填单台中继的 --max-hosts,⛔ 不填"全网 45%" —— 后者只作校验用。

5.1 输入(全部来自本表其它行,⛔ 无外部魔数)

键 值 单位 等级 来源定位 复算式
MEM_TOTAL_MB 1870 MB 实测 S0 P6:free -m 第 1 行 ssh -p 22 bt-server 'free -m | head -2'
MEM_AVAILABLE_MB 1002 MB 实测 S0 P6:free -m 的 available 列 同上
MEM_BUDGET_MB 1002 MB 推导 本表 = MEM_AVAILABLE_MB(47 同时跑 PG+Manager+nginx,故只取 available,⛔ 不用 total)
CPU_CORES 2 核 实测 S0 P6:nproc ssh -p 22 bt-server nproc
FD_LIMIT 262144 个 实测 S0:/proc/<relay pid>/limits Max open files ssh -p 22 bt-server 'cat /proc/$(systemctl show -p MainPID --value dshs-relay)/limits | grep "open files"'
RELAY_RSS_KB 72888 KB 实测 S0:ps -o rss= -p <relay pid>(used=2 时,含 Node 基座) ssh -p 22 bt-server 'ps -o rss= -p $(systemctl show -p MainPID --value dshs-relay)'
MEM_PER_HOST_MB 0.06 MB/台 实测(原为推导 2) 本单 §8.4 E8 + relay-mem-calibrate.mjs:本机独立 relay 实例 + 同进程合成 client N=2/10/25/50/100/150,每点 7 次采样取中位数 ⇒ 线性回归 RSS(N) = 48078 + 46.7·N KB,R² = 0.942(≥ 0.9 ⇒ 有效) 46.7 KB/台 × 1.2 余量 = 56.0 KB = 0.0547 MB ⇒ ceil 到 0.01 MB 位 = 0.06。⚠️ 原推导 2 MB 高估 36×:它把 per-stream 的 256 KB 高水位算进了 per-host;本值是空闲会话口径,带流量的 per-stream 开销未测(登记于 §9)
FD_PER_HOST 4 个/台 推导 1 条 WS + 每声明端口 1 个回环 listener + 2 个临时 ⇒ 上界 4 保守取上界
DESIGN_MARGIN 0.45 — 估值 443 单 §8.8 原文(异构纪律;⛔ 非同构 15%) —

5.2 推导式(⛔ 可独立手算,E2 判据)

C_MEM       = floor(MEM_BUDGET_MB / MEM_PER_HOST_MB)   = floor(1002 / 0.06)    = 16700     # ← 序⑥ S7 重算(原 floor(1002/2)=501)
C_FD        = floor(FD_LIMIT / FD_PER_HOST)            = floor(262144 / 4)    = 65536
C_RELAY     = min(C_MEM, C_FD)                          = 16700      # 带宽仍不参与取 min,见 §5.4
RELAY_MAX_HOSTS = floor(C_RELAY × DESIGN_MARGIN)        = floor(16700 × 0.45)  = 7515      # ← 原 225
键 值 单位 等级 来源定位 复算式
C_MEM 16700 台 推导 本表 floor(1002 / 0.06)
C_FD 65536 台 推导 本表 floor(262144 / 4)
C_RELAY 16700 台 推导 本表 min(16700, 65536)
RELAY_MAX_HOSTS 7515 台 推导 本表 floor(16700 × 0.45)(⚠️ 本单元格必须是纯数字 —— 探针 KEY_RE/cleanValue 不认识"(原 225)"这类夹注,会把整格当值 ⇒ NaN ⇒ OBS-02 假红;旧值 225 记在此列)。🔴 🔴 序⑥ S7 回头条件已触发并执行(2026-09-17):MEM_PER_HOST_MB 2 → 0.06、WAN_STEADY_THROUGHPUT 待测 → 352/12213 ⇒ 已重下发 47 与 106 两台的 capacity.conf(--max-hosts 7515),OBS-02 复验 PASS

校验(三条,全部满足才生效)—— 序⑥ S7 重跑记录

  • ① 防自锁:RELAY_MAX_HOSTS > used × 4 ⇒ 7515 > 4 × 4 = 16 ✅(验收时 used = 4)
  • ② 设计余量自洽:7515 / 16700 = 45.0% ≈ DESIGN_MARGIN ✅
  • ③ 千台需求校验:FLEET_RELAY_DEMAND = 450 ≤ RELAY_MAX_HOSTS = 7515(原为 450 > 225)⇒ 🔴 结论变更:"容量上必须 ≥2 台中继"这条理由随实测消失(单台容量已足够)。2 台的依据改为 ① 清单 §3 关键决定「有公网 IP 的节点自动升格中继候选」② 跨机真容灾(106 已升格,见 §8 ④)。⚠️ "零余量"这一旧事实不再成立,代之以下面那条:绑定约束从"内存"搬到了"带宽/时延"(见 §5.4)。

5.3 --max-hosts 的分母口径(⛔ 必须写清,否则数字会被误读)

--max-hosts 是每台中继的在册会话(host)数上限,它不是"全网 45%",分母是这一台中继;FLEET_RELAY_DEMAND(450)只是校验上界。今天生产上只有 1 台中继(47)⇒ 本值直接生效。

5.4 为什么带宽不参与 min(⛔ 不是漏了)—— 序⑥ S7 用实测重判

  • 判据(S7-2 原文):若新实测吞吐使"225 台(今为 7515 台)的控制面 + 实例面流量"逼近实测吞吐 ⇒ 带宽进 min。
  • 控制面(用实测值重算):1 / HB_SEC = 0.067 次/秒/台 × 7515 台 ≈ 504 次/秒;每台每秒控制面流量 ≈ 100 / HB_SEC = 6.7 B/s ⇒ 7515 台 ≈ 50 KB/s,对比绑定方向实测 352 KB/s(余量 7×)⇒ 控制面不绑定。
  • 实例面:不是"稳态负载"而是"单次传输",正确的判据是时延不是容量 —— 单次 46.3 MB 冷启动 ÷ 352 KB/s ≈ 135 s(原推算按 192 KB/s 是 247 s,按 22 KB/s 旧值则是 36 min)。⇒ ⛔ 仍不进 min,但登记为实例面单次传输的时延上界,并给出唯一的改进方向(提升对端公网出带宽,而非加中继)。
  • ⚠️ 本条的边界:352 KB/s 是 106(云轻量)出带宽封顶造成的,不是 relay 栈上限(上行方向实测 12213 KB/s 证明栈本身没到这个量级)。

5.5 /status.capacity 的语义纠正(P2 实测 vs 交接单期望)

事实 原文
max = 0 时只有 {max, used},没有 free src/net/relay/server.ts:502-506:this.maxHosts > 0 ? {max, used, free} : {max, used};实测 /status = "capacity": {"max": 0, "used": 2}
free 只有在 max > 0 时出现 同上 ⇒ E3 的 free = max - used 判据只在设值后成立(设值前 free 字段不存在,不是 null/不是 0)
满载时的 free 恒为 0 server.ts:761:capacity: { max, used, free: 0 }(拒绝载荷里)
used = 在册会话数(= host 数,不是流数、不是端口数) server.ts:505:used: this.sessions.size
retryAfterMs 口径 = 满载时给对端的排队建议 CAPACITY_RETRY_AFTER_MS = 5000 ms;客户端不消耗退避地排队(client.ts:532 / 881-896,附A 已核)

5.6 443 单 §8.7③ 要求的回答:relays[] 顺序语义

本单必须回答:443 单留下的「若将来 relays[] 引入非首位更优的显式优先级语义 ⇒ 需重新定义同源优先与它的先后关系」。

回答:本单不引入该语义。relays[] 的顺序语义固定为「主入口首位」—— 首位是主入口,其余是兜底候选;不存在"非首位更优"的显式优先级字段。⛔ 本表不新增任何优先级键(新增即等于把这条语义改了)。


§6 观测阈值(探针脚本的唯一取数来源;⛔ 脚本内无魔数 = E6)

ID 指标 阈值 判据
OBS-01 relay 在册会话数(used) MIN_HOSTS used ≥ MIN_HOSTS
OBS-02 capacity.max / free RELAY_MAX_HOSTS max = RELAY_MAX_HOSTS 且 free = max - used
OBS-03 身份强制与受信签名者 MIN_TRUSTED_SIGNERS identityRequired = true 且 trustedSigners ≥ MIN_TRUSTED_SIGNERS
OBS-04 通过节点凭据校验的注册数 MIN_IDENTITY_OK identityOk ≥ MIN_IDENTITY_OK
OBS-05 吊销清单规模 MAX_REVOKED_HOSTS revokedHosts ≤ MAX_REVOKED_HOSTS
OBS-06 判别器计数三件套存在 (字段存在性) typeof dial/dialDenied/dialFailed === 'number'
OBS-07 鉴权失败累计 MAX_AUTH_FAILED authFailed ≤ MAX_AUTH_FAILED
OBS-08 端点表全在线(空表可分) (无 false) endpoints[].online 全为 true。🔴 序㊲:空表不再一律判红(旧口径对"客户端挂在另一台中继"这个合法拓扑态判 FAIL = 假红)—— endpoints 为空时按对端中继视图(ssh <SSH_TARGET_106> curl <RELAY_STATUS_URL>,只读回环)分三种:① 对端中继有会话在声明端口 ⇒ SKIP + 留痕(客户端挂在另一台中继,⛔ 非故障)② 两台都无该客户端会话(或对端中继单元 is-active≠active)⇒ FAIL 并点名(确实没挂客户端)③ 对端中继读不回来(ssh / JSON 解析失败)⇒ FAIL 并点名(不可判,⛔ 不许静默当绿)。⛔ 零新增暴露面 / 零新增参数键;⚠️ 只在 47 视角为空时才去读(正常态零额外 ssh);夹具 = --peer-status-fixture <对端 /status 原文>
OBS-09 在册实例面探活(从 /status 派生;⛔ 不依赖表内固定端口) W47_AGENT_PORT / PEER_AGENT_PORT / PROBE_CODE_SET 在册 ⇒ 必须可达:派生子 = { e ∈ endpoints[] | e.online === true 且 e.port ∉ {W47_AGENT_PORT, PEER_AGENT_PORT} }。派生非空 ⇒ 每一个都必须可达(走它自己那条 localPort 回环落点,码 ∈ PROBE_CODE_SET,否则 FAIL 并点名);⚠️ 派生端口缺码(夹具没给 / 真机 curl 得 000)⇒ FAIL 并点名(契约面,⛔ 不静默放行)。派生为空 ⇒ SKIP + 留痕(含 OBS-08 同款"空表可分"归类;⛔ 不是 PASS、⛔ 也不是 FAIL)。🔴 序 ㊲:旧口径(序⑳–㊱)「按表内那个固定实例端口做在册判据」已退役 —— 实例端口由 spawn.ts#findFreePortInRange 上扫取第一个空闲口,「访问时自然替换」会把它漂移(实测 21000 → 21001)⇒ 表值一过期就假 SKIP(序㊱ 收口复核棒连踩两次)。⚠️ 47 本机实例面(:20000)结构性不在 endpoints[] 里(47 的 worker 不是 relay 客户端)⇒ 不再是本项取样点(⚠️ 旧口径下它也从未真正参与判定)。夹具 = --instance-fixture <实例端口=HTTP码>,…(按端口映射);⛔ 旧的位置式 <本机码>,<对端码> 已退役 ⇒ 给了直接报错退出(静默解释 = 造一个没人看得懂的 FAIL)
OBS-10 门户可达 PORTAL_CODE = PORTAL_CODE
OBS-11 零新增暴露面(监听集合 / nft 入站 accept 集合 / relay 只绑回环) LISTEN_REQUIRED / LISTEN_ALLOWED / LISTEN_ALLOWED_RANGES / DIAL_POOL_BOUND / NFT_ALLOW_INBOUND / RELAY_BIND 三集包含式:必在 ⊆ 实际 ⊆ 必在 ∪ 允许 ∪ 区间(含拨号池) ∪ 派生,差集逐条点名;且 nft 入站 accept ⊆ NFT_ALLOW_INBOUND;RELAY_PORT 只出现在 RELAY_BIND 上。🔴 序⑫:旧口径「计数相等」已退役 —— 它对实例/端点在线态敏感 ⇒ 假红,对"一进一出"替换式变化不敏感 ⇒ 假绿
OBS-12 relay 进程内存 RELAY_RSS_MAX_KB RSS ≤ RELAY_RSS_MAX_KB
OBS-13 presence 帧率(稳态) PRESENCE_STEADY_FRAMES_MAX / PRESENCE_SAMPLE_HITS_MIN 两次 /status 采样的增量:Δpushed ≤ 0 且 ΔstatusHits ≥ 1(活性证明:⛔ 不许"因为读不到所以看起来是 0");并附口径一致(presenceTiming 逐项 == PRESENCE_*)+ 判别器齐全(subs/pushed/snaps/rejected/statusHits 皆 number)+ snaps ≤ pushed
OBS-14 首帧即全量 SNAP(⛔ 无 N+1) (snaps / pushed / subs 三者关系) snaps ≤ pushed 且 subs > 0 ⇒ snaps ≥ 1(有订阅者却一帧 SNAP 都没发 ⇒ 首帧走"逐台拉",判 FAIL)
OBS-15 在线态表不撒谎 + 陈旧度 PRESENCE_STALE_P95_MAX_MS / PRESENCE_TTL_MS 活跃会话(lastSeenAgoMs ≤ PRESENCE_TTL_MS)必须被 presence[] 覆盖且 online = true;且 p95(lastSeenAgoMs over devices>0) ≤ PRESENCE_STALE_P95_MAX_MS
OBS-16 订阅生效期间 /status 轮询真的停了(序㉑ · 在册缺陷 P-1 的验收) PRESENCE_GATE_WINDOW_MS / PRESENCE_GATE_HITS_MAX 第三次 /status 采样(门窗口)的增量:ΔstatusHits ≥ 1(探针自身那一次 = 活性证明)且 ≤ PRESENCE_GATE_HITS_MAX(= 1 ⇒ 除探针外零命中)且 subs ≥ 1。🔴 为什么不能只看 OBS-13:OBS-13 的 ΔstatusHits ≥ 1 由探针自身两次读即满足 ⇒ 旧口径下门 95% 时间开着、轮询照旧在跑,而 OBS-13 仍然全绿 ⇒ 实测降幅只有 1.10× 却长时间没人发现。夹具实证走 --status-fixture-3(缺省 ⇒ Δ=0 ⇒ FAIL 并点名 = 契约面)
OBS-17 内容寻址判别器齐全 + 命中可断言(序㉔ · 内容分发的验收) CONTENT_TIER_HITS_MIN / CONTENT_BLOCK_SIZE / CONTENT_STORE_MAX_BYTES 两次 /status 的 content 块:① 判别器存在性 —— content.source 五档(local/peer/edge/region/origin)皆 number、content.peer 五个键(peerHits/peerMisses/crossGroupDenied/declarations/withdrawn)皆 number、content.store 七键皆 number(⛔ 缺一即 FAIL —— 这正是本线"静默放行"回头条件的机器判据);② 口径一致 —— content.blockSize == CONTENT_BLOCK_SIZE 且 content.storeMaxBytes == CONTENT_STORE_MAX_BYTES;③ 活性 —— local + peer 命中计数增量 ≥ CONTENT_TIER_HITS_MIN。🔴 为什么必须有 ③:只判 ①(判别器存在)会让"装了但一次都没命中"全绿 ⇒ 那正好是 E1 失败(全是回源)的样子。夹具实证走 --content-fixture(缺省 ⇒ 无 content 块 ⇒ FAIL 并点名 = 契约面)
OBS-18 「逐步拉起」认领逻辑在册 + 计数自洽(序㉕ · 实例逐步拉起的验收) DSHS_REHYDRATE_STAGGER_MS / DSHS_REHYDRATE_PROBE_MS dshs-worker journald 的 [rehydrate] 行(近 24 h):① 🔴 在册(强判据) —— 必须存在 [rehydrate] 行;换回旧行为(启动即 cleanAllStaleScopes())⇒ 永远不打这行 ⇒ 必红(本项正是"把清空换成认领"的可机器断言面)。② 计数自洽 —— 最近一条 [rehydrate] summary {...} 键齐全且 adopted + stopped === scanned(每条被扫到的 scope 都必须有处置结论,⛔ 不许"扫到了却不认领也不停")。⚠️ 诚实标注:⛔ 故意不把"扫到几条"当判据 —— 认领发生在进程启动时刻、探针是事后读,拿"当前 scope 数"比必然假红(启动后才起的实例对不上)⇒ scanned/adopted/stopped 只作信息输出,真机活性由 S6 的 E1/E3/E4 断言。夹具实证走 --rehydrate-fixture <journalctl 原文>(缺省 ⇒ SKIP:本项真机数据只能 ssh 取)
OBS-19 抖动观测块在册 + 口径一致(序㉖ · 骨干稳定选路的验收) JITTER_LIMIT_MS / JITTER_HIST_BUCKETS /status.jitter 块:① 判别器齐全 —— p95AbsDeltaMs / meanAbsDeltaMs / maxAbsDeltaMs / samples / deltas / sessions / thresholdMs / alerts 皆 number、overThreshold 为 boolean、hist 为数组(⛔ 缺一即 FAIL —— "没样本就少一个键"会让探针分不清「没装」与「装了但还没采到」,本线两处静默失效就是这么漏过去的);② 口径一致 —— jitter.hist.length == JITTER_HIST_BUCKETS 且 jitter.thresholdMs == JITTER_LIMIT_MS(防"装了但用的是另一套默认值 / 另一个键")。③ ① 本身即覆盖"无样本也必须结构完整"(sessions = 0 时上述键依然全在)。夹具实证走 --status-fixture 的 jitter 块(judged = true ⇒ 夹具模式照判,⛔ 不 SKIP)
OBS-20 容量余量仍在(⛔ 没打满)(序㉖ · E4 的验收) RELAY_UTIL_MAX_PCT /status.capacity:① utilPct / utilMaxPct 皆 number(⛔ 不许少键);② utilPct < utilMaxPct(⚠️ 方向是小于)⇒ 新接入仍被接受、余量 > 0。🔴 utilPct ≥ utilMaxPct ⇒ 软门已在拦新节点 ⇒ 对"骨干应留 30%+ 余量"的口径就是不合格(要扩容,⛔ 不是改判据凑绿)。附带输出 counters.utilRefused(本序新增:与硬门 refused 分开计)作信息面。夹具实证同 --status-fixture
OBS-21 每连接候选数 ≥ 2(序㉗ · E3「路径多样性」的验收) CAND_MIN 各连接路径自己的 CAND_OBS_PREFIX 观测行(按 CAND_OBS_UNITS_47 / CAND_OBS_UNITS_106 逐 <unit>@<host> 独立判):① 该路径有观测行 —— ⛔ 缺行即 FAIL,并把 journalctl 原文字节数一并打出(0 字节 = 读取失败,>0 字节 = 真没有该行 ⇒ 两者必须可区分);② count ≥ CAND_MIN。🔑 hosts(主机名个数)只作信息输出、⛔ 不作判据 —— 🔴 且它不是「独立物理路径数」:观测器不解析 DNS(零网络),而生产上前两条候选 ai1net.com 与 relay-direct.ai1net.com 摘名不同、同落 47 ⇒ 真机实测 count=3 时 hosts 也报 3,而机器级独立路径只有 2(47 + 106)⇒ 这个数只能提示"条数够 ≠ 冗余够",「冗余建成」必须由人按机器归属判(⛔ 别拿它当独立路径数)。故本项照实判条数、把 hosts 打出来供人判,⛔ 绝不放宽判据凑绿。⚠️ 观测行由 relay-tunnel.ts#RelayCandidateObservation 在每次真实解析时写、并按 RELAY_CAND_OBS_MS 周期重发上次快照;resolves=0 + source=unresolved ⇒ 从未解析过(⛔ 与"解析出 0 条"必须可区分,本线两处静默失效都栽在"分不清没装与没采到")。夹具实证走 --candidates-fixture-47 / --candidates-fixture-106(journalctl 原文;某侧缺 ⇒ 该侧 SKIP、全缺 ⇒ 本项 SKIP、⛔ 夹具模式绝不去 ssh)
OBS-22 退出路径不杀实例(序㉘ → 单 A · 候选 B 的验收) TEARDOWN_GUARD_MARKER / TEARDOWN_FORBIDDEN_TOKENS 两件套(逐台判,TEARDOWN_UNIT_47 / TEARDOWN_UNIT_106):① 🔴 静态守卫(判别力在此) —— 读部署产物 TEARDOWN_LIB_* 里三处 teardown() 窗口,要求 ⓐ 带 TEARDOWN_GUARD_MARKER;ⓑ 窗口内不出现 TEARDOWN_FORBIDDEN_TOKENS 任何字面量。旧产物 ⇒ 无标记 + orchestrator 体含 stop( ⇒ 必红(= 执行棒 S4「真机先红」的可机器断言面;⚠️ 判 lib/ 而非 src/ —— lib/ 才是在跑的那份)。② 认领面:TEARDOWN_UNIT_* 的最近一条 [rehydrate] summary 存在时要求键齐全 + adopted + stopped === scanned + scanned > 0 ⇒ adopted > 0(扫到存量却一条未认领 ⇒ 被杀了);scanned === 0 ⇒ 只作信息输出、⛔ 不判红。🔴 为什么 scanned > 0 不能无条件当判据(诚实口径,可推翻):scanned === 0 在日志上有两种不可区分的成因 —— ⓐ 实例被退出路径杀了(旧行为现场)ⓑ 启动时本来就没有存量(平台正常空闲态,两机实测 0/0)⇒ 无条件判它 ⇒ 空闲态永久红、判据灵敏度被磨掉。🔴 故「重启前后 scope 数守恒」在本探针里无法只读完成(要守恒就得先重启生产)—— 该条由执行棒 S5/S6 的真机演练举证(原文见 交接单_退出路径不杀实例_20260918.md §8.7),⛔ 探针不冒充。夹具实证走 --teardown-fixture-47 / --teardown-fixture-106(grep 原文;某侧缺 ⇒ 该侧 SKIP、全缺 ⇒ 本项 SKIP、⛔ 夹具模式绝不去 ssh)+ ② 复用 --rehydrate-fixture
OBS-23 🔴 组密钥加密"共享不退化 + 明文不出现"(序㉘ → 单 B · 用户拍板「C 组密钥加密」的验收) CONTENT_CRYPTO_DET_MIN / CONTENT_CRYPTO_DECRYPT_MIN / CONTENT_CRYPTO_SCAN_MIN / CONTENT_TIER_HITS_MIN / CONTENT_KEY_FILE / CONTENT_KEY_FILE_MODE /status.content.crypto 十键 + 密钥文件权限:① 判别器齐全 —— content.crypto 的 encrypts/decrypts/decryptRejected/epochs/epoch/epochExpired/detChecks/detMismatches/plainScans/plainLeaks 全部是 number(⛔ 缺一即 FAIL);且(真机) 两台机上 stat -c %a CONTENT_KEY_FILE 必须逐字 = CONTENT_KEY_FILE_MODE(启用了加密却拿不到 0600 密钥文件 ⇒ 判据面不自洽 ⇒ FAIL;⛔ 夹具模式不查文件、也不 ssh)。② F1 + F2 —— F1 = detChecks ≥ CONTENT_CRYPTO_DET_MIN 且 detMismatches = 0(同明文两次 ⇒ 密文逐字节相同 = 按哈希共享块的前提);F2 = decrypts ≥ CONTENT_CRYPTO_DECRYPT_MIN 且 decryptRejected = 0 且 content.source.local + content.source.peer ≥ CONTENT_TIER_HITS_MIN(🔴 阈值与 OBS-17 ③ 同源,⛔ 不另立一套)。③ F4①:明文不出现 —— plainScans ≥ CONTENT_CRYPTO_SCAN_MIN 且 plainLeaks = 0(对自己刚加密出的字节做字节级扫描)。🔴 为什么 ③ 单独必须有:「没测到」与「真没有」必须可分 —— 只判 ①("判别器在")会让"加密了但明文照样落库"全绿;反向证明(OFF 段必须命中 ≥ 1)由 --content-crypto-fixture 承载。⚠️ "缺省不启用" = SKIP + 留痕(合法状态),⛔ 但不许既不是 SKIP 也不是 FAIL 的"静默绿"。⚠️ epochExpired / decryptRejected 稳态须 0;后者持续增长 ⇒ 命中单 B §9-6(停下报告,⛔ 不许"重启一次看看")。夹具实证走 --content-crypto-fixture(缺省 ⇒ 取 --content-fixture 的 crypto 子块;两处都没有 ⇒ SKIP)|🔴 口径定稿(序㉛ · 2026-09-18):本行「缺省不启用 = SKIP + 留痕」即最终口径 —— 单 B 的 §4/S6 原写的「新 OBS-23 对旧生产 ⇒ FAIL」作废(它会把"合法地没启用"误报成红 ⇒ 磨掉判据灵敏度);该项的"真机先红"这条腿由权限腿承载(把 CONTENT_KEY_FILE 从 0600 改 0640 ⇒ FAIL 并分别点名两台实数权限)。⛔ 仍不许出现"既非 SKIP 也非 FAIL"的静默绿
OBS-24 网注册表 + 白名单派生自洽 + 跨网零共享(序㊱ · S1/S4) NODES_REGISTRY_FILE 控制面注册表 JSON(夹具 --nodes-fixture;真机 cat):① 形状合法(version / nodes / status ∈ {pending,approved} / nodeKey 64 hex)② 派生集合 ≡ 该网 approved 集合(差集逐条点名)③ pending 一律不在派生里("收单 ≠ 批准"的可断言面)④ 结构性错桶 = 0(派生桶里的 hostId 必须属于该桶那张网 ⇒ J5 的机器判据)⑤ 派生桶数 ≡ 有 approved 的网数(⛔ 不许把多张网合并成一张)。⚠️ 文件不存在 ⇒ SKIP + 留痕;存在却读不出 / 解析不了 ⇒ FAIL 并点名(两者可分)。🔴 刻意不判:同一 hostId 出现在两张网是合法状态(客户端自取设备名会撞名;test/overlay-join.test.mjs#D1 已钉死)⇒ 只作信息项打印,⛔ 不作判据。🔴 序㊳ 起:夹具模式缺 --nodes-fixture ⇒ SKIP + 留痕(⛔ 不去 ssh 读生产)
OBS-25 一键加入在册 + 失败具名 + 凭据一次性(序㊱ · S2/S3) JOIN_STEPS_MIN / JOIN_NAMED_FAILURES_MIN selfcheck 读数 JSON(夹具 --join-fixture;真机 cat <NODES_SELFCHECK_FILE>):① 四步齐且全绿(≥ JOIN_STEPS_MIN)② application.hasPrivateKey = 0 且申请单非空(私钥不出机)③ ledger.second = invite-already-used(一次性)④ silentRejections = 0 且具名失败 ≥ JOIN_NAMED_FAILURES_MIN(⛔ 不许静默拒绝)⑤ registry.auditOk = true ∧ derivation.crossNetworkShared = 0。⚠️ 私钥权限 nodeKeyModeOk: null = 本平台不可判(Windows)⇒ 留痕不计绿;= false ⇒ FAIL。⚠️ 读数文件不存在 ⇒ SKIP + 留痕。🔴 序㊳ 起:夹具模式缺 --join-fixture ⇒ SKIP + 留痕(⛔ 不去 ssh 读生产)
OBS-26 🔴 直连开关「用户可设置 + 默认开 + 有提示」(序㊵ · P2/S5 · 用户口径①②③ · 判据 D2/D3/D4) DIRECT_HINT_MIN_PARTS 数据源 = DIRECT_SELFCHECK_FILE(夹具 --direct-fixture;真机 cat <DIRECT_SELFCHECK_FILE>):① 默认值 = 开 —— switch.defaultCase.enabled = true 且 source = default(⛔ 不是"缺省关")② 关闭生效 —— switch.offCase.enabled = false 且 udpSocketsOpened = 0 且 candidatesEmitted = 0(关闭 ⇒ 零 UDP socket + 不发候选)③ 非法值不静默 —— switch.badCase.enabled = null(⛔ 不许当开、⛔ 不许当关)④ 提示可行动 —— hint.parts ≥ DIRECT_HINT_MIN_PARTS 且 mentionsOff ∧ mentionsScope ∧ mentionsImpact 全真 ⑤ join 回读 —— joinConf.direct = true 且 readback = true(= D3:新节点加入后配置读回缺省 = 开)。⚠️ 本项没有"缺省不启用"档(直连缺省即开)⇒ 只有 PASS/FAIL + 两种留痕 SKIP(读数缺失 / 夹具缺失)。🔴 真机的 ss -lunp 增量腿由 selfcheck --ss 另取
OBS-27 🔴 候选交换「只走同网 + 白名单」且拒绝必须显式(序㊵ · P2/S5 · 判据 D1) DIRECT_CAND_ACCEPT_MIN / DIRECT_CAND_REJECT_MIN 数据源同上(candidate 段):① accepted ≥ DIRECT_CAND_ACCEPT_MIN(同网 + 白名单内 ⇒ 接受)② 负腿 rejected.length ≥ DIRECT_CAND_REJECT_MIN 且每条都有具名 reason(跨网 / 替第三人申报 / 白名单外 / 本机不在白名单 / 坏形状 / 夹带凭据字段 / 坏地址 / 超限 / 过期)③ 🔴 silentRejections = 0(返空又不计数 = 本线老病根 ⇒ 直接 FAIL)④ bucketsPerNetwork = true(白名单按网分桶,跨网同名 hostId 互不可见)。⚠️ 准入实现必须是 network.ts#isAllowedDialer(= server.ts DIAL 白名单同一条策略)⇒ impl 字段点名实现;⛔ 直连模块里出现 dialers.get( = 抄了一份 ⇒ 由 test/overlay-direct.test.mjs#T4 守
OBS-28 🔴 打洞「成功路径 + 失败判死 + 冷却非零」(序㊵ · P2/S5 · 判据 D5/D6) DIRECT_PUNCH_MIN_OK / PUNCH_DEADLINE_MS / DIRECT_COOLDOWN_MS 数据源同上(punch / deps 段):① 成功路径 —— success.bidirectional = true 且 attemptsOk ≥ DIRECT_PUNCH_MIN_OK ② 🔴 单向不算直连 —— oneWay.bidirectional = false 且 两侧 reason = one-way("有收包"⛔ 不足以判成功)③ 失败判死 —— dead[0].reason = deadline 且 bounded = true(耗时 ≤ deadline + 3×重发间隔 ⇒ 有界、⛔ 不无限重试)④ 冷却非零且生效 —— cooldown.ms = DIRECT_COOLDOWN_MS(🔴 必须 > 0,0 ⇒ 重试风暴 ⇒ FAIL)且 secondAttemptBlocked = true(二次尝试被挡且不开 socket)且 zeroRejected = throws(构造期断言)⑤ 依赖面 —— deps.ok = true(lib/net/relay/direct/*.js 只 import node:dgram + 同模块相对路径)。⚠️ 打洞走用户态 dgram,⛔ 无内核驱动、⛔ 无虚拟网卡;成功路径的机器断言由离线 NAT 模拟器承载(云上真机腿被云安全组挡着 ⇒ 见 D8 与交接单 §2-3)
OBS-29 🔴 块 id 的 per-network 域分离(C)真生效(序㊻ · 04-133 §3) 无新增阈值键(五条腿全是布尔 ⇒ ⛔ 不造“凑数键”=不把判据变成配置) 数据源 = 本机进程内自检(直取 lib/net/relay/content/*;⛔ 零 ssh、⛔ 零生产依赖、夹具模式照跑):① P1 正腿 = 同字节 + 不同 network ⇒ 块 id 不同,且两侧都 ≠ 裸哈希;② P2 正腿 = 同 network + 同字节 ⇒ 块 id 相同(⛔ 只测 ① 会漏掉“去重被干掉”);③ P3 正腿 = store 写侧 putRejected = 0 + 读侧 corruptReads = 0 + chunker#reassemble 用同一把域密钥能重组回原内容 (⇒ 调用点无漏改;🔴 漏一处 ⇒ 写侧复算必抛);④ 🔴 N1 负腿(具名 flat-key-collapses) = 去掉 network 维度(两侧取同一 network ⇒ 同一把域密钥)⇒ ① 的谓词必须转假;⑤ 🔴 N2 负腿(具名 empty-key-falls-back-to-bare-hash) = netKey 取空 ⇒ 谓词必须转假,且取空与生效必须可分(keyed ≠ bare)。⛔ 正负两腿缺一不可(本线老病根 = 装了没生效 = 静默放行)。⏳ 真机腿(回头条件 · 部署那一棒必须补) = /status.content 的 blockIdKeyed === true 且 47 / 106 的 blockIdKeyId 逐字相同(跨机口径不一致 ⇒ 跨机取块全部判校验失败)

阈值取值(同为参数行,⛔ 不是脚本魔数)

键 值 单位 等级 来源定位 复算式
MIN_HOSTS 2 台 实测 S0 P2 /status.online[](manager + w-106) online.length
MIN_TRUSTED_SIGNERS 1 个 实测 S0 P2 trustedSigners = 1 —
MIN_IDENTITY_OK 2 次 实测 S0 P2 identityOk = 2(⛔ 不是 3:序③ 注释里的 3 是三台节点当时的口径) —
MAX_REVOKED_HOSTS 0 个 实测 S0 P2 revokedHosts = 0+/etc/dshs/revocations.json 空清单 —
MAX_AUTH_FAILED 50 次 推导 累计值会随时间涨(⚠️ 探针自己也污染它,见交接单 §5 S6);取"远大于稳态自然增长、又能挡住暴力破解"的档 经验上界
PROBE_CODE_SET 200,401 — 实测 S0 P7a:两个面在无凭据时回 401;带门户 Host 时回 200 curl -w '%{http_code}'
PORTAL_CODE 200 — 实测 S0 P2/S6:curl -s --http1.1 -o /dev/null -w '%{http_code}' -H "Host: ai1net.com" 127.0.0.1:3080/ 同左
LISTEN_COUNT 79 行 实测 ⛔ 已退役(序⑫ 集合判据替代)—— 仅对账用,不得再作为判据。原 S0 P5:ss -lntp | wc -l(与 443 单收口态逐字一致 ✅;⚠️ 该值含"47 有活跃实例"那一档,故与无实例态恒差 1 层) 同左
NFT_RULES 72 行 实测 ⛔ 已退役(序⑫ 集合判据替代)—— 仅对账用,不得再作为判据。原 S0 P5:nft list ruleset | wc -l(与 443 单收口态逐字一致 ✅;⚠️ 行数不是暴露面:47 的 input 链一条规则都没有、policy=accept) 同左
RELAY_RSS_MAX_KB 800000 KB 推导 由 §5 MEM_BUDGET_MB × 0.8 得 ≈ 800 MB(不随 MEM_PER_HOST_MB 变,故序⑥ 校准后仍保留该值)。⚠️ 原注"每 host 2 MB 假设的哨兵"已作废:实测斜率 46.7 KB/台 ⇒ 7515 台时预计 RSS ≈ 48078 + 46.7×7515 ≈ 390 MB,本哨兵(781 MB)仍留 2× 余量;且因 per-stream 开销未测(§9),运行期真正的操作约束就是本哨兵,不再是 --max-hosts MEM_BUDGET_MB × 1000 × 0.8

序⑲ 新增阈值键(OBS-13/14/15 的取数来源)

键 值 单位 等级 来源定位 复算式
PRESENCE_STEADY_FRAMES_MAX 0 帧 实测(口径 = 交接单 §6 E1) 稳态"变化驱动"⇒ 无状态变化则一帧都不推 判据本身(帧数,⛔ 不是时长)
PRESENCE_SAMPLE_HITS_MIN 1 次 推导 探针自己连读两次 /status ⇒ ΔstatusHits 至少 1;用来把"读不到"与"确实为 0"分开(本线反复踩的假绿) 探针一次采样 = 1 次读
PRESENCE_SAMPLE_GAP_MS 3000 ms 推导 必须 ≥ 3 × PRESENCE_BATCH_MS(否则批窗口还没过去,增量无意义) PRESENCE_BATCH_MS × 3 = 1000 × 3
PRESENCE_STALE_P95_MAX_MS 15000 ms 推导(= 心跳周期) D3:允许 5–15 s 陈旧;心跳每 HB_SEC 刷新一次 lastSeen ⇒ 稳态陈旧度的上界就是心跳周期 HB_SEC × 1000 = 15 × 1000

序㉑ 新增阈值键(OBS-16 的取数来源)

键 值 单位 等级 来源定位 复算式
PRESENCE_GATE_WINDOW_MS 60000 ms 推导 门窗口长度:必须 ≥ 4 × 轮询周期,否则判据无分辨力(轮询在跑也只看得到 0~1 次) RELAY_STATUS_POLL_MS × 12 = 5000 × 12
PRESENCE_GATE_HITS_MAX 1 次 推导 窗口内命中增量的上限 = 探针自身那一次(活性证明 ≥ 1 与"别人没拉" ≤ 1 由同一个数同时表达) 探针一次采样 = 1 次读

🆕 序⑫ 新增 4 键(OBS-11 集合判据的白名单,⛔ 取代 LISTEN_COUNT / NFT_RULES) ⚠️ 值格必须纯数字/纯 地址:端口(夹注 ⇒ NaN ⇒ 假红);⛔ 值内不得含 |、*、反引号 (探针 cleanValue() 会剥掉 * 与反引号 ⇒ 写 *:443 会被改成 :443 ⇒ 判据全红)。

键 值 单位 等级 来源定位 复算式
LISTEN_REQUIRED 0.0.0.0:22,[::]:22,0.0.0.0:80,0.0.0.0:443,0.0.0.0:888,127.0.0.1:3080,127.0.0.1:20080 — 实测 🆕 序⑫ S1:必在集(缺一条 ⇒ FAIL)。口径 = 平台工作必需 且 不受实例/端点在线态影响的固定口。值按 47 上 ss -lntp 原文逐条写;⚠️ 3080 的口在 47 上是 127.0.0.1:3080(⛔ 不是 0.0.0.0:3080 —— 原规划稿示例写错,以原文为准) ssh -p 22 bt-server "ss -lntp"
LISTEN_ALLOWED 0.0.0.0:58888,0.0.0.0:8765,127.0.0.1:15432,127.0.0.1:19100 — 实测 🆕 序⑫ S1:允许集(非必在) —— 出现合法、消失不红(只是某个可选项没开:宝塔面板口 / 管理 UI / 控制面 PG / 本机 worker agent) 同上
LISTEN_ALLOWED_RANGES 127.0.0.1:20000-20999,127.0.0.1:21000-21999 — 实测 🆕 序⑫ S1:区间允许集 = 两台 worker 的实例端口区间。⚠️ span 取 1000(⛔ 不是规划稿示例的 100):dshs-worker 单元实测 DSHS_INSTANCE_PORT_BASE=20000(47,经 /proc/<pid>/environ)/=21000(106,经 /etc/dshs-worker.env)+ 两者 DSHS_INSTANCE_PORT_SPAN=1000 ⇒ 并集 [20000,22000) 正好落在 relay 声明窗口 [19000,22000)(--base 19000 --span 3000)之内。拨号池⛔不在此键内(复用 DIAL_POOL_BOUND 派生,避免两处漂移) ssh -p 22 bt-server "tr '\0' '\n' < /proc/$(ss -lntpH 'sport = :19100' | grep -oP 'pid=\K[0-9]+' | head -1)/environ | grep INSTANCE_PORT"
NFT_ALLOW_INBOUND tcp:22,tcp:80,tcp:443,tcp:888,tcp:3080,tcp:58888 — 实测 🆕 序⑫ S4:入站 accept 白名单(nft -j 归一后 acceptSet ⊆ 本键)。归一形态 = <proto>:<dport>;「无 dport 匹配的 accept 规则」归一为 <proto>:any —— ⚠️ 47 的 input 链目前 0 条规则 ⇒ 集合为 ∅、本键暂无标记位;将来若出现必须显式加进来(⛔ 不许放宽判据) ssh -p 22 bt-server "nft -j list ruleset"

📌 成员归属判据(序⑫ S1 明文要求,防下一棒再吵):

  • 必在(LISTEN_REQUIRED)= 它不在 ⇒ 平台本身坏了(sshd / nginx 80·443 / 宝塔 888 / 平台门户 3080 / relay 回环口)
  • 允许(LISTEN_ALLOWED + _RANGES)= 它不在 ⇒ 只是某个可选项没开(面板口 / 管理 UI / PG / worker agent / 实例档 / 拨号池)
  • 🔑 "有/无实例态"⛔不编码进参数表(那等于把"对状态敏感"这条缺陷写进单一来源 = 净退化,违 R11)⇒ 实例档直接进允许区间:出现不报、消失不红。

⚠️ 附注(序⑥ S3(b) 明文要求登记的 OBS 侧口径):relay /status.sessions[].rttMs 是心跳往返 (含应用层处理与验签),⛔ 不得当链路 RTT 用,⛔ 更不得据它下"跨云不可玩"的结论 —— 实测三方对比:ICMP 148 ms | TCP 握手 median 153 ms | relay 心跳 344–376 ms(差 2.3×)。 真判据请用 overlay-jitter.cjs --icmp/--tcp。


§7 待测项汇总(⛔ 一个都不许编数;与表内 待测 单元格一一对应)

🔢 计数口径(E1 判据):表内 待测 单元格 4 个 → 0 个(序⑥ 全部换成实测,见下表「结果」列)。 第 5 条是「推导值待校准」(MEM_PER_HOST_MB)—— 序⑥ 已校准为 实测 0.06 MB/台。

# 键 换测条件 归属 序⑥ 结果
1 HOLE_PUNCH_RATE_LOCAL 3–5 台真机(分层:家宽 / CGNAT / 移动 / 企业网) 序⑥ ✅ 实测(分层):n=3 对,2/2 对可打洞(对端→本机 10/10);⚠️ 首选观察器不可用(云安全组拦 UDP 入站)⇒ 走第三方 STUN;不代表家宽场景
2 PER_PLAYER_BW_LOCAL 同上 序⑥ ✅ 实测:9.8 KB/s/玩家(n=10)/ 3.9(n=50);聚合天花板 200–350 KB/s
3 WAN_STEADY_THROUGHPUT 两端可控载荷(本次两个面在无凭据时只回 24–68 B) 序⑥ ✅ 实测:352 KB/s(106→47)/ 12213 KB/s(47→106),5 样本中位数,经 relay,稳态段 30 s
4 JITTER_LINK_MEASURED 同上;⚠️ 这是游戏可玩性的决定项(调研…:118) 序⑥ ✅ 实测:`p95(
5 MEM_PER_HOST_MB(推导值待校准) 压到 ≥ 20 台再量 RSS 斜率 序⑥ ✅ 实测:N=2..150 六点,斜率 46.7 KB/台,R²=0.942;原推导 2 MB 高估 36×

✅ 序⑨ 收口(2026-09-17 14:xx):无新增待测项(表内 待测 仍为 0 个)。本轮新增的两个键 (DRILL_SAMPLE_N = 5 / RELAY_GRACEFUL_BURST_MS = 15000)都是实测/推导值,⛔ 不含 待测; DRILL_POLL_MS 由 2000 改 500 属测量修正(剔掉 ≤2 s 量化误差),⛔ 不是待测项换测。 另:序⑨ 的四段分解登记在 §9 第 9 行(属"已知边界/实测记录",不进本表 §7 的"待测"口径)。

🆕 序㉛(2026-09-18)复算:仍为 待测 0 项 —— 序㉛ 是「补测 + 口径定稿」性质,⛔ 未新增任何键、⛔ 未改任何值。本棒新测的 4 项(加密性能开销 / 轮换代价 / 低熵明文定性 / 过渡窗口三档)是验收读数,一律落在 §11.3 补记区(= §10 指纹口径之外),⛔ 不冒充"待测项换真值"。

合计:待测 4 项 → 0 项;待校准推导 1 项 → 已校准。(E1 判据达标)

🆕 序⑦(2026-09-17)复算:仍为 待测 0 项 —— 序⑦ 新增的 RELAY_FAILOVER_* 5 键全部取默认值(= 源码真值),不产生新的「待测」;同时 §9 第 5 行(无失败切流)改判已闭环。⛔ §9 第 6 行(per-stream 内存)仍未测,仍是序⑦ 之后的回头条件。

🆕 序⑧(2026-09-17)复算:仍为 待测 0 项 —— 序⑧ 新增 3 键(RELAY_FAILOVER_EXEMPT = 源码默认值 1; DRILL_COOLDOWN_MS / DRILL_NO_SWITCH_OBSERVE_MS = 演练脚本自用的推导值)均不产生「待测」。 ⚠️ 两条口径要记住:① DRILL_COOLDOWN_MS 是演练期覆盖值,⛔ 不是生产值(生产恒为 RELAY_FAILOVER_COOLDOWN_MS=300000); ② 幕 4b 的结论标注了非生产冷却值方可复现(D9)。§9 第 5 行追加"冷却语义拆分已闭环";§9 第 6 行仍未测。

🔴 回头条件(已执行,2026-09-17 序⑥ S7):MEM_PER_HOST_MB 与 WAN_STEADY_THROUGHPUT 双双换成实测 ⇒ §5.2 已重算并重下发两台中继的 capacity.conf(225 → 7515),三条校验已重跑,OBS-02 复验 PASS。⛔ 未触发项:无(两项都换成了实测)。


  • 🆕 序㊱ 复算(仍 0 项):OBS-24 / OBS-25 两条不引入任何数值阈值(NODES_REGISTRY_FILE / NODES_SELFCHECK_FILE 是路径信息键;JOIN_STEPS_MIN = 4 与 JOIN_NAMED_FAILURES_MIN = 4 由源码常量直接取得,⛔ 非推导)⇒ 本表 待测 单元格仍为 0 个。

§8 权限影响评估(R5;只有产出,无动作)

交接单 §4.1-7 的固定 6 列:对象 / 是否扩大权限面 / 扩大到哪一类 / 是否已可收窄 / 证据 / 结论。 ⛔ 结论列只允许「收窄」或「维持」;若某项确需扩大 ⇒ 停下报告(命中 R5)。

# 对象 是否扩大 扩大到哪一类 是否已可收窄 证据(代码行 / 命令原文) 结论
① 虚拟网卡驱动(打洞所需) 否(本单零动作) 若将来做 ⇒ 权限位(管理员/内核态驱动) ✅ 可收窄:不做虚拟网卡(纯 relay 中继形态已闭环),要打洞也只走 UDP 用户态(无驱动) 本表 §3.2 HOLE_PUNCH_RATE_LOCAL = 待测;现网 2 节点全走 relay(/status.networks[0].sessions = [manager, w-106]);⚠️ 我方 relay 只做 TCP/WS,代码里没有任何 TUN/TAP 调用(grep -rn "tun|tap|ip tuntap" src/net/relay/ = 0 命中) 维持
② 骨干节点开端口 否 — ✅ 已收窄:骨干成员只拨出、不开入站(worker 永远只拨出,106 入站 = 0) 主单元 ExecStart 无 --host ⇒ 默认绑 127.0.0.1;实测 ss -lntp 中 20080 只出现在 127.0.0.1:20080;覆盖网络_骨干层方案_20260916.md「骨干不得被默认征用」 维持
③ nft 打洞规则 否(本单零动作) 若将来做 ⇒ 入站面(放行 UDP 入站) ✅ 可收窄:现实测 nft list ruleset = 72 行且与 443 单收口态逐字一致 ⇒ 打洞规则一条都没加 S0 P5:ssh -p 22 bt-server 'nft list ruleset | wc -l' → 72;对比交接单 §6 E9 的收口态 72 维持
④ 第二中继机(L3 跨机真容灾) 否(已实施;R5 要求的"实施态"逐项如下) 若实施 ⇒ 入站面(新机新公网口)+凭据面(新节点密钥签发) ✅ 已实施且两项都没扩大:新增监听口 = 0(relay 仍只绑 127.0.0.1:20080;对外复用 106 既有 nginx 的 443,证书 = 本机既有 Let's Encrypt(SAN 覆盖 IP 106.54.21.172),⛔ 未下发任何证书/私钥)|新增凭据 = 0(relay 侧只需公钥类文件:relay-keys.json、overlay-signers.json、revocations.json;⛔ node-*.key 与 overlay-signer-key.pem 一律留在 47)|106"入站 = 0"这条已收窄成果:443 本来就开着(宝塔 nginx),本次只在既有 server 块内加一条 location ⇒ ss -lntp 计数未变 systemctl cat dshs-relay(106)|ss -lntp|106 的 extension/106.54.21.172/relay-b.conf 维持
⑧ 临时 UDP 入站面(序⑥ S4 打洞探测的观察器) 是(临时,已关闭) 入站面(UDP 高位口) ✅ 已收窄:对象 = 47 的 0.0.0.0:21100/21101(overlay-holepunch.cjs --observer),开放时长 ≈ 75 s、进程退出即释放;关闭证据 = ss -lunp | grep 2110[01] ⇒ 空。⚠️ 实测该口零收包(连同机发出的都收不到)⇒ 47 的云安全组拦 UDP 入站(⛔ 不是 nft:input policy = accept)⇒ S4 改走第三方 STUN ss -lunp(空)|本单 §8.4 E6 维持
⑨ 106 的 443 relay 路由(序⑥ S8 新增 location /dshs-relay) 是(新增可路由路径,⛔ 未新增监听口) 入站路径面(不是新口) ✅ 已收窄:只放行 /dshs-relay 一个端点(⛔ 不写 location /、不复制站点任何路径;未知路径 404);relay 侧仍强制 HMAC +节点凭据(identityRequired = true) 本单 §8.6 + curl -H "Upgrade: websocket" … https://106.54.21.172/dshs-relay = 101|/nope = 404 维持(监听口零新增)
⑤ 443 兜底入口(复核是否真零扩大) 否 — ✅ 已收窄(相对 sshd 时代净减 1 个公网口) 实测:32022 已回收(ss -lntp 无该口);443 由 nginx 复用(ss -lntp 显示 0.0.0.0:443 + nginx 4 个进程),未新增监听;/etc/dshs.env 的 DSHS_OVERLAY_BOOTSTRAP_SEEDS 含 relay-direct.ai1net.com/dshs-relay,而 DSHS_OVERLAY_ADDR_OVERRIDES=relay-direct.ai1net.com=47.77.182.89 ⇒ 走的是既有 443 维持
⑥ relay 拨号白名单 dialers(R5 引入) 否 — ✅ 已收窄:白名单是准入收窄(默认拒绝,⛔ 不是放开);且构造时定型、运行期不可改 src/net/relay/server.ts:355 private readonly dialers;:404 normalizeDialers() 在构造期;:747 const wantDialer = this.dialers.get(network)?.has(hostId) === true(默认拒绝);drop-in dialers.conf = Environment="DSHS_RELAY_DIALERS=ops:manager"(只有 1 个拨号方) 维持
⑦ 每机独立密钥与信任根保管 否 — ✅ 已收窄:私钥只在本机、信任根在离线签发;relay 侧只有公钥与签名者集合 /etc/dshs.env DSHS_OVERLAY_NODE_KEY_FILE=/etc/dshs/node-manager.key(私钥路径仅本机);drop-in identity.conf 里只有 DSHS_OVERLAY_ROOT_PUBKEYS=<公钥> + DSHS_OVERLAY_SIGNER_SET_FILE ⇒ 私钥从不出现在 relay 的配置面;:399 this.trustedSignerKeys/:401 this.requireIdentity 均 readonly 维持
⑩ P2 直连(打洞)的 UDP 入站面(🆕 2026-09-18 12:22 用户已拍板) 是(本期唯一"扩大"项;已按拍板收窄到最小形态) 入站面(云安全组 UDP 小段)+ 节点侧 UDP socket(用户态、无内核驱动 —— 与 ① 的"不做虚拟网卡"一致) ✅ 已收窄:① 来源范围 = 乙(仅放行对端节点 IP,⛔ 非 0.0.0.0/0)② 用户可设置 + 默认开启 + 提示用户(用户口径)③ 关闭后零 UDP socket(可断言 ss -lunp 无新增)④ ⛔ 不新增任何"对公网任意源开放"的规则 拍板原话 = 1 用户可设置,默认开启提示用户 2 乙(2026-09-18 12:22)|执行依据 = 交接单_覆盖网络直连与P2P_20260918.md §7(判据 D8)|⚠️ 技术现实:对端为家宽 / 移动时公网出口 IP 会变 ⇒ 序㊴ 自决 乙-1 起步(只对有固定公网 IP 的节点开;家宽节点本期走中继),乙-2 / 乙-3(🔴 需云 AK/SK)登记为该单 §9 回头条件 扩大(经用户拍板 · 形态已收窄)

两条附注

  • ⚠️ 本表 §5.1 的「每 host 0.06 MB」是空闲会话的实测斜率 ⇒ 它不覆盖 per-stream 缓冲(见 §9 新登记行)。
  • ⛔ 本表口径仍成立:零新增公网端口、零新增入站面、零凭据外发;序⑥ 的两处"潜在扩大项"(④ 第二中继 / ⑧ 临时 UDP / ⑨ 443 路由)逐条列在表内,结论均为维持或收窄。
  • 🆕 一条口径更新(2026-09-18 12:22):⑩ P2 直连的 UDP 入站面已由用户拍板(「用户可设置 + 默认开启 + 提示用户」+ 来源范围 乙)⇒ 上一条的"零新增入站面"自此只对已收口部分成立;⑩ = 本期唯一经拍板的扩大项,形态已被拍板收窄(仅对端 IP、用户可关、零内核驱动)。⛔ 截止本行尚未实施、零动作(开工依据 = 交接单_覆盖网络直连与P2P_20260918.md)。

§9 未纳入本表的已知边界(登记,不改)

# 边界 为什么不动 归属
1 引导链缓存两支无"答出者"信息 ⇒ 切兜底有 ≤ DIRECTORY_REFRESH_SECONDS(300 s)收敛期 交接单 §8.7④ 明确"本单把 300 s 收进参数表并写明该收敛期,⛔ 不改缓存结构" 已闭环(本表 §4)
2 106 的 agent 面不吃引导链(DSHS_RENDEZVOUS_URL 被当 relay URL 直接用) 交接单 §2 P8:只记录、⛔ 不许顺手撤(撤掉 = tunnel===undefined 生产回归) 若要改,归序⑥
3 D2 字面判据不可满足(CF 泛解析) 未做灰云记录 ⇒ 属"解析层也不经 CF"的更大改动 序⑥ 后候选
4 relay 进程没有 MemoryMax(只有本表的软口径 --max-hosts) 加 cgroup 内存上限 = 改单元语义 + 可能 OOM-kill relay(新失败模式)⇒ 超出本单范围,且 R11(不许净变差) 序⑥ 候选(与 MEM_PER_HOST_MB 校准一起做)→ ⚠️ 序⑥ 只做了测量与登记,仍未加 MemoryMax(同上理由,且实测后 --max-hosts 已非绑定约束)
5 🔴 无"失败切流"(序⑥ E9 后半判红的根因):客户端把 relay url 在首次解析后钉死 —— main.js --client 无重解析;worker 走 DSHS_RENDEZVOUS_URL 同样不吃引导链;只有 Manager 的拨号通道有周期性重解析(web/server.ts#refreshOverlay),而它的换址条件是"目录里的地址变了",与"当前 relay 挂了"无关 修它 = 新功能(需"连接失败后重解析 + 排除已失败 relay"),⛔ 属单外发现,只报告不动手(R7) ✅ 已闭环(序⑦ · 2026-09-17):候选集不再退化成单点(listOverlayRelayCandidates);三处客户端(Manager 拨号 / worker 实例面 / relay --client)接同一个 RelayFailoverSupervisor;阈值见本表 §4 RELAY_FAILOVER_*。⛔ 服务端零改动(D8)|序⑧:冷却语义拆分已闭环(2026-09-17)—— 冷却表结构化(键仍按 url,新增 kind)+ replace() 增 origin(health 可一跳豁免 / directory ⛔ 不可)+ 豁免有界(每 url 每冷却周期一次)+ 双开关与判别器(RELAY_FAILOVER_EXEMPT、exemptSwitches、|豁免 日志标记)|真机判据 = 演练幕 4 系列(--scene 4 / 4b / 4c)
6 per-stream 内存开销未测:MEM_PER_HOST_MB = 0.06 MB/台 只是空闲会话斜率;带流量时每条流最多 QUEUE_MAX_BYTES(1 MB) 缓冲 需带流量的压测(S6 的已定口径是"每个声明 1–2 个端口的空闲会话") 序⑦ 候选(回头条件:--max-hosts 若重新收紧,必须先有本数)
7 106 的第二中继不提供 /dshs-overlay/bootstrap(目录由 47 的 127.0.0.1:3080 签发;实测 3080 只绑回环、106→47:3080 TCP_BLOCKED) 两条绕法都有代价:走 47:443(同一失败域,等于没增益)/搬证书私钥(命中 R5 扩大) 已登记;功能影响 = 0(客户端 resolveOverlayRelay ③ 对取不到的 origin continue,且 relays[] 首位是主入口)
8 106 的 443 vhost 归宝塔面板管理:本次把 relay location 放进 extension/106.54.21.172/*.conf(面板重写 vhost 主文件不会丢它) ⛔ 若将来面板重建该站点,需复查 nginx -T | grep dshs-relay 运维注意(已写进 relay-b.conf 头部注释)
9 换址墙钟的四段分解(序⑨ 实测 · 2026-09-17,n=5+n=5 真机样本,逐行对齐 journal 时间戳):检测 15.0–16.6 s(= RELAY_GRACEFUL_BURST_MS 地板 + 0–0.7 s 重试相位)+ 首试延迟 0.3–5.0 s(RELAY_FAILOVER_CHECK_MS 相位 + 拨号耗时)+ 白等 12.02 s → 0.09 s(修前 waitUpOn 对"必然失败的同机候选"吃满 upTimeoutMs;修后终态早退)+ 建连 2.7–5.2 s ⇒ 总 30.7–34.1 s → 20.9–23.9 s(RELAY_FAILOVER_DEADLINE_MS 30000 内侧 5/5) 白等段已修(waitUpOnStatus 终态早退,三处装配点共用一份);检测段 ⛔ 不改(D3:15 s 的 burst 窗口是"计划内下线不是故障"的有意设计,改它 ⇒ 每次 relay 重启都切流) ✅ 序⑨ 已闭环(本表 §4 的 RELAY_GRACEFUL_BURST_MS / RELAY_FAILOVER_UP_TIMEOUT_MS;复算工具 = scripts/overlay-failover-drill.cjs --trace)

§10 指纹

  • 本节口径(推荐核对用,可复现):整个 §10 不计入 ⇒ 复核命令 sed '/^## §10 指纹/,$d' 参数表_覆盖网络_20260917.md | md5sum ⇒ 075a97239c2d1d8a1f442ec6cc787ede(序㊿ 执行棒 · 2026-09-19 07:5x · 旧域移除棒;本轮变更 = §3.9 PORTAL_HOST_HEADER 值格 alotbuy.com → ai1net.com、DRILL_KILLED_MATCH 值格同改(🔴 演练脚本幕4-A/幕4-B 的判据就是 includes(<本键值>) ⇒ 不改本键 必假红)+ 两处 -H "Host: alotbuy.com" curl 示例 + §6 一条候选摘名说明(relay-direct 前缀由 alotbuy.com 改 ai1net.com);🔴 零个新增 / 改动生产 env · ⛔ 未改任何阈值(DSHS_OVERLAY_BOOTSTRAP_SEEDS 与 DSHS_OVERLAY_ADDR_OVERRIDES 走 drop-in,本表只作事实记录);上一版 = ac6bbbbb8c92bd57ff0dc4cd8f4983ba —— 其原说明照抄如下:序㊻ 执行棒 · 2026-09-18 22:4x;本轮变更 = §6 新增 OBS-29 一行(块 id per-network 域分离 C:五条腿 + 两条负腿具名;🔴 本行是判据重裁的产物 —— 原 OBS-29 的计划负腿(「稀释源换成确定性派生量 ⇒ 必红」)随 D 被实测判为「作用面为空」(序㊺)而失去对象);⚠️ 顺带纠正一条既有漂移:本行原记 d408d640246a980f702fe7b0a2895219(序㊴ 12:2x),而序㊺ 两次独立现算均为 6b37bfd506758d882d9f803678f85d23 —— 原因是 §11 补记在 §10 口径之外(sed 在 ## §10 指纹 处截断)⇒ ⛔ 不是算错、是漏更新 ⇒ 本次一并同步。🔴 零个新增 / 改动生产 env · ⛔ 未改任何阈值 · ⛔ 值格一律未动(只加判据行);上一版(记录值) = d408d640246a980f702fe7b0a2895219(序㊴ 规划棒 · 2026-09-18 12:2x;本轮变更 = §8 新增 ⑩ P2 直连的 UDP 入站面一行(🆕 用户 12:22 已拍板:「用户可设置 + 默认开启 + 提示用户」+ 来源范围 乙)+ §8 附注补一条口径更新("零新增入站面"自此只对已收口部分成立);🔴 零个新增 / 改动生产 env —— ⑩ 仅登记,⛔ 尚未实施、零动作;上一版 = 2275870a5a9eb1964a648f2950dee528(序㊳ 执行棒 · 2026-09-18 11:4x;本轮变更 = ① §3.9 DRILL_RELAY_UNIT 改名为 RELAY_UNIT_NAME(键名与用途对齐 —— 它记的是"两台机的 relay 单元名"这一事实、被演练脚本与探针 OBS-08 共用 ⇒ 原 DRILL_ 前缀不相称;⚠️ 值格未动、仍 dshs-relay)② §3.9 小节标题补注(该键也被探针读取)③ §6 OBS-24 / OBS-25 两行补「夹具模式缺本夹具 ⇒ SKIP + 留痕(⛔ 不去 ssh 读生产)」。🔴 零个新增 / 改动生产 env · ⛔ 未改任何阈值 · ⛔ 值格一律未动;上一版 = 71848fad4ee0d6a574d833169e042fd1(序㊲ 执行棒 · 2026-09-18 11:1x;本轮变更 = §6 OBS-08 / OBS-09 两行判据口径改写(OBS-09 的在册判据改为从 /status.endpoints[] 派生;OBS-08 加「空表可分」)+ §3.9 四行标注(W47_AGENT_PORT / PEER_AGENT_PORT = 「序㊲ 起被探针引用」;LOCAL_INSTANCE_PORT / PEER_INSTANCE_PORT = 「序㊲ 起探针⛔ 不再引用」)。🔴 值格一律未动 · ⛔ 零个新增 / 改动生产 env · ⛔ 未改任何阈值(只改判据与说明);上一版 = 2ae954e2e07b47651a84e597c19a3444(序㊱ 收口复核棒 · 2026-09-18 10:3x;其变更 = §3.9 PEER_INSTANCE_PORT 值格 21000 → 21001(实测替换,机理见该行注)+ PEER_AGENT_PORT 出处括注同步为 ports=19000/21001;⚠️ 两键都只被探针 / 本表读取 ⇒ ⛔ 零个新增 / 改动生产 env、⛔ 未改任何阈值;上一版 = 695dcb4d88a778840322bc221417d339(序 ㊱ 执行棒收口值 · 2026-09-18 10:0x,其变更 = §3.10 新增 8 键(NODES_REGISTRY_DIR / NODES_REGISTRY_FILE / NODES_CONSUMED_DIR / NODES_SELFCHECK_FILE / NODES_EMERGENCY_DROPIN / INVITE_TTL_MIN = 30 / JOIN_STEPS_MIN = 4 / JOIN_NAMED_FAILURES_MIN = 4)+ §6 新增 OBS-24 / OBS-25 两行 + §7 补一行"序㊱ 复算(仍 0 项)" + §3.10 NODES_REGISTRY_FILE 行补「控制面 CLI 覆盖键 = --registry(⛔ 不是 --file)」与一条真机键名混用教训;⚠️ 8 键全部只被探针 / 控制面 CLI 读取 ⇒ ⛔ 零个新增生产 env;上一版 = 52e59df229102d5afa8c8bf01a960398(序 ㉛ 执行棒收口值 · 2026-09-18 07:4x;本轮变更 = ⛔ 零键零值变更 —— 只在 §3.9 CONTENT_EPOCH_GRACE_MS 行的说明列补"序㉛ 三档实测曲线"、§6 OBS-23 行末补「口径定稿」、§7 补一行"序㉛ 复算(仍 0 项)";⚠️ 值格一律未动 ⇒ 探针取阈值不受影响);再上一版 = 08c2835a7e9f0faef20f9c1d3e619d55(序 ㉘ → 单 B 执行棒收口,其变更 = §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 一行;再上一版 = 3b295a8c43aafc4a62c6f1a59ea0b43a(序㉚ 收口,其变更 = §3.8 新增 6 键 TEARDOWN_GUARD_MARKER / TEARDOWN_FORBIDDEN_TOKENS / TEARDOWN_LIB_47 / TEARDOWN_LIB_106 / TEARDOWN_UNIT_47 / TEARDOWN_UNIT_106 + §6 新增 OBS-22 一行)。🔴 本单新增的生产 env 只有 1 个(relay 侧 DSHS_CONTENT_GROUP_KEY_FILE)—— §3.9 那 6 键只被探针读取,⛔ 不写进任何 drop-in / env)
  • 上一版(序㉛ 收口)留档 = 52e59df229102d5afa8c8bf01a960398(序㉛ 执行棒收口值 · 2026-09-18 07:4x)—— 该版口径与完整回落链原文照抄如下,供对照:该版变更 = ⛔ 零键零值变更(只在 §3.9 / §6 / §7 的说明文字上补实测曲线与"复算仍 0 项")。
  • 全文件 md5:请用 md5sum 参数表_覆盖网络_20260917.md 现取 —— ⛔ 此处故意不内嵌数值:它包含本节自身,写进去即刻失效(自指)。

§11 补记(2026-09-17 11:1x,在 §10 指纹口径之外)

  • ✅ "第 4/5 台真机来源"已拍板(登记给 §9 第 5 行那条线的同批遗留):用户原话「1 本机内存大 可以模拟多台」⇒ 选 本机模拟多台,放弃"用户自备设备"与"新开云主机"。
  • 本机实测:总内存 47.6 GB / 空闲 27.4 GB / 32 核;relay 单实例 ≈ 48 MB ⇒ 可模拟数十台。⚠️ 共用同一出口 IP ⇒ 对"切流逻辑"够用,对"家宽 / 运营商 NAT 差异"无增量。
  • ⛔ 不改本表任何数值:HOLE_PUNCH_RATE_LOCAL(分层实测 2/2)与其样本口径保持原样;本补记只登记"多实例可作第 4/5 台"这一环境决定。
  • 🔒 本补记位于 §10 之后 ⇒ 复核指纹 db1317c2f7aaef7b47785c1f4fc9de03 仍然有效(后续引用无需换值)。
  • 谁能改这张表:任何一次实测替换(待测 换真值 / MEM_PER_HOST_MB 校准)都会改指纹 ⇒ 改完请同步更新本值,并在工作区日志里记一笔"哪个键从什么换成什么"。

§11.1 补记(2026-09-18 00:2x,序㉗ 执行棒;同在 §10 指纹口径之外)

  • 🔴 CAND_OBS_UNITS_47 首轮写错 ⇒ 已在真机首轮修正(假红):初版误列 dshs-worker=worker。取证 = ssh -p 22 bt-server "grep -c DSHS_RENDEZVOUS_URL /etc/dshs-worker.env" ⇒ 0、systemctl show dshs-worker -p Environment | grep -c RENDEZVOUS ⇒ 0 ⇒ 47 的 worker 根本不是 relay 客户端(不建 RelayTunnel),列进去只会读到"永远无观测行"= 假红。现值 = dshs=manager(✅ 真机 [overlay-candidates] scope=manager … count=3 已核到)。
  • 🔴 hosts 的语义 = 「主机名个数」(⛔ 不是独立物理路径数):真机首轮 count=3 时 hosts 也报 3,因为 ai1net.com 与 relay-direct.ai1net.com 摘名不同、同落 47;观测器刻意不解析 DNS(零网络 I/O)。⇒ 本表 §6 OBS-21 已写明"这个数只作信息输出、⛔ 不作判据"。
  • ⚠️ OBS-21 开局为红,且这是"如实判"而非缺陷:dshs@47 count=3 ✓ | dshs-worker@106 count=1 ❌(source=chain)。106 侧根因已在 106 自己的日志里可机读:journalctl -u dshs-worker | grep overlay-dir ⇒ 近 3h 166 行,样本 = ⚠ 拒绝 https://alotbuy.com/dshs-overlay/bootstrap(no-trusted-keys) + ⚠ 取目录全部失败 ⇒ 回落到内置种子地址本身:wss://alotbuy.com/dshs-relay;取证 = /etc/dshs-worker.env 里 DSHS_OVERLAY_DIR_PUBKEYS 计数 0、DSHS_OVERLAY_DIR_CACHE 计数 0。⇒ 「判据成立」✓ 而「冗余建成」✗,已按用户口径点名为独立后续项(⛔ 未放宽 CAND_MIN 凑绿)。
  • ⛔ 本补记不改本表任何数值(CAND_MIN = 2 是用户口径「每连接候选数 ≥ 2」的落地,⛔ 不因"当前建不成"而下调)。
  • 🔁 状态已改判(2026-09-18 06:1x,序㉙ 执行棒)⇒ 见 §11.2:上面这条"开局为红"是当时的事实;现 OBS-21 绿(两路径 count=3)。

§11.2 补记(2026-09-18 06:1x,序㉙ 执行棒;同在 §10 指纹口径之外)

  • 🔁 状态改判:OBS-21 红 → 绿。真机读数 = dshs@47 count=3 hosts=3 ✓ / dshs-worker@106 count=3 hosts=3 ✓(两路径都 ≥ CAND_MIN=2)⇒ 探针 22 PASS / 0 SKIP / 0 FAIL(rc=0)。⛔ 未放宽 CAND_MIN、未改本表任何数值。
  • 改动(全序唯一一处):106 /etc/dshs-worker.env 追加 DSHS_OVERLAY_DIR_PUBKEYS=fd81a6dd7ee22cc121239253d9bee33585638823f5425b537da930e5054f3349(与 47 同一把)。47 侧真值来源 = /etc/systemd/system/dshs.service.d/overlay-dir.conf(私钥 DSHS_OVERLAY_DIR_KEY=/etc/dshs/overlay-dir-key.pem、公钥文件 /etc/dshs/overlay-dir-key.pub 同值)。⛔ 未加 DSHS_OVERLAY_DIR_CACHE(见下第 4 条)。
  • 🔬 先取证再动手 —— "106 取目录的两条路径"各自取证(⛔ 未假设"补钥即绿"):(a) 公网路径 https://alotbuy.com/dshs-overlay/bootstrap ⇒ 106 上 curl = http 200 / remote 172.67.194.206(Cloudflare)/ 443 字节,且 106 日志的失败原因码是 no-trusted-keys —— 该码在 directory.ts#fetchDirectory 里位于 fetch → res.json() → parseDirectory 之后 ⇒ "取到了、只是验不过",可达性成立。(b) 直连 http://47.77.182.89:3080/... ⇒ curl timeout(000)(= 已知 TCP_BLOCKED 边界),⚠️ 但取目录流程根本不用 3080(走域名 → 443 → nginx → 回环 3080)⇒ 该边界不构成阻塞。
  • ⚠️ DSHS_OVERLAY_DIR_CACHE 106 仍为 0(如实登记,⛔ 按"只做被明确要求的事"未扩范围):47 的该键同样为 0 且 47 侧 OBS-21 一直绿 ⇒ 本序与"已验证有效"的 47 配置逐项对齐。缺它的后果 = 目录不可达时没有 ④「过期缓存」档、直接落 ⑤ 种子兜底(候选退回 1 条)⇒ 属冗余韧性而非本项判据;若要"CF 抖动期也保住 ≥2",需单独立项(⛔ 本序不顺手做)。
  • ⚠️ 一条既有缺陷的实测补强(⛔ 非本序引入,⛔ 本序不改码):孤儿端点条目不会自愈 —— 序㉚ 记的"替换窗口内短暂留 1 条 stale 端点条目(自愈时延未测)"本序给出答案:relay-tunnel.ts#cancel() 只撤销当前进程 forwarded 集里的端口,上一进程遗留的条目无人撤销 ⇒ 只能靠**后续一次"重新登记同一端口号"**把它覆盖回 online=true。实测:重启 106 dshs-worker 后旧条目 21000→41831 恒 online=false(约 10 min 未自愈);停掉实例让 Manager 自动重新拉起 ⇒ 新实例落回 21000 ⇒ 条目被覆盖、转绿。⇒ 触发条件 = 上一进程的端点且该端口未被新进程重新声明;判据影响 = OBS-08/OBS-09 在该窗口内会红(重启后必现),⛔ 不是"跑一会儿就好"。
  • 回滚点 = 106 /opt/dsh/backups/seq29-20260918-054852/dshs-worker.env.orig(md5 6b509ce447d9519241aa73243e07a69f);回滚 = 删该行 → systemctl restart dshs-worker(秒级)。

§11.3 补记(2026-09-18 07:3x,序㉛ 执行棒 · 单 B §10 未验证项补齐 + 口径定稿;同在 §10 指纹口径之外)

本节的数全部是原始读数(脚本原文落盘于工作区 _tmp_seq32/)⇒ ⛔ 不是估值、⛔ 未做任何粉饰;⛔ 本表任何数值未被改动(§3.9 / §6 只增"实测依据"与"口径定稿"文字说明,值格与键表零变更)。

① 加密性能开销(原 §10-1;_tmp_seq32/p01-perf.txt) —— 纯本地、零网络。臂 A = 明文、臂 B = 组密钥加密;同一份 11,363,655 B(= chunker.ts 首屏包实测值)⇒ 11 块;预热 2 次不计,正式 n = 11;分位 = nearest-rank(⛔ 不插值);机 = 本机 AMD Ryzen 9 7950X / 32 逻辑核 / Node v22.22.2。

  • 总墙钟(切块+〔加密〕+put + 链取回+〔解密〕+拼接):明文 p50 = 22.636 ms / p95 = 23.020 ms|加密 p50 = 42.782 ms / p95 = 43.599 ms ⇒ 绝对增量 +20.146 ms(p50)/ +20.579 ms(p95),比值 1.890×(p50)/ 1.894×(p95)。
  • 分段(p50):put 15.981 → 27.363 ms(+11.382)|get 6.662 → 15.444 ms(+8.782)。
  • 吞吐(按 p50 总墙钟折算):502.02 MB/s → 265.62 MB/s。密文固定开销 = 28 B/块(iv 12 + tag 16)⇒ 本包 +308 B。
  • 两臂都校验"取回字节逐字节等于原包"= true;加密臂 decrypts = 11、local 命中 11、origin 命中 0(⇒ 加解密未把共享打回去)。
  • ⚠️ 口径诚实标注:这是单核单进程、页缓存全热的本机读数 ⇒ 它是"加密层自身的成本上界参照",⛔ 不是真机端到端时延承诺(真机还叠加 relay 跳、TLS、磁盘)。序㉔ 的 E1(回源份数)不受影响(本臂 origin 命中恒 0)。

② 轮换代价量化(原 §10-2;_tmp_seq32/p02-rotate.txt) —— 夹具 = 4 组 × 4 节点 = 16 节点(组内 peer 共享、跨组不共享),包 5,255,225 B(= 序㉔ E1 同值)⇒ 6 块;组名 ops|grp0..3;⚠️ 不预置内容(预置会让 local 恒命中 = 假绿 —— 本棒首版夹具即栽在此处,已改正)。

  • 轮换前:第 1 轮(冷启动)回源 = 21,021,572 B = 4.0001 × 包 = 1 × 包/组(local/peer/origin 命中 = 0/72/24)|第 2 轮(稳态)回源 = 0(命中 96/0/0)。
  • 轮换(epoch 1 → 2,4 组同时):块 id 改变 24/24 = 100.00%;旧块未清(各节点 storeBytes 仍为 5,255,393)。
  • 轮换后:第 1 轮回源 = 21,021,572 B(与冷启动逐字节同值)|第 2 轮 = 0。
  • ⇒ 轮换后首轮 ÷ 轮换前冷启动 = 1.0000,即轮换的代价 = 一次完整冷启动(全量回源);1 × 包/组 的 E1 口径在轮换后依然成立(组内共享未退化),"之后回落"= 第二轮起 0 回源。
  • 🔴 对照腿(⛔ 防假绿):同一夹具不轮换跑第 3 轮 ⇒ 回源 = 0 ⇒ 证明"回源是轮换引起的",⛔ 不是夹具恒回源。
  • ⚠️ 附带代价(本轮新读数):节点 storeBytes 5,255,393 → 10,510,786(块数 6 → 12)⇒ 旧 epoch 密文变垃圾且不会自动清(要靠 LRU 淘汰 / 手动清缓存)⇒ 轮换 = 一次全量回源 + 一份等量的缓存垃圾。decryptRejected 全程 0(旧块按"id 不同"被绕过,⛔ 不靠"解密失败"发现)。

③ 低熵明文的安全性(原 §10-3;_tmp_seq32/p03-lowentropy.txt) —— 口径 = 只做定性,⛔ 不写"很安全"。本棒把攻击面拆成两个攻击者模型分别取证(⛔ 不分清就会把结论写反):

  • 模型 A(持组密钥者 = 组内成员 / 拿到 0600 密钥文件者):域 |D| = 1000 条 8 B 短块(blk-0000…),victim 明文 = blk-0777;字典枚举 1000 条耗时 11.644 ms(≈ 85,878 条/秒)、查表命中 1、恢复明文与原值一致 ⇒ 低熵明文对持钥者可枚举恢复(外推:本机单核速率下域 2^32 ≈ 14.7 小时)。🔴 这正是"组内互信"这一代价的具体含义。
  • 模型 B(中继 / 无密钥者):拿另一把密钥自造字典 1000 条 ⇒ 查表命中 0;即使已持有 1 对(明文, 密文)⇒ 能预测的其他明文密文数 = 0 / 999。机理 = iv = HMAC(组密钥, 明文) ⇒ 无密钥者算不出任何候选密文 ⇒ 枚举不可行(这一点与"朴素 iv = H(明文) 的收敛加密"不同,是本方案的一个真实增益)。
  • 模型 B 确实能观测到的(泄露面):语料 10,000 块取自 100 个不同明文 ⇒ 不同密文数 = 100(= 明文不同数)⇒ 中继可读出"这次传输里有几个不同值"+重复率 / 频次结构,并可在同 epoch 内跨节点、跨时间关联同一块;极端域(如 {yes, no})⇒ 中继看到 2 种密文,内容空间已被压到 2 选 1。
  • 反向腿(⛔ 防"随便都能破"的误读):1000 条 64 B 高熵明文 ⇒ 不同密文数 1000(无碰撞)⇒ "可枚举"是低熵特有,⛔ 不是本加密的普遍失效。
  • ⇒ 定性结论:本方案把中继的可见面压到"相等性 / 重复率",明文保密成立;但"内容不可推断"不成立(低熵 / 高度重复的载荷会泄露结构),且持钥者侧无保密性。若要治低熵,只有加随机化 ⇒ 会牺牲块级共享 = 真取舍、需另立单。

④ 过渡窗口三档(原 §10 第 6 行;_tmp_seq32/p04-epoch.txt) —— 已在 §3.9 CONTENT_EPOCH_GRACE_MS 行写入曲线(小 60 s、中 24 h、大 7 d;归零点 = 最晚 retiredAt + 本值 = 24 h / 48 h / 8 d;窗口内 epochExpired=0;超窗后逐 epoch 点名 + decryptRejected +1),本处只记两条口径:⚠️ 时钟走注入 now ⇒ 结论完全可复现、⛔ 不依赖真实等待;⚠️ 本夹具的写 epoch 全程 5/5 可解(当前密钥恒可用)。

⑤ 口径定稿(原 §10-5 / §8.13-①) —— 见 §6 OBS-23 行末的「口径定稿」 与 交接单 §11.5:保留「缺省不启用 ⇒ SKIP + 留痕」,单 B §4/S6 的「对旧生产必 FAIL」作废,"先红"腿改由权限腿承载。⚠️ ⛔ 单 B §4 / §6 的正文一个字都没改 —— §8 是截断点、§8 前前缀 149596460288ca1a5b7abddc490f7909 是不变量(改它 = 打穿证据链)⇒ 定稿写在 §8 之后(§8.14 / §11.5)。

⑥ 组密钥签发固化(原 §8.13-②) —— 已把 scripts/overlay-keyring.cjs(18153 B,md5 f1855eaef30f71fbca00ac0a92865c0e)铺到 47 两处正式路径:/opt/dsh-relay/scripts/overlay-keyring.cjs(就地更新 —— 旧版 12915 B、grep -c sign-group-key = 0,已入回滚点)+ /opt/dshs/scripts/overlay-keyring.cjs(此前不存在 = 单 §8.13-② 记的那处缺口)。回滚点 = 47 /opt/dsh/backups/seq31-20260918-073857/。在机上验证:sign-group-key rc=0(凭据 274 B,network=ops group=relay epoch=1 keyId=3560130eb0e5e4aa,载荷内密钥本体出现次数 = 0、keyId = 1)|verify-group-key rc=0|篡改 epoch ⇒ signature-mismatch rc=1|交叉核对(传 --epoch 9)rc=1。🔴 该动作不重启任何单元(它是纯 CLI,⛔ 无进程加载它)⇒ 零运行时影响。


§11.4 补记(2026-09-18 10:0x,序㊱ 执行棒 · P1「节点一键加入 + 分组准入」;同在 §10 指纹口径之外)

① 零回归三件套(开工基线现核 = 201/200/0/1 / 12P / 23P,三者皆 rc=0)

件 本次读数 与基线
npm.cmd test(Node 22.22.2) 201 tests / 200 pass / 0 fail / 0 cancelled / 1 skipped 一致
overlay-failover-drill.cjs --scene all --table 12 PASS / 0 SKIP / 0 FAIL(幕4-C 实测 19370 ms / deadline 30000 ms) 一致
overlay-probe.cjs --table 24 PASS / 1 SKIP / 0 FAIL(⛔ 0 FAIL) 详见 ②(少 1 PASS、多 1 SKIP,已归因)

② OBS-09 由「在册」退化为「不在册 ⇒ SKIP」(🔴 本棒唯一未复原项**,如实记)**

  • 基线:PASS OBS-09 … w-106:39027=401(端点表 2 条)。
  • 现状:SKIP OBS-09 在册实例面 无|不在册 2 项:本机:20000,对端:21000(端点表 1 条 = w-106 port=19000 localPort=43061)。
  • 归因(均有原文):本次运行的 failover 演练重启了两端 relay / worker ⇒ OBS-01(used=1)/OBS-04(identityOk=1)/OBS-08(端点表 0 条)同时转红;为恢复,执行 47 systemctl restart dshs + 106 systemctl restart dshs-worker(开发环境服务器;动手前已声明)⇒ 三者全部回到绿,但 106 worker 的重注册读数为 accepted=[19000],而演练刚结束时是 accepted=[19000,21000](106 journalctl -u dshs-worker 09:48:29 vs 09:56:30 两行原文)⇒ 实例端口 21000 的注册未随 worker 进程重建。⚠️ 106 实例 :21000 与 47 实例 :20000 都在监听、门户 200 ⇒ 无业务中断。
  • ⚠️ 这是 OBS-09 的设计内合法态(「不在册 ⇒ SKIP + 留痕」,⛔ 不判红)⇒「0 FAIL」成立;但较基线少 1 个 PASS,不当作没发生。
  • 回头条件:下一棒收口若 OBS-09 仍为 SKIP,或出现「跨机实例面访问不通」⇒ 升级为独立小项(第一步查"实例 → 本地 worker 的端口注册"链路,⛔ 不先猜 relay)。

③ 真机端到端链路(_tmp_seq33/chain47.sh,全程在 /tmp,⛔ 未碰生产注册表 / 未碰生产签名者密钥) —— rc=0,11 步全绿:join 四步 4/4 → 收单 status=pending → 同一张邀请二次收单 ⇒ invite-already-used(J1) → 批准前派生为空(收单 ≠ 批准)→ 批准后 DSHS_RELAY_DIALERS="lab-net/lab-1" → 三张网(lab-net / ops / u:5)各得自己那条、零共享(J5) → 申请单内私钥出现次数 = 0、节点私钥权限 600(J6)→ 一次性台账条数 3 ≡ 已收单次数 3。

④ 真机自检读数(/var/lib/dshs/overlay/selfcheck.json,用修复后的 CLI --out 重跑后现取) —— platform=linux|nodeKeyMode=600 ⇒ nodeKeyModeOk=true(Linux 权限腿成立)|joinOk=true(四步 4/4)|application.bytes=533、hasPrivateKey=0、shapeOk=true / hostMatches=true(CLI 回环)|ledger={first:ok, second:invite-already-used}|namedFailures=5(阈值 ≥ 4)、silentRejections=0|registry.auditOk=true、misfiled=0|derivation.crossNetworkShared=0。

⑤ 一条真机缺陷(已修 + 已补判据 + 已先红后绿) —— 控制面 CLI 的 --file 键名混用:它本是 apply 的申请单入参,却被注册表文件路径复用 ⇒ apply --file <申请单> 把注册表也指到申请单 ⇒ 收单在第 4 步炸,而 nonce 已被原子占位(对外症状 =「收单失败 + 同一张邀请再也用不了」)。修 = 注册表覆盖键改名为 --registry;先红后绿 = 临时改回 opt('file') ⇒ 新增的 E10(CLI 级真回环) 转红并报出同一句「注册表 …/app.json 形状非法」;还原 ⇒ 33/33 全绿。⚠️ 调库式的 E9 抓不到这一类缺陷(它绕过了 CLI 的参数解析)—— 这是"判据必须走真实路径"的又一次落地。

⑥ 一处"刻意不判"(⛔ 防假判据) —— OBS-24 原拟判「同一 hostId 出现在 ≥2 张网 ⇒ FAIL」,真机夹具证明这是合法态(客户端自取设备名会跨用户撞名;单测 D1 已钉死"两张网各自独立、互不影响")⇒ 该腿没有判别力(永远 PASS)。已降级为信息项,改判「结构性错桶(auditDerivation.misfiled)= 0」+「派生桶数 ≡ 有 approved 的网数」(⛔ 防把多张网合并成一张)。

⑦ P1 出口项 / P2 接口点(本棒自决,可推翻) —— 在册文件集里的 src/web/routes/overlay-nodes.ts(管理面 API:列出 / 批准 / 移除节点)本棒不建。理由:① 与 P1 硬门「零新增暴露面」 风险最高(它是唯一会动 HTTP 面的在册项);② S1–S4 与 J1–J6 的验收均不依赖它(S1 明写"只读面先行",而控制面 CLI list 已是只读面);③ 交接单 §7「权限影响评估」本身就是 P2 专用。⇒ 移入 P2,与 S5 的权限影响评估一并做。

⑧ 收口复核(2026-09-18 10:0x–10:4x · 序㊱ 收口复核棒;⛔ 只复核,⛔ 未开工 P2) —— ① OBS-09 由 SKIP 回到 PASS:开工读数 SKIP … 不在册 2 项:本机:20000,对端:21000(端点表 1 条);链路取证 = 106 /healthz 原文 {"instances":0,"tunnel":{"ready":true,"ports":[19000]}} ⇒ reconcileTunnel() 无 live 端口可声明(根因 = orchestrator.ts:558 listUserInstances() 取 mains,而认领只写 this.adopted(:1492)⇒ 认领到的实例平台侧不可见);复原 = 走设计内路径(临时 session 一次真实用户可见面访问 ⇒ 实例被 cleanStaleScopes 自然替换 ⇒ 10:18:34 dsh-100002-7d1c8cbf.scope 起、新实例经 launch 路径声明端口)⇒ /healthz = {"instances":1,"tunnel":{"ready":true,"ports":[19000,21001]}}、用户可见面 200 / 62451 B / 502 标记 0、临时 session 已删。② 本表值格一处实测替换:PEER_INSTANCE_PORT 21000 → 21001(+ PEER_AGENT_PORT 括注同步 ports=19000/21001)—— 因替换落点漂移(findFreePortInRange 上扫,替换时 21000 仍被旧 scope 占着),⛔ 未为了迁就旧值去重启生产实例;§10 指纹 695dcb4d88a778840322bc221417d339 → 2ae954e2e07b47651a84e597c19a3444。③ 零回归三件套 = npm.cmd test 201/200/0/1|--scene all 12P/0S/0F(幕4-C 17375 ms / deadline 30000)|探针 25 PASS / 0 SKIP / 0 FAIL(rc=0);node --test test/overlay-join.test.mjs 33/33。④ 两条点名(⛔ 本棒未改码):(a) 🔴 OBS-09 的"在册"判据钉死会漂移的实例端口 ⇒ 每次「访问时自然替换」后都可能假 SKIP(同步表值只治输入、不治判据)⇒ 交序㊲;(b) ⚠️ 探针只读 47 的 RELAY_STATUS_URL,而 worker 可能挂在 106 中继(--scene all 跑完实测 ep=[] / used=1 / idOk=1,= 潜在假红)⇒ 同交序㊲。⑤ 边界自证:⛔ 未 commit / push(HEAD 09ce76f)|⛔ 未改任何生产配置值|🔴 RELAY_FAILOVER_COOLDOWN_MS=0 计数 0|⛔ 未开工 P2|⚠️ 对生产的 3 个动作(guest 实例一次设计内替换 / 106 dshs-relay 停启各一次 / 本表文档值同步)均属 R8、动手前已声明、无不可逆项。

§11.5 补记(2026-09-18 11:1x,序㊲ 执行棒 · 观测面去常量依赖:OBS-09 在册判据跟随事实 + 观测点假红可分;同在 §10 指纹口径之外)

① 零回归三件套(三件都在改动之后跑)

件 本次读数 与基线
npm.cmd test(Node 22.22.2) 201 tests / 200 pass / 0 fail / 0 cancelled / 1 skipped(rc=0) 逐字一致
overlay-failover-drill.cjs --scene all --table 12 PASS / 0 SKIP / 0 FAIL(rc=0;幕4-C 实测 18353 ms / deadline 30000 ms) 一致
overlay-probe.cjs --table 25 PASS / 0 SKIP / 0 FAIL(rc=0) 一致

⚠️ 两处口径说明:❶ npm.cmd test 没有 --table 参数(它是测试入口,不是覆盖网络脚本)⇒ "三者都带 --table" 对它不适用;另两件均已带 --table "<本表绝对路径>"。❷ 探针不在 npm test 的覆盖内(测试输出里 grep -c overlay-probe = 0)⇒ 探针自身的零回归 = 上表第 3 行那次真机跑。

② OBS-09 去常量依赖 —— 先红后绿(原文级;两侧逐行 diff 只差 OBS-09 一行)

夹具 = 本表副本(PEER_INSTANCE_PORT 值格回写成漂移前的 21000)+ 真实 47 /status(事实 = 实例在 21001)⇒ 正是「表值过期、事实已漂移」这一实际场景。

  • 🔴 红(旧码 · 旧口径) = SKIP OBS-09 在册实例面 无 (阈值 ∈ {200,401})|不在册 2 项(记 SKIP 的依据):本机:20000,对端:21000 ⇒ 旧判据拿表值 21000 去 endpoints[] 里找 ⇒ 找不到 ⇒ 假 SKIP(与序㊱ §8.8-⑥-1 记的现象逐字同型)。
  • ✅ 绿(新码 · 同表同夹具) = PASS OBS-09 在册实例面(派生 1 条):w-106:42717=401 (阈值 ∈ {200,401}) ⇒ 派生集来自 /status.endpoints[](port=21001 / localPort=42717),表值 21000 已完全不参与判据。
  • 🔴 旧夹具口径已退役(⛔ 不让它静默解释):--instance-fixture 401,401 ⇒ rc=2 且报 ❌ --instance-fixture 项 "401" 形态不合法。序㊲ 起口径 = **按实例端口映射**(如 "--instance-fixture 21001=401");⛔ 旧的位置式 "<本机码>,<对端码>" 已退役(判据不再依赖表内固定端口)
  • 🔴 派生端口缺码 = FAIL 并点名(⛔ 不静默放行):不给码 ⇒ FAIL OBS-09 在册实例面(派生 1 条):w-106:42717=? (阈值 ∈ {200,401}) |❌ 缺码 21001(夹具未给该端口的码 / 真机取不到); 给成 20000=200(旧端口的码)同样 FAIL ⇒ 只有"派生出来的那个端口"的码才算数,⛔ 拿旧端口糊弄不过去。

③ 「空表可分」—— 同一份 47 输入 ⇒ 三种可分辨输出(原文级)

按对端中继视图(ssh test106 'curl -s 127.0.0.1:20080/status',只读回环)分类:

  • (甲) 真机 · 演练态(--scene all 跑完、⛔ 未做任何复原)—— 47 = endpoints: [] / capacity.used=1 / counters.identityOk=1(= §8.8-④ 记的 ep=[] / used=1 / idOk=1,独立复现);106 = endpoints = [w-106:19000, w-106:21001]:
    • FAIL OBS-01 在册节点 used=1 (阈值 ≥ 2)
    • FAIL OBS-04 identityOk=1 (阈值 ≥ 2)
    • SKIP OBS-08 端点表 0 条 ⇒ SKIP + 留痕|47 视角端点表为空,但**对端中继**(test106)声明了端口:w-106 ports=19000/21001 ⇒ 客户端挂在**另一台中继**上(⛔ 非故障、⛔ 不判红)
    • SKIP OBS-09 在册实例面 无(从 /status.endpoints[] 派生为空:端点表 0 条 = agent 0 条 + 离线 0 条) ⇒ SKIP + 留痕|…(同一句归类) ⇒ 21 PASS / 2 SKIP / 2 FAIL(rc=1)。🔴 旧口径对照(同态):旧 OBS-08 的 eps.length > 0 一条会恒 FAIL ⇒ 分不出"挂在别处"与"确实没挂" ⇒ 这正是被点名的假红,本棒已消除。⚠️ 余下 2 条红是 OBS-01/OBS-04 的计数判据(used/identityOk 只有 1),⛔ 与"端点表为空"无关。
  • (乙) 同输入 + 对端中继没有会话(--peer-status-fixture = 真实 106 视图,其 sessions=[]):
    • FAIL OBS-08 端点表 0 条 ⇒ FAIL(两台中继都无该客户端会话)|对端中继(test106)在运行但**没有任何会话在声明端口**,而 47 视角端点表也为空 ⇒ **该主机确实没挂客户端**
    • SKIP OBS-09 …(同一句归类) ⇒ "挂在别处"≠"确实没挂"≠"不可判",三者可从输出直接读出。
  • (丙) 同输入 + 未给对端中继视图:
    • FAIL OBS-08 端点表 0 条 ⇒ FAIL(不可判)|对端中继(test106)**不可判**:夹具模式未给 --peer-status-fixture(⛔ 不去 ssh)
  • (丁) 端点表非空、但派生为空(另有 agent 端点在册)⇒ 只命中 OBS-09 的 SKIP 腿:
    • SKIP OBS-09 在册实例面 无(从 /status.endpoints[] 派生为空:端点表 1 条 = agent 1 条 + 离线 0 条) ⇒ SKIP + 留痕|…
    • PASS OBS-08 端点表 1 条 / 离线 0 条 ⇒ 两种"空"确实不是一回事("端点表空" ≠ "派生为空")。

🔴 复原(按序㊱ §8.8-④ 同款;⛔ 未重启任何 worker、⛔ 未换实例):停 106 dshs-relay ⇒ 约 15 s 后 47 视角回落为 used=2 / 端点 = [(19000, 39815), (21001, 44231)] ⇒ 启回 106 dshs-relay(active)。复跑探针 = 25 PASS / 0 SKIP / 0 FAIL(rc=0),PASS OBS-09 … w-106:44231=401 —— ⚠️ localPort 由 42717 → 44231,判据跟着事实走、⛔ 无须改表值,这正是本项要拿到的收益。

④ 边界自证:⛔ 未 commit / 未 push(HEAD 09ce76f;git status --short = 10 处,与开工逐数相同;本棒只动 scripts/overlay-probe.cjs 一个仓内文件)|⛔ 未改任何生产配置值(无 drop-in / env / nft·nginx / bwrap 改动,本表值格一律未动)|⛔ 未新增任何暴露面(新读数走回环 + 既有 ssh;复跑 OBS-11 多出 0 缺失 0、relay 口仍 1/1 绑回环)|🔴 RELAY_FAILOVER_COOLDOWN_MS 命中数 0(⛔ 未写进任何回滚 / 演练 / 夹具路径)|⛔ 未开工 P2(P2 仍待用户拍板权限影响评估)|🔴 密钥本体不经网络 / 不经 relay(本棒不碰密钥)。

⑤ 如实留档(必填):

  1. ⚠️ OBS-01 / OBS-04 在演练态下仍会红(MIN_HOSTS = 2 / MIN_IDENTITY_OK = 2,而演练态 47 视角只有 manager 一条会话)—— 它们是计数判据、不是"端点表空不空"的判断 ⇒ 按范围⛔ 未改;处置 = 收口时做一次 106 relay 停启让 worker 回落 47(与序㊱ §8.8-④ 完全同款),复原后两者即绿。
  2. ⚠️ OBS-09 的取样面变化:47 本机实例面(:20000)结构性不在 endpoints[] 里 ⇒ 不再是本项取样点;⚠️ 判据能力无净损失 —— 旧口径下它永远落在"不在册"一侧、从未进入 bad 判定(源码文件头有原话)。本棒⛔ 未新增取样源(若日后要给本机实例面也上判据,属独立小项)。
  3. 🔬 一条文档口径歧义**(⛔ 非本棒引入;⚠️ 已核代码、⛔ 不凭印象下结论):文件头总括写"夹具模式…把远端原文喂进来 ⇒ 不 ssh",但 OBS-24 / OBS-25 的分支是 if (<各自夹具> !== undefined) { 读夹具 } else { 去 ssh }(无 fixture 守卫)⇒ 只给了 --status-fixture 的一次夹具跑里,这两行其实是生产读数**(本棒夹具跑实测:OBS-24 = "注册表 形状合法|网 0 张",OBS-25 = "入口在册 ✅(4/4 步)",stderr 可见多次 ssh)。⚠️ 两头都说得通:OBS-24/OBS-25 各自的用法行写的是"(真机 = cat …;缺省/文件不存在 ⇒ SKIP + 留痕)" ⇒ 与代码一致;只有文件头那句总括与事实不符。⇒ ⛔ 按"只做被明确要求的事"未改;建议二选一:(a)给这两项补"夹具模式一律 SKIP"的守卫(⇒ 夹具跑变成封闭的,与本线"夹具 = 零副作用实证手段"的定位一致)、或(b)把文件头那句限定为"该 OBS 的远端原文喂进来 ⇒ 不 ssh"(⇒ 承认夹具跑可混生产读数)。⇒ ✅ 序㊳ 已按 (a) 落地:两处取数改为 if (<夹具> !== undefined) { 读夹具 } else if (fixture) { SKIP + 留痕 } else { ssh }(守卫排在 ssh 之前)+ 文件头新增 「封闭性」硬约束段(含"新增可 ssh 取数的 OBS 必须照此顺序写"的自检口径)。
  4. ⚠️ DRILL_RELAY_UNIT 被本棒复用(用于把"对端中继未运行"与"对端中继读取失败"分开)—— ⛔ 未新造键(它记的就是"两台机的 relay 单元名"这个事实,与 JITTER_LIMIT_MS 同款做法);⚠️ 键名 DRILL_ 前缀与用途已不相称,建议下一棒按需改名(文档项)。⇒ ✅ 序㊳ 已改名 = RELAY_UNIT_NAME(§3.9 键行已同步;引用点 3 处全改:overlay-failover-drill.cjs / overlay-probe.cjs / 本表;值格未动仍 dshs-relay)。
  5. 📁 证据落点 = 工作区 _tmp_seq37/(红/绿原文、演练态双视图、四组夹具、npm test 全文);⛔ 未清理 —— 它本身就是"先红后绿"的可复现证据。

§11.6 补记(2026-09-18 11:4x–12:0x,序㊳ 执行棒 · 两项收口:探针夹具封闭性 + DRILL_RELAY_UNIT 键名对齐;同在 §10 指纹口径之外)

① 探针夹具模式封闭性(✅ 用户拍 A 方案 = 补守卫,⛔ 不是改文件头口径)

改法(OBS-24 / OBS-25 两处):取数分支由 if (<夹具> !== undefined) { 读夹具 } else { ssh } 改为 … else if (fixture) { SKIP + 留痕 } else { ssh } —— 守卫排在 ssh 之前。文件头新增 「封闭性」硬约束段(夹具模式下任何取数必须来自夹具;缺该夹具 ⇒ SKIP + 留痕,⛔ 绝不回落去 ssh;并写明"新增可 ssh 取数的 OBS 必须照此顺序写"+判据=夹具跑里出现任何一次 ssh 即违约)。

先红后绿(原文级 · 两侧逐行 diff 只差 OBS-24 / OBS-25 两行) —— 两侧用同一份 --table 副本(把 SSH_TARGET_47 / SSH_TARGET_106 指向 127.0.0.1、SSH_PORT 指向 1 ⇒ "是否 ssh"变成可观测差异;夹具 = 真实 47 /status):

  • 🔴 红腿(守卫摘掉,还原序㊱ 形态):FAIL OBS-24 ❌ 注册表**读取失败**(⛔ 与"不存在"可分):Command failed: ssh -p 1 -o BatchMode=yes 127.0.0.1 if [ -f /var/lib/dshs/overlay/nodes.json ]; … + ssh: connect to host 127.0.0.1 port 1: Connection refused;OBS-25 同款。真 ssh 调用计数(grep -c 'Command failed: ssh')= 2。
  • 🟢 绿腿(现产物):SKIP OBS-24 夹具模式未给 --nodes-fixture ⇒ SKIP + 留痕(⛔ 不去 ssh 读生产)、SKIP OBS-25 夹具模式未给 --join-fixture ⇒ SKIP + 留痕(⛔ 不去 ssh 读生产)。真 ssh 调用计数 = 0。

可分性(新守卫没把"给了夹具但读不到"也吞成 SKIP):给不存在的夹具路径 ⇒ FAIL OBS-24 ❌ 注册表**读取失败**(⛔ 与"不存在"可分):夹具不存在:…/NO_SUCH_nodes.json(OBS-25 同款)—— 与"未给夹具 ⇒ SKIP"可分 ✅。

② DRILL_RELAY_UNIT → RELAY_UNIT_NAME(键名与用途对齐)

理由:它记的是"两台机的 relay 单元名"这一事实,且被演练脚本与探针 OBS-08 共用 ⇒ 原 DRILL_(演练专用坐标)前缀与用途不相称。值格未动(仍 dshs-relay)⇒ ⛔ 零个新增 / 改动生产 env。引用点 3 处全改:scripts/overlay-failover-drill.cjs(r.need + 一行原因注释)|scripts/overlay-probe.cjs(OBS-08 的判别器)|本表 §3.9 键行 + 小节标题注(§11.5-⑤-4 已同步为"已改名")。⚠️ 文档库档案不改(04-调整方案/117-… 与归档单 T13-中继失败切流.md 记的是当时事实,⛔ 不追改)。

③ 零回归三件套(三件都在改动之后跑)

# 命令 读数 开工基线 判定
1 npm.cmd test(Node v22.22.2) 201 tests / 200 pass / 0 fail / 0 cancelled / 1 skipped(rc=0) 同 ✅ 逐字一致
2 overlay-failover-drill.cjs --scene all --table 12 PASS / 0 SKIP / 0 FAIL(rc=0;幕4-C 20770 ms / deadline 30000) 12P/0S/0F ✅
3 overlay-probe.cjs --table(真机) 25 PASS / 0 SKIP / 0 FAIL(rc=0) 25P/0S/0F ✅

⚠️ 两条口径沿用序㊲:❶ npm.cmd test 没有 --table 参数(它不经参数表,此处"三件都带 --table"只对后两件成立);❷ 探针不在 npm test 覆盖内。

④ 演练副作用与复原(按序㊱ §8.8-④ / 序㊲ §11.5 同款;⛔ 未重启任何 worker、⛔ 未换实例) —— 跑完 --scene all 后 47 视角确为空(endpoints=[] / used=1 / identityOk=1)⇒ 探针呈 21 PASS / 2 SKIP / 2 FAIL(2F = OBS-01 used=1 (阈值 ≥ 2)、OBS-04 identityOk=1 (阈值 ≥ 2) —— 二者是计数判据、⛔ 与"端点表为空"无关;2S = OBS-08 / OBS-09 走"对端中继声明了端口"腿)⇒ 停 106 dshs-relay ⇒ 约 22 s 后 47 视角回落 used=2 / identityOk=2 / 端点 = [(19000, 33213), (21001, 42783)] ⇒ 启回(active)⇒ 探针回 25 PASS / 0 SKIP / 0 FAIL(rc=0)。⚠️ localPort 由 42717 → 42783(本次回落又漂一次;改动后首次真机跑时为 44231)—— 判据跟着事实走、⛔ 无须改表值,这正是序㊲ 要拿到的收益。

🔑 顺带取得键名改名的真机端到端证据(比手跑单条命令更强):演练态那次 SKIP OBS-08 的原文是「…但对端中继(test106)声明了端口:w-106 ports=19000/21001…」⇒ 说明判别器真的走完了真机分支那条 ssh(内含 systemctl is-active ${RELAY_UNIT_NAME})⇒ ✅ 改名后真机取数可用。

⑤ 边界自证:⛔ 未 commit / 未 push(HEAD 09ce76f;git status --short 11 处 = 序㊲ 的 10 处 + 本棒新增的 scripts/overlay-failover-drill.cjs,逐条可解释)|⛔ 未改任何生产配置值(本表值格一律未动)|⛔ 零新增暴露面(OBS-11 多出 0 缺失 0、relay 口仍 1/1 绑回环)|🔴 RELAY_FAILOVER_COOLDOWN_MS 命中 0(⛔ 未写进任何回滚 / 演练 / 夹具路径)|⛔ 未改 bwrap / nft / nginx|⛔ 未开工 P2(P2 待拍板清单 = 交接单 §8.11)|🔴 密钥本体不经网络 / relay(本棒不碰密钥)。

⑥ 证据落点 = 工作区 _tmp_seq38/(red.txt / green.txt / l3_badfixture.txt 及各自 .err、real.txt、probe_drillstate.txt、probe_final.txt、drill.txt、npmtest.txt、mk_fixtures.py、repo/);⛔ 未清理 —— 它本身就是"先红后绿"的可复现证据。

§11.7 补记(2026-09-18 12:2x,序㊴ 规划棒 · P2 拍板落地 + 本线首份 P2 可执行单;同在 §10 指纹口径之外)

① 用户拍板(原话,⛔ 不许改写):1 用户可设置,默认开启提示用户 2 乙(2026-09-18 12:22) ⇒ 两问闭合:① 打洞能力交给用户自己控制(可设置),平台默认开启且必须提示用户(⛔ 不是简单的"开 / 不开");② 云安全组 UDP 入站的来源范围 = 乙(固定小段、仅放行对端节点 IP,⛔ 非 0.0.0.0/0)。

② 本表变更:§8 新增 ⑩ P2 直连的 UDP 入站面 一行 + 附注补一条口径更新;🔴 §10 指纹 2275870a5a9eb1964a648f2950dee528 → d408d640246a980f702fe7b0a2895219(现算并同步)。⚠️ 值格一律未动 ⇒ ⛔ 零个新增 / 改动生产 env(⑩ 只登记)。

③ 产物 = 本线首份 P2 可执行单:工作区根 交接单_覆盖网络直连与P2P_20260918.md(归档号 132 · 239 行 · §8 前前缀 8925c2460810c6dc5cdbc075b6cdaed2)。结构 = 用户口径(§1)+ 只读前置 8 条已取证事实(§2)+ 在册文件集(§3)+ 技术路线(§4)+ 判据 D1–D8(§5)+ 步骤 S5 / S6 / S7(§6)+ 权限影响评估(§7 · 已拍板)+ 回报格式(§8)+ 回头条件(§9)+ 未验证项(§10)。🔴 两条关键判据:D7 = peer 接线后块 id 必须复算(⛔ 漏了不算通过)|D8 = 云安全组 UDP 段 source ≠ 0.0.0.0/0。

④ 三条如实登记:

  1. 🔴 乙方案的技术现实(同时写进新单 §4.5):对端为家宽 / 移动时公网出口 IP 会变 ⇒ 规则会失效、且云安全组规则数有上限 ⇒ 序㊴ 自决(可推翻) = 乙-1 起步(只对有固定公网 IP 的节点开;家宽节点本期走中继,⛔ 不影响可用性);乙-2(半自动)/ 乙-3(云 API 自动化,🔴 需云 AK/SK)登记为新单 §9 回头条件 —— ⛔ 本棒未请求任何凭据。
  2. ⚠️ 旧单正文一字未改(交接单_节点一键加入与分组准入_20260918.md §8 前前缀 085e28b56a08aa1f01999bdb3e238911 逐字不变 ✅)—— 拍板落地写在其 §8.12(截断点之后);P2 开工依据移交新单。
  3. ⚠️ 一条遗留(仅报告,⛔ 未动手):dsh-server-docs/04-调整方案/.lock-131 仍在(序㊱ P1 的占号窗口未释放)⇒ 后果 = 后续取号者永久跳过 131(本棒已现核 ⇒ 取 132)。⛔ 本棒不动他人锁类目录。

⑤ 边界自证:⛔ 零代码改动 · ⛔ 零服务器改动 · ⛔ 未 commit / 未 push(HEAD 仍 09ce76f)· ⛔ 未改任何生产值(本表只加不改)· ⛔ 未开工 P2(命中 R5 的项虽已获拍板,本棒仍只登记不实施)。

§11.8 补记(2026-09-18 13:2x,序㊵ 执行棒 · P2/S5 落地:直连候选交换 + 打洞探测 + 开关/默认值/提示 + 只读观测面;同在 §10 指纹口径之外)

① 本表变更:§3.11 新增 12 键(DIRECT_ENV_KEY / DIRECT_DEFAULT_ENABLED / DIRECT_OFF_VALUES / PUNCH_PORT_BASE / PUNCH_PORT_SPAN / PUNCH_DEADLINE_MS / DIRECT_COOLDOWN_MS / DIRECT_PUNCH_MIN_OK / DIRECT_CAND_ACCEPT_MIN / DIRECT_CAND_REJECT_MIN / DIRECT_HINT_MIN_PARTS / DIRECT_SELFCHECK_FILE)+ §6 新增 OBS-26 / OBS-27 / OBS-28 三行。🔴 §10 指纹 d408d640246a980f702fe7b0a2895219 → 6b37bfd506758d882d9f803678f85d23(现算并同步)。⚠️ 值格一律未动 ⇒ ⛔ 零个新增 / 改动生产 env(DSHS_OVERLAY_DIRECT 在两机 /etc/dshs*.env + systemd 的命中数 = 0 ⇒ 走缺省即开)。

② 读数 = 探针 28 PASS / 0 SKIP / 0 FAIL(rc=0;基线 25P ⇒ +3 = OBS-26/27/28,⛔ 零回归)|npm.cmd test 201/200/0/1(与基线逐字相同)|--scene all --table 12 PASS / 0 SKIP / 0 FAIL(rc=0;幕4-C 23422 ms / deadline 30000)。产物 = 工作区根 交接单_覆盖网络直连与P2P_20260918.md §8(§8 前前缀 8925c2460810c6dc5cdbc075b6cdaed2 回填后逐字不变 ✅ ⇒ "§8 是截断点"这条口径的又一次自证)。

③ 一条"证据项 ≠ 判据项"的口径登记:OBS-26 附注写"真机的 ss -lunp 增量腿由 selfcheck --ss 另取" ⇒ 本棒把它做成带正对照的可信读数(只有 ssDuringOnCase ≥ 1 才认可 ssDuringOffCase = 0;正对照缺失 ⇒ 该 0 可能是"测量坏了" ⇒ 潜在假绿),但未把它并入判据(⛔ 不改已发布的阈值口径)⇒ 读数落点 = DIRECT_SELFCHECK_FILE#udp({ssBefore:0, ssDuringOnCase:1, ssDuringOffCase:0, ssAvailable:true},区间 [21100,21115])。

④ 🔴 D8 未实施(红):云安全组 UDP 入站仍未放行(双向实测均 发 20 收 0;宿主机侧 nft input policy = accept ⇒ 挡在云侧、⛔ 非 nft);拟录 乙-1 两条 = 目标 47.77.182.89 / 106.54.21.172 × 入站 × UDP × 21100/21115 × 源分别为 106.54.21.172/32 / 47.77.182.89/32 × 允许 —— ⛔ 零个 0.0.0.0/0(⛔ 拆分等价写法一并禁止,命中 交接单 §9-3 = 立刻停手回滚)。未实施的理由 = 属 §1 边界外第 ④ 类(需用户提供控制台访问方式 / 审批)⇒ 本棒 ⛔ 未索取、未硬编码任何云 AK/SK。逐条清单见交接单 §8.2-D8。

⑤ 边界自证:⛔ 未 commit / 未 push(HEAD 仍 09ce76f;git status --short 17 处 = 与开工逐数相同)· ⛔ 未改 src/net/relay/server.ts(零字节改动;network.ts 只加 isAllowedDialer)· ⛔ 未改 nft(72 行与收口态逐字一致)/ nginx / bwrap 参数 · 🔴 RELAY_FAILOVER_COOLDOWN_MS=0 命中 0 · 🔴 密钥本体 ⛔ 不经网络 / 不经 relay。

§11.9 补记(2026-09-18 14:0x,序㊶ 执行棒 · P2/S6:peer 档接线 + D7 块 id 复算;同在 §10 指纹口径之外)

① 本表变更 = 0:本棒 ⛔ 未新增任何键、⛔ 未改任何值、⛔ 未改任何阈值。🔴 §10 指纹 = 6b37bfd506758d882d9f803678f85d23(现算,与 §11.8 记的值逐字一致)—— 佐证"§11 小节在 §10 口径之外"。⛔ 零个新增 / 改动生产 env(DSHS_OVERLAY_DIRECT 两机命中 0 ⇒ 走缺省即开)。

② 读数 = 探针 28 PASS / 0 SKIP / 0 FAIL(rc=0;与 §11.8 基线同值、⛔ 零回归)|npm.cmd test 201/200/0/1(与基线逐字相同)|--scene all --table 12 PASS / 0 SKIP / 0 FAIL(rc=0;幕4-C 18990 ms / deadline 30000)|test/overlay-content.test.mjs 44/44。产物 = 工作区根 交接单_覆盖网络直连与P2P_20260918.md §8.8(§8 前前缀 8925c2460810c6dc5cdbc075b6cdaed2 写入后逐字不变 ✅)。

③ 🔴 D7 主判据(本棒唯一新增判据) = "peer 接线后块 id 与接线前逐字一致":idsDigest = ec70e7166ae24b1367c7c251ead8ad2979f66463ae3e6380eeda84fabb985c3a,四处同值(本机接线前 / 本机接线后 / 47 生产接线前 / 47 生产接线后)⇒ ⛔ 该值不作为表值固化(它是某次夹具的读数,夹具变则值变);口径才是不变量 = 取回字节必须复算出请求的同一个块 id,不符 ⇒ 丢弃且不算命中(idMismatches 计数,⛔ 不静默返"没有")。先红两腿已实测可用:扰动 blockIdOf(digest → fbb80585c86a4c492f6d71a7af9212121f35c2c43f2cc7018d08149e2cff5ac3)/拆闸门(peer-wire 判据 ⑦ 由 PASS 转 FAIL,篡改块被记成"命中成功"= 假绿本形)。

④ 🔴 D8 仍未放行(红 · 只读核对、⛔ 未实施):双向并发 UDP 实测 47 侧 sent=29 / recvLocal=0、106 侧 sent=28 / recvLocal=0(reason=deadline);两机 nft 72 行未动 ⇒ 挡在云侧。拟录 乙-1 两条(目标 47.77.182.89 / 106.54.21.172 × 入站 × UDP × 21100/21115 × 源分别为对端 /32)原样维持 —— ⛔ 无任何 0.0.0.0/0 形态、⛔ 未索取 / 未硬编码任何云 AK/SK。⇒ 真机双向直连不可达属已知,peer 档在 SG 未开时的正确形态 = 回落 wss 且块 id 不变(即 D7 所要验的形态)。

⑤ 边界自证:⛔ 未 commit / 未 push(HEAD 仍 09ce76f;git status --short 19 处 = 开工 17 + 本棒 2)· ⛔ 未改 src/net/relay/server.ts(零字节)· ⛔ 未改 src/net/relay/content/** 的块 id 计算路径(sha256(密文) 口径未动;扰动实验为临时且已逐字还原)· ⛔ 未改 nft(72 行)/ nginx / bwrap · 🔴 RELAY_FAILOVER_COOLDOWN_MS=0 命中 0 · 🔴 密钥本体 ⛔ 不经网络 / 不经 relay(/etc/dshs/content-group-key.json 两机仍 600)。

§11.10 补记(2026-09-18 14:5x,序㊷ 执行棒 · P2/S7:规模回归(候选数 / 直连成功率 / 切换耗时 / 回源字节 四项复测);同在 §10 指纹口径之外)

① 本表变更 = 0:本棒 ⛔ 未新增任何键、⛔ 未改任何值、⛔ 未改任何阈值。🔴 §10 指纹 = 6b37bfd506758d882d9f803678f85d23(现算,与 §11.7–§11.9 记的值逐字一致)—— 佐证"§11 小节在 §10 口径之外"。⛔ 零个新增 / 改动生产 env(DSHS_OVERLAY_DIRECT 两机命中 0 ⇒ 走缺省即开);⛔ 未新增任何监听口(收口后 ss -lunp 在 21100–21115 段 0 行)。

② 四项复测读数(⛔ 不冒充"网络能不能打洞")

# 项 本棒原始读数 表内落点 / 基线 判定
① 候选数 dshs@47 count=3 hosts=3(source=chain → 收口后 cache)|dshs-worker@106 count=3 hosts=3(resolves=744 source=chain)|直连候选准入 accepted=3 / rejected=9(全具名)/ silentRejections=0 / bucketsPerNetwork=true|规模夹具 2 网 × {4,16,64} 节点 ⇒ accepted 10/34/130(线性)、rejected 32/128/512(8 类原因各 perNet 条)、silentRejections=0、按网分桶 ✅ CAND_MIN=2(§6 OBS-21)|DIRECT_CAND_ACCEPT_MIN=2 / DIRECT_CAND_REJECT_MIN=3(§3.11) ✅
② 直连成功率 本系统(离线 NAT 模拟器,同一份 runPunchAttempt)正腿 24/24 = 1.0000 双向、p50 建连 155 ms;负腿 one-way(a/b) 各 12 全 0、both 12 全 deadline|真机 47→106 sent=21 recv=0、106→47 sent=28 recv=0(reason=deadline) DIRECT_PUNCH_MIN_OK=1(§6 OBS-28①)|HOLE_PUNCH_RATE_LOCAL(§3.2)= 分层可行性 2/2 —— ⚠️ 它是"能不能打洞",⛔ 不是"本系统成功率" ✅ 判据成立 / 🔴 冗余未建成(真机 0,D8)
③ 切换耗时 幕4-C 20871 ms / 30000(PASS);幕1-A 20605 ms;幕4-A 18815 ms —— 三处均在 deadline 内侧;收口后 jitter.p95AbsDeltaMs=6 ms / overThreshold=false 🔴 阈值权威落点 = §4 RELAY_FAILOVER_DEADLINE_MS = 30000 ms(⚠️ ⛔ 不是 §3.2 —— §3.2 是打洞率;四段分解在 §9 第 9 行,修后基线 20.9–23.9 s) ✅
④ 回源字节 夹具 = 同一份 5,255,225 B / 6 块(逐次计 origin 取块):1组×4节点 amp 1.0000(5,255,225 B)/ 1组×16节点 amp 1.0000 ` 4组×4节点 amp **4.0000**(21,020,900 B)/ 8组×4节点amp **8.0000**(42,041,800 B)|对照腿(peer 关)=4.0000 / 16.0000 / 16.0000 / 32.0000|🔴 **每组合计恒 1.0000** ⇒ **回源字节 ∝ 组数,⛔ 不随组内节点数增长**|运行时接线腿(8 组×4 节点)direct-优先 hits.direct=144 / hits.wss=0、idChecks=144 / idMismatches=0` §7 既有读数(序㉔ 1.0000 ⇄ 4.0000;序㊛ 4 组×4 节点 = 21,021,572 B = 4.0001×)

③ 与 §5(45% 中继口径)的关系:⛔ 本轮不触发 §5 的任何回头条件 —— §9-8「打洞失败率导致中继容量超 45% 设计值 ⇒ 回头重算 RELAY_MAX_HOSTS」未命中:真机打洞成功率 0(D8 未放行)⇒ 跨机流量本来就走中继,中继仍按 45% 异构口径(DESIGN_MARGIN = 0.45)设计、容量未承压(实测 used=2 / max=7515 / utilPct=0,OBS-20 余量 70 个百分点、软门 utilRefused=0)。⇒ 本棒 ⛔ 未动 RELAY_MAX_HOSTS(7515),⛔ 未重下发 capacity.conf。⚠️ 待 D8 放行、直连真机成功率可测后,§5 才有新增量可算(那将是一次独立回头)。

④ 与 §7(待测项汇总)的关系:本表 待测 单元格仍为 0 个(⛔ 本棒未新增键、⛔ 未把任何估值冒充实测)。⚠️ 交接单 §10 的三条(与 §7 口径不同层、⛔ 别混)本棒各有增量,但都 ⛔ 不写成"已判定":§10-1 本系统打洞成功率 ⇒ 模拟器 1.0000(n=24)/真机 0.0000(n=2 方向)并列,公网场景仍不可判定(真机腿被云安全组挡着);§10-2 中继带宽节省比例 ⇒ 内容面 75.00%(组内 4 人)/93.75%(组内 16 人)= 1 − 组数/节点数,真机直连 = 0;§10-3 直连切换耗时 ⇒ 18.8–20.9 s(n=3,均在 30000 ms 内侧)。

⑤ 读数与产物:探针 28 PASS / 0 SKIP / 0 FAIL(rc=0;开工前 + 收口后两次同值)|npm.cmd test 201/200/0/1(逐字同基线)|--scene all --table 12 PASS / 0 SKIP / 0 FAIL(rc=0;6m20s;⛔ 未套外层超时 ⇒ 末尾 restore() 正常执行、两台 relay active)|git status --short 19 处(与序㊶ 收口逐数相同 ⇒ 零新增条目)。产物 = 工作区根 交接单_覆盖网络直连与P2P_20260918.md §8.9(§8 前前缀 8925c2460810c6dc5cdbc075b6cdaed2 写入后逐字不变 ✅)。

§11.11 补记(2026-09-18 15:2x,序㊸ 执行棒 · 观测面缺口收口:① 探针在 106 上可跑 ② 参数表定位与 cwd 解耦 ③ 106 残留副本处置;同在 §10 指纹口径之外)

① 本表变更 = 0(键 / 值 / 阈值全未动):本棒 ⛔ 未新增任何键、⛔ 未改任何值、⛔ 未改任何阈值 —— 三项收口全部落在脚本侧(overlay-direct-probe.cjs / overlay-probe.cjs / overlay-failover-drill.cjs)。🔴 §10 指纹 = 6b37bfd506758d882d9f803678f85d23(现算,与 §11.7–§11.10 记的值逐字一致)—— 又一次佐证"§11 小节在 §10 口径之外"。⛔ 零个新增 / 改动生产 env · ⛔ 零新增监听口。

② 两条必须知道的"取数路径"事实(⛔ 不是阈值,故不进 §3/§6)

  • 参数表定位已与 cwd 解耦(overlay-probe.cjs + overlay-failover-drill.cjs)⇒ 从代码仓根 / 任意目录都能跑。候选目录链(写死)= --table(显式文件,最高优先、直接返回)> --dir > DSHS_OVERLAY_TABLE_DIR > 机器本地注册文件(DSHS_OVERLAY_TABLE_REGISTRY,缺省 ~/.dshs/overlay-table-dir,一行一个目录、# 注释)> cwd > 脚本自身目录及其上两级。
  • 🔴 "恰好 1 个才算合法"保留、且更严:任一候选目录 ≥ 2 份 ⇒ 立即报错;跨候选目录合计 ≥ 2 份 ⇒ 也报错(逐份列出)。一份都没找到 ⇒ 报错并列出所有搜过的目录 + 注册方法;⛔ 未放宽成"找不到就用缺省"(两腿防错实测:同目录 2 份 ⇒ rc=2;跨目录 2 份 ⇒ rc=2;探针与演练各测一次)。
  • 🔑 为什么需要"注册文件"(实测):本表不在仓内(git ls-files | grep -c 参数表 = 0)而在工作区根,且本机仓在 D:、工作区在 E:(不同盘)⇒ 纯路径推算到不了。⇒ 机器差异一律放 $HOME,⛔ 仓内零机器路径。换机器 / 换工作区路径 ⇒ 需补这一行(或改用 --table / --dir / 两个 env)。

③ 观测读数形状(序㊸ 起):106(worker / relay 节点)的 /opt/dshs-cluster/lib 无 registry.js(控制面注册表模块)⇒ overlay-direct-probe.cjs selfcheck 的 join 回读(判据 D3)那条腿在节点形态下具名降级: {"degraded":true,"degradedLegs":["joinConf"],"joinConf":{"available":false,"reason":"module-missing","missing":[{"spec":"../lib/net/relay/registry.js",…}]}} ⇒ 文本行 join 回读:⛔ 不可用(module-missing)—— 缺 …|⚠️ 这是「没装」不是「没采到」。其余子命令与其余读数照常(106 上 status / punch / selfcheck 三条全通,candAcc=3 / candRej=9 / silent=0 / 打洞双向=true / 冷却 300000 / 零值 throws)。🔴 ⛔ 不许静默返"没有":缺块不填 direct / readback("没装"与"跑了但读数为空"形状不同 = 可分)。

  • ⚠️ 随之而来的 OBS-26 子判据口径(登记在此,§6 行文本本棒 ⛔ 未改 —— 为保 §10 指纹):读数生产方显式给出 joinConf.available === false 时,OBS-26 的 join 子判据取 SKIP + 留痕(⛔ 不判红,detail 点名缺哪个模块);老形状(无 available 键)走原路径、判定与文本逐字不变。⇒ 下次有键值变更时顺带并入 §6 OBS-26 行。

④ 读数(零回归 + 106 侧):探针 28 PASS / 0 SKIP / 0 FAIL(rc=0;代码仓根不给 --table 一次 + 收口后工作区根一次,两次同值)|npm.cmd test 201/200/0/1(逐字同基线;duration_ms=53315.1989)|--scene all(无 --table,从代码仓根)12 PASS / 0 SKIP / 0 FAIL(rc=0;6m23s;幕4-C 17240 ms / 30000 ms;表路径原文 # 参数表=E:\ProgramData\AIProject\aliyun-dsh-server\参数表_覆盖网络_20260917.md)。106 侧:status / selfcheck --json / punch --peer 47.77.182.89:21100 三条 rc=0(punch 原文 窗内零收包(deadline 3000 ms,实耗 3000 ms,发 21 包)⇒ 判死 + 进冷却 300000 ms)。探针三处副本 md5 全同 = 4734686b5270059697ccbcf966c34ade(仓 / 47 /opt/dshs/scripts / 106 /opt/dshs-cluster/scripts);回滚点 = 两机 /opt/dsh/backups/seq43-20260918-150549/(md5 8d1ea60a01fc09fb5f56e492afb76fc9)。

⑤ 产物 = 工作区根 交接单_覆盖网络直连与P2P_20260918.md §8.10(§8 前前缀 8925c2460810c6dc5cdbc075b6cdaed2 写入后逐字不变 ✅)。🔴 D8(云安全组)本棒零动作(⛔ 未改云侧规则、⛔ 无 0.0.0.0/0 形态、⛔ 未索取 / 未硬编码 AK/SK)—— 仍在册待拍板。

§11.12 补记(2026-09-18 22:2x,序㊺ 执行棒 · 低熵块治理 · M1 四项读数(本线首次实测);同在 §10 指纹口径之外)

① 本表变更 = 0(键 / 值 / 阈值全未动):本棒 ⛔ 未新增键、⛔ 未改任何值、⛔ 未改任何阈值 —— 新增内容全部是读数。🔴 §10 指纹(现算)= 6b37bfd506758d882d9f803678f85d23,与写入前逐字一致 ✅(又一次佐证"§11 小节在 §10 口径之外")。⛔ 零个新增 / 改动生产 env · ⛔ 零新增监听口 · ⛔ 未动 CONTENT_BLOCK_SIZE。

② 度量对象(真实内容 · ⛔ 零合成字节)

  • S1 = 真实首屏包:GET https://admin.alotbuy.com/ 壳页 → 取页面内全部 /plugins/??…&rev=… combo,按页面顺序拼接(R4 临时会话,用完即删)。⚠️ 源码事实纠正:combo 由宿主运行时拼装(@deepseek-ai/dsh-client-modules 的 README.zh.md:快照式提供、按 3 KiB URL 上限分区)⇒ 盘上不存在"单文件产物",只能 HTTP 取。
  • S2 = 真实独立分发内容 = S1 内的 60 份 combo(每份 combo 即一份独立分发内容),其中 56 份是「整体 < 1 MiB 的单块内容」。
  • 🔴 口径纠正(重要):本棒实测 S1 总量 = 23,629,336 B,远大于本表 :146 注里引用的历史值 11,363,655 B。后者 = 最大单条 combo(本棒实测 11,794,471 B ≈ 12 块)—— 历史档案里的 11,363,655 B ⛔ 不是"全部 combo 之和"(当年只测了那一条 200 响应)。本棒流 md5 = 41333ba2d6b480036c694d9fee7e5c1c(⚠️ combo 分区随实例启动世代变 ⇒ 跨重启不可逐字复现,复现须记 rev 世代)。

③ M1 四项读数(阈值 H ≤ 4.0 bit/byte;子窗口 4 KiB / stride 4 KiB)

项 读数
M1-a 种类数 / 重复率 总块 23 |唯一 id 23 |重复率 0.00% |完全重复块 0 个
M1-b 低熵块数 / 体积 0 块 / 0 B |逐块 H ∈ [5.1807, 5.8444] |H 直方图 = {5.0-5.5: 8, 5.5-6.0: 15}
M1-c 占首屏包比例 0.000000%
M1-d 子窗口熵(反向腿) 窗口 5,768 个 |低熵段 15 段,合计 241,664 B = 1.0225% |段字节 min 4,096 / max 81,920 |直方图 {≤4KiB: 10, 4KiB-16KiB: 3, 16KiB-64KiB: 0, 64KiB-256KiB: 2, 256KiB-1MiB: 0, >1MiB: 0} |最大两段 = 81,920 B ×2
(S2)低熵内容 60 份中 0 份含低熵块 |逐份 Hmin ∈ [4.8354, 6.3485] |单块内容 56/60
S1 逐块 H 5.2089 5.5612 5.7684 5.5095 5.6777 5.4695 5.6530 5.6974 5.2515 5.5237 5.5102 5.1807 5.2527 5.7228 5.8444 5.6450 5.5956 5.6805 5.6275 5.2722 5.3887 5.5581 5.3555

④ 三条判据性结论(每条引上表数字)

  1. ✅ "首屏包内不存在纯低熵块" —— 证实(04-133 §1.1 推论 1):23 块全部 H ≥ 5.1807 ⇒ M1-b / M1-c 皆 0。
  2. ❌ "< 1 MiB 的独立小内容若是低熵 ⇒ 纯低熵块" —— 未证实(04-133 §1.1 推论 2):S2 里确实有 56 份单块小内容,但它们全是高熵 JS bundle(Hmin ≥ 4.8354)、低熵内容 0 份 ⇒ 该风险面在当前真实内容上不存在。
  3. ⚠️ "什么算低熵"的阈值无法由本次读数定死(对应 04-133 §10-3 在册项):全部块落在 5.0–6.0 ⇒ 阈值取 ≤ 5.0 时"低熵块"恒 0;字节分布熵对 JS 文本不是强判别器(参照系:均匀随机 ≈ 8.0、全零 = 0)⇒ 🔑 M1-d 才是有效的腿:它抓到 15 段 / 1.0225% 的块内低熵片段(max 81,920 B ≪ 1 MiB)⇒ 印证"低熵以块内小片段形态存在、且已被同块高熵内容稀释"。

⑤ 对 D(非确定性) 的作用面判定:🔴 D 的目标在当前真实内容上已由「定长 1 MiB 切分」结构性达成 —— 低熵物质只以 ≤ 81,920 B 的块内片段存在(占 1.0225%)⇒ 对块级去重 / 枚举不可见;且不存在低熵内容单元(④-2)。⇒ D-1 无对象可稀释、D-2 的启用前提不成立(04-133 §4.1/§4.2 的决策点 4 两分支都不命中)⇒ 本棒判:D ⛔ 本轮不实现(实现 = 纯增复杂度 + 测量上零收益 ⇒ 触 R11「拿不出正向做法 ⇒ 立即停止」)。⚠️ 这不是降级 / 不是静默兜底:目标已由实测证实达成。🔑 D 的回头条件(具名)= 出现「低熵内容单元」 ⇒ 由 ⑥ 的固化探针可重复复查(再测一次即可判定,⛔ 不需要先写代码)。

⑥ 产物(只读探针 · 零第三方依赖):scripts/overlay-entropy.cjs —— ⛔ 不 ssh / ⛔ 不碰网络 / ⛔ 不写生产路径,只读本地流文件;切分调用仓库那份 chunkify(lib/net/relay/content/chunker.js,即 src/net/relay/content/chunker.ts 的编译产物;可用 OVERLAY_CHUNKER 覆盖)⇒ ⛔ 无算法复刻(无双源)。复现:node scripts/overlay-entropy.cjs --in <S1流> [--parts parts.json] [--json] [--window 4096] [--stride 4096] [--threshold 4.0]。 🔴 取数侧的两条实测事实(⛔ 不是阈值,故不进 §3/§6):(a) /plugins/??… 有鉴权(无会话 ⇒ 401 / 24 B)⇒ 必须临时会话;(b) 平台控制面的权威库 = PG(DSHS_DB_URL=postgres://[email protected]:15432/dshs,见 dshs.service.d/cluster.conf)⇒ 仓里的 mksess.cjs 写的是 SQLite /var/lib/dshs/dshs.db ⇒ 已失效(插进去的会话 Manager 查不到 ⇒ 一插就 401)。🔴 这是一条待收口的缺陷(⛔ 不属本单产物,另立小项)。


§11.13 补记(2026-09-18 22:3x,序㊻ 执行棒 · C(块 id 域分离)落地 + OBS-29 判据重裁;同在 §10 指纹口径之外)

落点段 = §6 判据表(新增 OBS-29 一行)+ §10 指纹(重算并回填)。🔴 ⛔ 未新增任何阈值键 / ⛔ 未改任何既有值 / ⛔ 未改任何超时 / ⛔ 未动 DEFAULT_BLOCK_SIZE。

① 新增 OBS-29(判据面) —— 位置 = §6 判据表 OBS-28 之后(编号承接现表最大值 ⇒ ⛔ 未跳号、⛔ 未复用)。五条腿全部是布尔 ⇒ ⛔ 不需要任何新阈值键(否则就是"凑数键"):

腿 内容 类别
P1 同字节 + 不同 network ⇒ 块 id 必须不同,且两侧都不等于裸 sha256 正腿
P2 同 network + 同字节 ⇒ 块 id 必须相同(去重不得丢) 正腿
P3 装配面(store 写/读复算 + 链取回 + 重组位)无漏改 正腿
N1 具名负腿 flat-key-collapses:netKey 取同值 ⇒ P1 必红 负腿
N2 具名负腿 empty-key-falls-back-to-bare-hash:netKey 取空 ⇒ P1 必红 负腿

② 判据重裁(本棒存在的主因) —— 序㊺ 实测判 D ⛔ 本轮不实现(§11.12-⑤)⇒ 原 OBS-29 那条"稀释源换成确定性派生量 ⇒ 必红"的负腿已失去对象 ⇒ 重裁为 C 的判据(= 上表五腿)。🔴 ⛔ 不许只给正腿 —— 本线慢性病 = "装了没生效" = 静默通过 ⇒ 两条负腿都真跑过并具名(见 ④ 先红后绿)。

③ E1 基线重取 —— E1 定义不重估(仍是「回源字节 ≈ 1 份 × 组数」),只重取基线读数(C 改了 id 口径 ⇒ 旧读数作废)。新读数:同网 4 轮 × 4 块 ⇒ 全部 local 命中、source.origin = 0、store.puts = dedupIds.length、putRejected = 0;跨网起点 local = 0 ⇒ 各 1 份。

④ 演练临时副本(⛔ 非权威,用完即删) —— 本棒为跑通 overlay-failover-drill.cjs 生成过 _tmp_seq46/参数表-演练临时.md,唯一差异 = SSH_TIMEOUT_MS 20000 → 180000。原因(实测):currentChannelHint() 的 journalctl -u dshs --since -6h + grep -F '[overlay-dir] 取址'(每 2 s 一条 ⇒ 6 h ≈ 1 万行 / ≈ 1.3 MB)单次 ssh 需 ~47 s > 20 s ⇒ spawnSync ssh ETIMEDOUT。⚠️ 该函数源码注释自述「仅供人读,不参与判定」 ⇒ 属已知健壮性缺陷(另立小项);⛔ 本棒未改演练脚本。权威参数表(本文件)除 ①② 外一个字节未动。

§11.14 补记(2026-09-19 00:1x,序㊼ 部署棒 · C 落地两机 + OBS-29 真机腿 PASS · ⚠️ OBS-09 退化 SKIP;同在 §10 指纹口径之外)

落点段 = §6 OBS-29 行的「⏳ 真机腿」结清(⛔ 未改该行任何字—— 结清体现在本补记)。🔴 ⛔ 零个新增 / 改动生产 env · ⛔ 未改任何阈值 / 超时 · ⛔ 值格一律未动 · ⛔ 未动 DEFAULT_BLOCK_SIZE。

① 部署(全量口径 · 本棒实测认定) —— 两机 4 处 lib 实为历次增量 scp 的叠加(47 /opt/dshs/lib/net/relay/index.js mtime 12:55:27,而同目录 content/chunker.js 06:52:16)⇒ 与本机 build 产物同名 md5 不同 38–41 个。本棒据此按全量替换(270 文件 × 4 处);包 = dshs-lib-seq47.tgz(681,966 B,md5 546b30719ca1bfca1e04189e1099424e);🔴 先传包 → 两机各自验包 md5 → 两机同一窗口内解包 + 重启。核对 = 不一致 0(与代码仓 lib 同名 md5 不同 0 / 仅本机有 0;270 × 4 = 1080 对全额逐字节一致;各位置多出的 11–12 个 = 历史 .bak-* 遗留,⛔ 未删)。回滚点 = 两机 /opt/dsh/backups/seq47-20260918-2347/(4 处;47 = 262 / 263 文件,106 = 260 / 262;共 6.2M / 6.1M)。

② OBS-29 真机腿(本棒主判据 · ✅ PASS) —— 47 / 106 /status.content = blockIdKeyed **true**、blockIdKeyId **d6e62322e5166938**(两机逐字相同)、blockSize **1048576**(⛔ 未动)、storeMaxBytes **67108864**(⛔ 未动)。第二来源 = 两机 relay 启动判别器日志原文(47 23:47:43 / 106 23:47:37):[content] 块 id 口径 = HMAC-SHA256(域密钥) blockIdKeyId=d6e62322e5166938。⇒ §6 该行「⏳ 真机腿(须部署后补)」已结清。

③ 🔴 OBS-09 由 PASS 退化 SKIP(既有缺陷被本棒硬要求的「重启 106 dshs-worker」触发 · ⛔ 非本单引入) —— 原文 = SKIP OBS-09 在册实例面 无(从 /status.endpoints[] 派生为空:端点表 1 条 = agent 1 条 + 离线 0 条)。事实链:106 实例 286172(dsh --profile web --port 21001)启动于 10:18:34(= 重启前 13.5 h)、未被 teardown(OBS-22 scanned=1 adopted=1 stopped=0 + 日志 [rehydrate] probe OK dsh-100002-7d1c8cbf.scope :21001)⇒ 但 47 端点表只有 w-106:19000、缺 w-106:21001(本表 §3.9 PEER_INSTANCE_PORT 行 10:2x 实测应有两条)。根因 = relay-tunnel.ts#cancel() 只撤销当前进程 forwarded 集里的端口 ⇒ 上一进程遗留条目无人撤销;恢复路径须"实例重新拉起"(端口登记发生在 spawn 流程内)⇒ 与 memory 明令「⛔ 别为迁就旧值重启生产实例」冲突 ⇒ 本棒不修、不凑绿(另立小项)。⏳ 本行 / §6 OBS-09 判据口径本棒⛔ 未改。

④ 零回归 —— 探针 28 PASS / 0 FAIL / 1 SKIP(rc=0;基线 29P/0S/0F ⇒ 差 1 = 上述 OBS-09)|--scene all 12 PASS / 0 SKIP / 0 FAIL(rc=0;6m28s)✅ 逐字同基线|npm test 本轮 ⛔ 未跑(本棒零代码改动,未触单测面)。

⑤ 指纹 —— §10 现算 ac6bbbbb8c92bd57ff0dc4cd8f4983ba(与序㊻ 记录值同值 ⇒ 本补记在 §10 口径之外);🔴 零个新增 / 改动生产 env · ⛔ 值格一律未动(本棒只做部署与读数)。

§11.15 补记(2026-09-19 01:0x,序㊽ 执行棒 · 修「重启 worker 后实例端口注册丢失 / 孤儿端点条目不自愈」⇒ OBS-09 由 SKIP 回到 PASS;同在 §10 指纹口径之外)

① 缺陷与落点(文件 + 行,⛔ 不是 spawn 流程) —— reconcileTunnel()(对账自愈)早就存在(src/worker/agent.ts:256-264),链路是通的,只是看不到那条端口:对账口径 live 只取 spawner.listUserInstances(),而它的实现是 return [...this.mains.values()](src/supervisor/orchestrator.ts:558-560)=本进程 launch 过的;认领来的既有实例刻意不进 mains(src/supervisor/orchestrator.ts:290 原文「已被「认领」的既有 scope —— ⛔ 刻意不进 mains」,理由见文件头 序㉕:launchToken 不可恢复)⇒ 上一进程遗留的实例端口没有任何人会重新声明。relay 侧则本来就等着:dropSession() 只摘会话、⛔ 不删条目(src/net/relay/server.ts:2151-2203),而 onPortChange(add=true) 命中既有条目时走 ensureEndpoint() ⇒ 复用条目、只换绑定会话(localPort 沿用)⇒ 重登记 = 就地覆盖孤儿。🔴 ⇒ §11.14-③ 那句「恢复路径须"实例重新拉起"」已被本棒实测证伪:存在零中断路径。

② 修复(2 个源文件 · ⛔ 零新依赖 / 零新监听口 / 零新 env) —— orchestrator.ts 新增 adoptedInstancePorts()(只报 alive === true 且带端口的认领记录)+ onRehydrateSettled 回调与 pendingProbes / rehydrateScheduled 落定计数;agent.ts 把对账口径改为 listUserInstances() ∪ adoptedInstancePorts(),并抽出 tunnelTick() 供 20 s 定时器与「认领落定」回调共用一份语义。🔴 ⛔ 未绕过既有准入 / 白名单:新端口走的仍是 tunnel.forward() → RelayClient.addPort() → PORT_ADD → relay.onPortChange(含既有 [base, base+span) 窗口校验 + worker 侧 allow 白名单)。

③ 读数(探针 · 真机) —— 改前 26 PASS / 2 FAIL / 1 SKIP(rc=1) ⇒ 改后(终态)29 PASS / 0 FAIL / 0 SKIP(rc=0):OBS-01 FAIL used=1 → PASS used=2;OBS-08 FAIL 端点表 1 条 / 离线 1 条 → PASS 端点表 2 条 / 离线 0 条;OBS-09 SKIP 派生为空(agent 0 条 + 离线 1 条) → PASS 在册实例面(派生 1 条):w-106:42497=401。⚠️ 如实纠正:§11.14-③ 与本棒 prompt 记的「28P/0F/1S」与本棒开工实测不同 —— 实测 26P/2F/1S,因 106 worker 于 00:04:30 按抖动路径切到 106 自家中继(原文 [relay-switch] #3 …->wss://106.54.21.172/dshs-relay(原因:当前通道抖动量超标(p95|ΔRTT|=2941ms ≥ 阈值 20ms)…))⇒ 47 视角连 w-106 会话也一并失去。

④ 端点表原文 —— 47 视角:改前 [('w-106',19000,42313,**False**)](孤儿)⇒ 改后 [('w-106',19000,45095,**True**), ('w-106',21001,36535,**True**)]、sessions=[('w-106',**[19000,21001]**),('manager')]、used=2。🔴 先红最关键的一条:改前 106 自己的中继表里也只有 19000,而 106 上 ss -lntpH 明明白白 127.0.0.1:21001 在听 ⇒「实例活着、却没人替它把端口声明出去」,与"挂在哪台中继"无关。机理原文(47 relay journal):AUTH OK … ports=[19000] ⇒ 1 s 后 host ops/w-106 +port 21001 -> 127.0.0.1:36535(⚠️ 20 s 定时器首拍在 00:31:31 ⇒ 这一枪只能来自新装的"认领落定即登记";且旧口径下 20 s 拍同样看不到 21001)。

⑤ 孤儿自愈对照(受控 · ⛔ 未停实例) —— [A] 停 worker 前 used=2 eps=[(19000,45095,True),(21001,36535,True)]|[B] stop dshs-worker(孤儿态)used=1 eps=[(19000,45095,**False**),(21001,36535,**False**)] ← 条目仍留在表里(= 改前 OBS-08 红形态),同时 106 ss -lntH 'sport = :21001' = 1 行(实例全程未动)|[C] start 后 used=2 eps=[(19000,45095,**True**),(21001,36535,**True**)]。🔬 最强一条:localPort 在 [B]→[C] 逐字沿用(45095/36535 未变)⇒ 证明是「复用既有条目、只换绑定会话」,⛔ 不是新开监听口。

⑥ 部署 / 回滚点 —— 包 dshs-lib-seq48.tgz(685,368 B / 270 文件,md5 a902c936cefdeeb3eb13c67dfcb5dd00;两机解包前各自验包 md5 均一致)。全量核对 = 不一致 0(270 × 4 = 1080 对;缺失 0;各位置多出 11–12 个 = 历史 .bak-*)。回滚点 = 两机 /opt/dsh/backups/seq48-20260919-0030/(47 = 282 / 281 文件;106 = 282 / 281)。⚠️ 如实留档:首轮"两机同窗口"脚本把 cd 写进后台复合命令内 ⇒ 106 未执行、47 已落地 ⇒ 实际窗口相差 ≈ 28 s(47 00:30:47 / 106 00:31:15);本棒改动为纯 worker / supervisor 侧增量(⛔ 无协议帧 / 无接口形状 / 无 env 变化)⇒ 无跨版本不兼容。⛔ 本棒未为验证重启任何实例;唯一服务中断 = 受控对照中 dshs-worker 停启一次(停用 ≈ 5 s)。

⑦ 未解决项(⛔ 如实 · 交下一棒) —— (a) 🔴 观测点单一:OBS-01 / OBS-08 / OBS-09 的绿取决于 106 worker 挂在哪台中继(探针只读 47 的 RELAY_STATUS_URL),而 worker 通道会按抖动切换 ⇒ worker 一旦落到 106 自家中继,三项即再红 / SKIP。⛔ 本棒 未动选路策略(属既有设计)⇒ 登记为未解决项。(b) ⚠️ 死口孤儿未回收:替换后旧端口条目没人 PORT_DEL ⇒ 永久 online=false 留表(RelayClient.removePort() 对"本进程从未加过的端口"早退);⛔ 修它需 worker 能读到中继侧条目表,而二者可能不同机 ⇒ 非本棒可及。

⑧ 指纹与零变更 —— §10 现算 ac6bbbbb8c92bd57ff0dc4cd8f4983ba(与 §11.14 同值 ⇒ 本补记在 §10 口径之外);🔴 ⛔ 值格一律未动(含 PEER_INSTANCE_PORT 仍 21001 —— 那是"某次现测"的快照,且 OBS-09 已不从该键取值)⇒ 零个新增 / 改动生产 env · ⛔ 未改 nft / nginx / bwrap · ⛔ 未调 RELAY_FAILOVER_DEADLINE_MS / HB_SEC / burst · ⛔ 未禁用 COOLDOWN_MS=0。

§11.16 补记(2026-09-19 01:0x–01:4x,序㊾ 执行棒 · OBS-01 / OBS-08 / OBS-09 的数据源改为「两台中继并集」⇒ 消除「worker 归属漂移」造成的假红 / 假 SKIP;同在 §10 指纹口径之外)

① 本表变更 = 0(键 / 值 / 阈值全未动) —— 本棒 ⛔ 未新增任何键、⛔ 未改任何值、⛔ 未改任何阈值;唯一改动在脚本侧(scripts/overlay-probe.cjs,单文件六处,见 §11.15-⑦(a) 那条遗留)。⇒ 上表 §6 里 OBS-01 / OBS-08 / OBS-09 三行的阈值与判据形态逐字不变,只换了数据源(判据行末的口径说明另行补注于交接单 §16-2)。

② 口径(并集 · 唯一入口 readPeerView()) —— 两台中继各自只看得见挂在自己身上的客户端 ⇒ 单看 47 会让三条判据的绿取决于 106 worker 挂在哪台中继(worker 按抖动换址,既有设计)⇒ 漂移态下 47 视角同时失去"会话"与"实例面" ⇒ OBS-01 FAIL(used=1 < 2)/OBS-08 FAIL(离线孤儿)/OBS-09 SKIP(派生为空)= 假红 / 假 SKIP。口径 = 按 hostId(含 network)合并两台中继视图:eps 键 = network:hostId:port、online 取或、localPort 取在线那一侧;used = network/hostId 去重后的并集基数(⛔ 不是求和 —— 同一节点两台都残留时会重复计数 = 另一方向的假绿)。🔴 实例探活必须回到"那一台"上做:localPort 是中继机回环落点 ⇒ 归属 106 的端点得 ssh 到 106 探(47 上那个口根本不存在)。🔴 只并这三项:OBS-11 的 derived / OBS-02 / OBS-13·OBS-16(47 的 counters)一律保持 47 视角(⛔ 并集只消除"看不见",⛔ 不放宽判据)。🔴 OBS-16 的 Δ 不受影响:对端那份是另算的一次独立采样(读 106 的 counters)且位置在第三次采样之后(门窗口 (status2, status3] 内只有 sleepSync)⇒ 对 47 /status 的读取次数一次都没变;实证 = 47 statusHits 5→6 (Δ=1) 而中间插了 2 次读 106、106 自身 2→3。

③ 读数(探针 · 真机) —— 漂移态(47 视角 used=1 / endpoints=[]、106 视角两条 online=true):PASS OBS-01 used=2(并集: 47=1 + 对端=1 去重后计)|PASS OBS-08 端点表(并集) 2 条 / 离线 0 条(47 视角 0 条 + 对端 2 条)|PASS OBS-09 在册实例面(并集派生 1 条):w-106:40701=401 —— 🔬 探的是 106 机上的回环落点(47 上不存在该口)⇒ "按 from 分机探活"这条腿真走到了 106。终态(收口后) = 29 PASS / 0 FAIL / 0 SKIP(rc=0)(OBS-09 落点 w-106:35713=401)。夹具先红后绿 = 同一份夹具(真实 /status 原文 + 真实 ss/nft + 参数表副本)只差是否给对端那一半:红 rc=1(OBS-08 FAIL 2 条/离线 2|OBS-09 SKIP)→ 绿 rc=0(OBS-08 PASS 2 条/离线 0|OBS-09 PASS w-106:36873=401),两侧逐行 diff 只差这 2 行(OBS-01 在夹具模式按既有语义记"未取证" ⇒ 其红腿由真机退化腿承载:对端不可达 ⇒ FAIL OBS-01 used=1 =改前 47 单点判法)。

④ 部署(⛔ 零新增监听口 / ⛔ 零服务读取) —— 47 /opt/dshs/scripts/overlay-probe.cjs:旧 md5 eaec1ad560adbec37a15cd145a5ebb77(109266 B · Sep 18 12:49 = 序㊳ 期,陈旧)⇒ 新 d2f879dc3220f2800d812437518e3c63(135215 B · 0755);106 /opt/dshs-cluster/scripts/overlay-probe.cjs:此前不存在 ⇒ 新落同 md5(root:root 0755)。🔴 取两机同源的理由:一份新一份旧/无,日后在那台机上跑一次就得到旧口径读数(本线反复踩的"同一事实两处打架")。⚠️ 该文件不被任何单元读取(探针是从本机 ssh 出去的工具)⇒ ⛔ 零服务影响。

⑤ 零变更 / 指纹 —— §10 现算 ac6bbbbb8c92bd57ff0dc4cd8f4983ba(与 §11.14 / §11.15 同值 ⇒ 本补记在 §10 口径之外);⛔ 值格一律未动 · ⛔ 零个新增 / 改动生产 env · ⛔ 未改 nft / nginx / bwrap · ⛔ 未调 RELAY_FAILOVER_DEADLINE_MS / HB_SEC / burst · ⛔ 未禁用 COOLDOWN_MS=0(grep 计数 0)。⚠️ 未解决项不变:(a) D8 云安全组(乙-1)待你在云控制台落地(两机+本机均无云凭据);(b) 死口孤儿仍未回收(并集只消除"看不见",online=false 条目仍留表)。