932 lines
189 KiB
Markdown
932 lines
189 KiB
Markdown
# 参数表 · 覆盖网络(**唯一一张**)
|
||
|
||
> **线**:覆盖网络线 | **序**:⑤(参数表 · 观测 · 权限评估)| **产出**:执行棒 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(|ΔRTT|) ≥ 它` ⇒ 允许换到更稳候选)。🔴 **⛔ 不新造 `JITTER_SWITCH_MS` 同值键** —— "超标"与"该换路"是**同一件事**,两个键 = 同一事实两处写(本线知识碎片化教训)。⚠️ 值 `20` 仍是**业界估值**(非实测),实测定值属待测项 |
|
||
| `JITTER_LINK_MEASURED` | **3**(判定值 = `p95(|ΔRTT|)`;另记 `mdev` = 1.37–1.38 ms、`avg` = 148.3 ms、丢包 1.3–1.7%) | ms | **实测** | 本单 §8.4 E5 + `overlay-jitter.cjs --icmp`(47→106 与 106→47 各 300 包 × 0.2 s) | **达标**:3 ms < `JITTER_LIMIT_MS`(20 ms,⚠️ 该限值是**业界估值**口径)。⚠️ 这条**推翻了**"当前跨云链路上 jitter 不可能达标"的旧定性结论 —— 旧结论是拿 `RELAY_RTT_W106` 当链路 RTT 推的(口径错,见下行) |
|
||
| `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** | `node --test test/orchestrator-teardown.test.mjs` ⇒ `T5`(逐文件点名)+ `T9`(判别器自证) |
|
||
| `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(|ΔRTT|)` = **3 ms ⇒ 达标**(限值 20 ms 属估值口径) |
|
||
| 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://dshs@127.0.0.1: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` 条目仍留表)。
|
||
|
||
|
||
|
||
|