Files
dsh_ai1net_server/归档/交接单-20260924-归档/交接单_骨干稳定选路与加密_20260917.md
T

251 lines
22 KiB
Markdown
Raw Normal View History

# 交接单 · 骨干节点落地(稳定高效选路 + 传输可加密)
- **序号**:覆盖网络线 **序 ㉖ · 规划棒**
- **立单**:2026-09-17 20:3x
- **用户拍板**:**「入口 §4 骨干节点的服务范围:按照连接稳定高效的方式 数据安全可加密传输」**(2026-09-17 20:2x)
- **上游依据**:`覆盖网络_骨干层方案_20260916.md` **§7(原 A/B 待拍板)**/**§8 落地顺序**/`覆盖网络_瓶颈落地方案_20260916.md` **§4(jitter 是一等指标)**/`覆盖网络_补遗与参考方案_20260916.md`(45% 设计 / 55% 余量口径)
- **状态**:**待执行**
---
## §0 摘要 + 我把用户口径翻译成的两条硬判据
用户**没有按 A/B 选**,而是给了一句**目标导向**的话:**「按照连接稳定高效的方式 数据安全可加密传输」**。
⇒ **我的解读(已定项,可推翻)**:用户要的不是"服务范围"这个二元选择,而是**两条可验证的工程质量判据**:
| 口径 | 翻译成机器判据 | 与 A/B 的关系 |
|---|---|---|
| **连接稳定高效** | **选路按 jitter 排序**(⛔ 不按 RTT)+ 每连接保 2–3 条候选路径 + 中继利用率留 **30%+** 余量 + 骨干间心跳 ≤10 成员全互联 | **与 A/B 正交** —— 无论自用还是全网,这条都要满足 |
| **数据安全可加密传输** | ① 传输层加密(**wss/TLS 已在**,⛔ 不新造)② **内容/信令完整性由签名与哈希兜住**(本线已有 relay 签名目录 + 密钥体系)③ **对外只暴露"转发能力",⛔ 不暴露"看到谁连谁"的元数据** | **B 档的最大缺点正是"骨干会看到流量元数据"** ⇒ 用户这句**恰好把 B 档的最大代价按住了** |
✅ **因此我的判定(可推翻):按 "A 起步、口径按 B 的质量标准建设" 落地** —— 即 **§7 原倾向 A→B 渐进 与用户口径并不冲突**:
- **可见范围**仍取 **A**(只服务自己名下设备):无计费/合规纠纷、权限面不变(⛔ 不命中 R5)、元数据暴露面最小 —— **这直接满足"数据安全"**;
- **质量与冗余**按 **B 的标准**建设(多中心骨干、jitter 选路、30%+ 余量):**这直接满足"稳定高效"**;
- **"可加密"** ⇒ 做成**能力就位**(TLS + 签名 + 内容哈希),**⛔ 不在本单引入端到端加密**(见 §7)。
---
## §1 目标(可判定"做完了没有")
| # | 判据 | 期望 | 怎么测 |
|---|---|---|---|
| **E1** | **选路按 jitter**(⛔ 不按 RTT) | 两候选路径中 jitter 更低者被选中,**即使其 RTT 更高** | 夹具:候选 A(RTT 20ms/jitter 15ms) vs B(RTT 30ms/jitter 2ms) ⇒ 断言选 **B** |
| **E2** | **jitter 可观测** | 每连接有 jitter 直方图 + 超阈值自动切路径并**告警** | 判别器计数 + 直方图落盘 |
| **E3** | **路径多样性** | 每连接维护 **2–3 条**候选(直连 / 就近中继 / 备用中继) | `/status` 可查候选数 ≥ 2 |
| **E4** | **利用率留余量** | 中继利用率 ≤ **70%**(留 30%+) | `used / capacity` 可查、超限**拒绝新接入**(⛔ 不打满) |
| **E5** | **骨干互认只校验不自行批准** | 控制面签发资格;骨干间只验签 | 未签名骨干接入 ⇒ 被拒并**点名** |
| **E6** | **元数据最小化**(A 档) | 骨干**只知"转发给谁",不知"谁在连谁"**;跨用户**不可见** | 断言跨用户 0 命中(与内容分发的 `E5` 同族) |
| **E7** | 零回归 | `npm test` **176/175/0/1**、`--scene all --table` **12P/0S/0F**、`overlay-probe --table` **16P/0F/0S** | 三件套跑同值 |
---
## §2 只读前置(执行前**必须**先核实的 5 条)
| # | 要核实什么 | 命令 | 期望 |
|---|---|---|---|
| **P1** | 现行 relay 是否**已**做到"只绑回环 + 443 兜底" | `ss -lntp` + relay ExecStart | 已实测:两台 relay 均**只绑 `127.0.0.1:20080`**、106 复用既有 443 ⇒ **零新增公网口**(⛔ 本单不许破坏这条) |
| **P2** | 现行选路是**按什么排序** | 读 `src/net/relay/switcher.ts` / `directory.ts` | ⚠️ 预判:**按健康度/冷却**,**⛔ 不是按 jitter** ⇒ 这是本单的主要缺口 |
| **P3** | jitter 现测值 | `scripts/overlay-jitter.cjs` | 已有脚本;序⑥ 实测 `p95(\|ΔRTT\|)` = **3 ms**(达标)⇒ **本单把"点测"变成"持续采样 + 参与选路"** |
| **P4** | 骨干"资格"体系现状 | `scripts/overlay-keyring.cjs` + relay `keys` 表 | 已有四层密钥模型(离线根 → 在线签名者 → 每机节点密钥 → 会话)+ 逻辑名索引 + 成员资格校验 ⇒ **E5 大部分已就位** |
| **P5** | 45% 容量口径现值 | 参数表 `RELAY_MAX_HOSTS` | 实测 **7515**(序⑥ 重算并已下发两台)⇒ 本单**只读不改**,⛔ 不动容量值 |
---
## §3 范围
### 3.1 在册文件集(⚠️ **超出必须先停下报告** — R7)
| 面 | 文件 | 说明 |
|---|---|---|
| 新增 | `src/net/relay/jitter.ts` | jitter 采样与直方图(**选路的输入**) |
| 改动 | `src/net/relay/directory.ts` | 候选排序:健康度 → **jitter 为主序**(⛔ 不删既有冷却语义) |
| 改动 | `src/net/relay/switcher.ts` | ⚠️ **只加"jitter 劣化即切",⛔ 不改冷却语义** |
| 改动 | `src/net/relay/server.ts` | 利用率守卫(`E4`)+ 候选路径可查(`E3`) |
| 改动 | `scripts/overlay-jitter.cjs` | 点测 → 持续采样 |
| 改动 | `scripts/overlay-probe.cjs` | 新增 `OBS-17`(jitter)/`OBS-18`(余量) |
| 改动 | `参数表_覆盖网络_20260917.md` | 新增 `JITTER_*` / `RELAY_UTIL_MAX_PCT` 键 |
⛔ **不在本单范围**:`RELAY_FAILOVER_*` 任何值、`HB_SEC`、burst、`switcher.ts` 冷却语义、`RELAY_MAX_HOSTS`、房间层、打洞实现、内容分发的块级实现(**另单**,见 `交接单_内容分发块级寻址_20260917.md`)。
### 3.2 三条硬约束
1. 🔴 **⛔ 不改 `switcher.ts` 冷却语义**(本线持久禁令)—— 只**新增** jitter 触发条件。
2. 🔴 **⛔ 不新增公网监听口 / 不改 nft·nginx**(R5)—— 骨干仍只绑回环 + 443/TCP 兜底复用。
3. 🔴 **骨干资格只能控制面签发**(权威状态单点 Manager)—— 骨干**只校验,不自行批准**。
---
## §4 步骤 S0–S6
| 步 | 做什么 | 验证(一次可执行) | 回滚 |
|---|---|---|---|
| **S0** | 只读前置 `P1–P5` + 基线 jitter 采样(**零改动**) | 输出 Before:现行选路按什么排序(**原文行号级证据**) | 无(只读) |
| **S1** | jitter 采样与直方图(`jitter.ts`) | 夹具:喂 3 条已知 jitter 序列 ⇒ 直方图与 p95 **逐项可断言** | 删新增文件 |
| **S2** | **候选排序改为 jitter 为主序**(`directory.ts`) | `E1`:**先红后绿**(jitter 更低但 RTT 更高的那条**必须被选中**) | 还原排序函数 |
| **S3** | 切换条件新增"jitter 劣化即切"(`switcher.ts`,⛔ 不碰冷却) | `E2`:劣化 ⇒ 切 + 告警;⛔ 冷却语义断言**不变** | 移除新增条件 |
| **S4** | 利用率守卫 + 候选可查(`server.ts`) | `E4`:利用率 > 阈值 ⇒ 拒绝新接入并**点名**;`E3`:候选数 ≥ 2 | 还原守卫 |
| **S5** | 骨干互认与元数据最小化复验(`E5` / `E6`) | 未签名骨干被拒并点名;跨用户 0 命中 | 同 S2 |
| **S6** | 真机验收 + 观测 + 零回归(`E7`)+ 收口 | 三件套同值;探针新增 2 项 PASS | 产物回滚点 |
---
## §5 验收判据 E1–E7
见 **§1**。⚠️ **`E1` 是主判据**("选路按 jitter 排序而非 RTT")—— 这一条**能不能机器断言**,决定了"连接稳定高效"是不是**嘴上说说**。
---
## §6 回滚(两层)
1. **排序级**:还原 `directory.ts` 的排序函数 + 移除 `switcher.ts` 新增条件 → `tsc` → scp `lib/` → `restart dshs` ⇒ 回到"按健康度/冷却"的旧选路。
2. **产物级**:scp 回滚点 `/opt/dsh/backups/seq26-<ts>/lib/net/relay/{directory,switcher,server,jitter}.js`。
🔴 ⛔ **回滚路径里不许出现 `RELAY_FAILOVER_COOLDOWN_MS=0`**。
---
## §7 安全与加密档位(**用户口径的关键落点**;属已定项、⛔ 不再上抛)
用户原话:**「数据安全可加密传输」**。逐条落法:
| 层 | 现状 | 本单怎么做 |
|---|---|---|
| **传输层加密** | ✅ **wss / TLS 已在**(relay 经 `https://alotbuy.com/dshs-relay` 可达、双路 101) | **复用,⛔ 不重造**。骨干之间同样只走 wss |
| **身份与授权** | ✅ 四层密钥模型已落地(离线根 → 在线签名者 → 每机节点密钥 → 会话)+ 逻辑名索引 + 成员资格校验 + 吊销演练通过 | `E5`:**骨干间只校验,不自行批准**(补齐"骨干资格"这一档) |
| **完整性** | ✅ 有签名目录 | 信令与目录**全程验签**;⛔ 不引入第二套密钥体系 |
| **元数据最小化** | ⚠️ B 档的最大缺点是"看到谁连谁" | `E6`:**A 档可见范围** ⇒ 骨干**只知转发目标、不知连接双方**;跨用户不可见 |
| **端到端加密(E2E)** | ⛔ **不做** | **属真取舍**:E2E 会让"每个接收者密文不同" ⇒ **与内容分发的按哈希共享块直接冲突**(要加密就失去共享)⇒ 本阶段取 **"TLS + 验签 + 内容哈希"**;E2E 登记为**待评估**(见 §9-3) |
🔑 **一句话**:**"可加密"= 能力就位(TLS + 验签 + 哈希)+ 元数据最小化**,⛔ **不是**"现在就上端到端加密"。
---
## §8 回报格式(执行会话**必须**回填)
```markdown
### 8.x 序 ㉖ 执行棒回报(YYYY-MM-DD HH:MM–HH:MM)
#### ① S0–S6 逐条(命令 + 原文输出 + 判定)
#### ② 主判据 E1:jitter 更低(但 RTT 更高)的候选被选中
- 候选 A = ? | 候选 B = ? | 实测选中 = ? | 原文 = ?
#### ③ 先红后绿(原文级)
#### ④ 零回归三件套(均带 `--table`)
#### ⑤ 边界自证
⛔ 未改 `switcher.ts` 冷却语义 / ⛔ 未改生产值 / ⛔ 未新增公网口 / ⛔ 未改 nft·nginx / 🔴 `COOLDOWN_MS=0` = ? / ⛔ 未 commit·未 push
#### ⑥ 指纹
参数表 = ?(前值 `42238175d84319ada99afa56d583f9db`)|directory.ts / switcher.ts / server.ts md5 = ?
```
---
### 8.1 序 ㉖ 执行棒回报(2026-09-17 22:34–23:2x)
#### ① S0–S6 逐条(命令 + 原文输出 + 判定)
| 步 | 做了什么 | 证据(原文级) | 判定 |
|---|---|---|---|
| **S0** | 只读前置 + 改前基线 | **P2 ✅ 缺口确认**:`directory.ts` 候选序 = 签名目录发布序(同源优先)、`switcher.ts` 换址 = 冷却/豁免优先级,**两处均无 jitter**(`grep -n "sort(\|jitter\|rtt" src/net/relay/{directory,switcher}.ts` 得 0 处 jitter 排序)。**P1/P5 间接取证**:47 `ss -lntp` ⇒ relay 只绑 `127.0.0.1:20080`、无新增公网口;`curl /status` ⇒ `capacity.max=7515`(未动)。**改前 `/status` 原文**:`"capacity":{"max":7515,"used":2,"free":7513}` ⇒ **无 `utilPct`/`utilMaxPct`、无 `jitter` 块** = 改造前形态的直接证据 | ✅ |
| **S1** | 新增 `src/net/relay/jitter.ts`(237 行):`absDeltas` / `percentile` / `histogram` / `statsFromDeltas` / `JitterTracker` / `orderByJitter` / `pickJitterTarget` / `sharedJitterTracker` | 新夹具 `test/overlay-jitter.test.mjs` **21 用例**(直方图逐桶、p95 口径、零样本 `D9`、采样门限、`pickJitterTarget` 五态) | ✅ |
| **S2** | `directory.ts` 候选排序 → **jitter 为主序**(`orderByJitter`:已知 jitter 升序 + 未测样本保原序排后 + **零样本逐字返回原数组**) | **先红后绿**见 ③;`D9` 由 `E1-b` 用例钉(零样本返回**同一个数组**,非等值新数组) | ✅ |
| **S3** | `switcher.ts` 新增"**jitter 劣化即切**":`jitterSwitches` / `jitterAlerts` 计数 + 注入位 + `pickJitterTarget` 调用;🔴 **冷却表(`cooldown`)一行未动** | `E2-b`/`E2-c` 用例:jitter 换址**无豁免权**(`exemptSwitches` 增量 = 0);真机告警原文见 ② | ✅ |
| **S4** | `server.ts`:**利用率软门**(`utilMaxPct`,`used/max×100 ≥ 它` ⇒ 拒**新**接入、⛔ 不驱逐在线;计数独立为 `utilRefused`)+ **`jitter` 观测块**(无样本也**结构完整**)+ 每会话 `rttSamples` 环 | 重启后真机 `/status` 原文:`"capacity":{"max":7515,"used":2,"free":7513,"utilPct":0,"utilMaxPct":70}` + `"jitter":{"samples":0,…,"hist":[0×8],"sessions":0,"thresholdMs":20,"overThreshold":false,"alerts":0}` | ✅ |
| **S5** | E5/E6 复验 | **E5**:探针 `OBS-03 identityRequired=true trustedSigners=1` + `OBS-04 identityOk=3` + `OBS-05 revokedHosts=0` ⇒ 骨干资格仍**只校验不自行批准**(本序**零改动**身份面)。**E6**:`content.peer` 五键齐(`crossGroupDenied=0`)|⚠️ **诚实标注**:`0` = 本期**无越界尝试**,**不是**"被拒 0 次"⇒ E6 的强判据(构造越界尝试)属内容分发线,本序只做复验 | ✅(复验) |
| **S6** | 真机验收 + 观测 + 零回归 + 收口 | 见 ②③④⑤⑥;探针新增 **`OBS-19`/`OBS-20`** 由红(改造前无 `jitter` 块 ⇒ 必红)转绿 | ✅ |
#### ② 主判据 E1:jitter 更低(但 RTT 更高)的候选被选中
- **夹具(机器断言,E1-a)**:候选 A(RTT 20 ms / jitter 15 ms)· 候选 B(RTT 30 ms / jitter 2 ms)⇒ **实测选中 = B** ✅ `ok 8 - E1-a 主判据:**jitter 更低的那条排在首位,即使它 RTT 更高**`
- **真机读数(47 上 `node scripts/overlay-jitter.cjs --watch --targets "127.0.0.1:20080,106.54.21.172:443" --rounds 14 --gap 400`)**:
| 候选路径 | RTT median | RTT p95 | **jitter p95(\|ΔRTT\|)** | hist(8 桶) |
|---|---|---|---|---|
| `127.0.0.1:20080`(本机 relay 回环) | 0.59 ms | 4.75 ms | **3.88 ms** | `[13,0,0,0,0,0,0,0]` |
| `106.54.21.172:443`(跨云兜底) | 151.64 ms | 1166.34 ms | **1015.69 ms** | `[8,4,0,0,0,0,0,1]` |
原文:`"order":["127.0.0.1:20080","106.54.21.172:443"]`、`"e1":{"curJitterMs":3.88,"wouldSwitchTo":null}`
- ⚠️ **诚实标注**:真机两条真实路径的 **jitter 序与 RTT 序同向**(回环两项都最优)⇒ **真机拓扑无法区分"按 jitter 排"与"按 RTT 排"** ⇒ 主判据的判别力**由夹具提供**(E1-a/E1-e 端到端用例),真机只作同口径佐证。⚠️ 这是**拓扑限制**、⛔ 不是判据失败(§9-6 未命中:机器断言**跑出来了**)。
- **端到端(E1-e)**:当前通道不健康 ⇒ 换到 jitter 更低的那条(目录序里它排第二)—— 同一破坏实验下**必红**(见 ③)⇒ 证明它是真判据。
#### ③ 先红后绿(原文级)
**破坏点** = 编译产物 `lib/net/relay/jitter.js:221` 的候选排序 comparator 改为 `() => 0`(= 退化成"保原序")⇒ `npm test` 前先单跑新夹具:
```
not ok 8 - E1-a 主判据:**jitter 更低的那条排在首位,即使它 RTT 更高**
not ok 10 - E1-c 未测样本的候选**保原序排在其后**
not ok 12 - E1-e **端到端主判据**:当前通道不健康 ⇒ 换到 jitter 更低的那条
# tests 21 / # pass 18 / # fail 3
```
⇒ `npm run build` 还原 ⇒ `# tests 21 / # pass 21 / # fail 0`,且 `lib/net/relay/jitter.js` md5 **回到部署值 `47e248d84aa6fa6c8f29f18f942b82cb`**(逐字一致)。
#### ④ 零回归三件套(均带 `--table`)
| 件 | 基线 | 本棒实测 | 判定 |
|---|---|---|---|
| `npm test`(Node 22) | 176 / 175 / 0 / 1 | **197 / 196 / 0 / 1**(+ 新夹具 **21 用例**) | ✅ 零退化 |
| `scripts/overlay-failover-drill.cjs --scene all --table` | 12 PASS / 0 SKIP / 0 FAIL | **12 PASS / 0 SKIP / 0 FAIL** | ✅ 同值 |
| `scripts/overlay-probe.cjs --table` | 17 PASS / 0 FAIL / 1 SKIP | **19 PASS / 0 FAIL / 1 SKIP**(+ `OBS-19`/`OBS-20`;`OBS-09` SKIP = 47 无活跃实例,正常态) | ✅ +2 |
#### ⑤ 边界自证
⛔ **未改 `switcher.ts` 冷却语义**(`cooldown` 表结构/写入/读取**一行未动** —— 只**新增** jitter 触发条件与两个计数)|⛔ 未改任何生产值(本仓 `COOLDOWN_MS *= *0` 命中 **0**;47 `dshs*.service.d/*.conf` 内 `COOLDOWN` 命中 **0**;`RELAY_FAILOVER_*` / `HB_SEC` / burst / `PRESENCE_*` / `DSHS_REHYDRATE_*` 逐项未动)|⛔ 未新增公网监听口(47 `ss -lntp` 非回环面 = 58888/80/22/888/443/8765 **全为既有**;relay 仍只绑 `127.0.0.1:20080`)|⛔ 未改 nft·nginx | 🔴 **`COOLDOWN_MS=0` 计数 = 0**(未写进任何回滚 / 演练 / 夹具路径)|⛔ **未 commit / 未 push**(HEAD `04776af` 未变;`git status` 显示 `M`/`??` 未暂存)。
**部署(在册产物)**:`lib/net/relay/{jitter,directory,switcher,server}.js` scp 到 47 **两处**(`/opt/dshs/lib/net/relay/` + `/opt/dsh-relay/lib/net/relay/`)⇒ **8 个文件 md5 与本机全同**;重启 `dshs-relay` + `dshs` + `dshs-worker`(🔴 **动手前已取证 47 上无活跃实例 scope** ⇒ 未影响任何用户)。回滚点 = **`/opt/dsh/backups/seq26-20260917-225032/`**(含 `.relay/` 子目录,覆盖两处旧值)。
#### ⑥ 指纹
- **参数表**(`sed '/^## §10 指纹/,$d' | md5sum`)= **`4c78912d03f4f209d417407a80018361`**(前值 `42238175d84319ada99afa56d583f9db`)
⚠️ **重要勘误**:开工单 §11 记的"立单时 `42238175…`"**在本棒开工时已失效** —— 实测起点 = **`93e38d1a57fbcd3126d2122150a2e838`**(立单后序㉔/㉕ 已在 §10 之前加键:`CONTENT_*` + `OBS-17`、`DSHS_REHYDRATE_*` + `OBS-18`)。凡拿立单值当"改前基线"的判据都会**假红**。
本棒变更 = §3.5 之后新增 **7 键**(`JITTER_ENABLE` / `JITTER_SAMPLE_MAX` / `JITTER_MIN_SAMPLES` / `JITTER_HIST_MAX_MS` / `JITTER_HIST_BUCKETS` / `JITTER_SAMPLE_GAP_MS` / `RELAY_UTIL_MAX_PCT`)+ `JITTER_LIMIT_MS` 行**语义扩展注** + §6 新增 `OBS-19` / `OBS-20`。
- **源码 md5**:`jitter.ts` = `ea72bdf6c0c4b78b6c791f1bc3a7709b`(新增)|`directory.ts` = `e24501b1cada3d2aeb03a8d6ff9a572b`|`switcher.ts` = `65942838b3418f0a0d22b0c0d1dd4165`|`server.ts` = `33c9186762e7e6977a56f472a7b11e89`
- **构建产物 md5**(部署对账值):`jitter.js` = `47e248d84aa6fa6c8f29f18f942b82cb`|`directory.js` = `e2347a7047d5cb6a10cd03fda51db40e`|`switcher.js` = `5e09d04452c8ded7c3824fb450ecfcb0`|`server.js` = `c643ea04d6a42a80e87b146e4580be79`
- **开工单 §8 前前缀指纹**:**`dbcbe633aa1aef92f4c35c77fad3ec11` 复核一致** ✅(开工前校验通过 ⇒ 本棒依据有效)
#### ⑦ 三条必须留档的实测发现(⛔ 均未越线处置)
1. 🔴 **`JITTER_SWITCH_MS` 未新造** —— 参数表**已有** `JITTER_LIMIT_MS = 20`(序⑥ 的 `p95(|ΔRTT|)` 达标限值),"超标"与"该换路"是**同一件事** ⇒ 直接**复用**该键。🔑 理由 = **同一事实两处写**是本线的知识碎片化病根。⚠️ 该值仍是**业界估值**(非实测),定值属待测项。
2. 🔴 **`dshs` 侧 `/opt/dshs/lib/net/relay/server.js` 比 `dsh-relay` 侧落后一个版本**(取证:`diff` 13 行,**只有** relay 侧有序㉔ 的 `statusContent` 注入位)⇒ 序㉔ 当时**只铺了 relay 侧**、`dshs` 侧被漏掉。本棒两边同铺 ⇒ **该既存不一致已随本棒消除**(属同一文件新版本不可剥离,非"顺手优化")。
3. ⚠️ **jitter 告警计数与日志条数口径不同**:`/status.jitter.alerts` = **当前进程**累计(实测 `2`),`journalctl | grep -c relay-jitter` = **跨进程**累计(近 30 min 实测 **11** 行 —— 期间 `--scene all` 重启过 relay)。⛔ 两者**不可直接对比**。告警原文:`[relay-jitter] ⚠ 会话 ops/w-106 抖动超标(p95|ΔRTT|=1540ms ≥ 阈值 20ms,样本 4 个,最近 rtt=423ms)⇒ 该链路的对端应换更稳的候选`。
4. ⚠️ **E3(路径多样性)未取得机器断言面**:判据"每连接 2–3 条候选 ⇒ `/status` 可查候选数 ≥ 2"的落点**落在 §3.1 在册文件集之外**(需改 `src/worker/relay-tunnel.ts` 或 `src/web/server.ts` 暴露字段;而 relay 的 `/status` **语义上不承载** client 侧候选概念)⇒ 按 **§9-5 停下报告**、未扩大。**既存事实**:候选链实现已在册(`directory.ts:789 listOverlayRelayCandidates` + `web/server.ts:605` 注入 worker),但 47 当前形态(单 relay 回环 + 443 兜底)**实际可用候选 = 1 条** ⇒ "冗余"尚未成立。
---
## §9 回头条件(**一出现必须回头**)
1. 要**改 `switcher.ts` 冷却语义** ⇒ 停下报告(持久禁令)。
2. 要**新增公网监听口 / 改 nft·nginx** ⇒ 停下报告(R5)。
3. 要引入**端到端加密**(E2E)⇒ **属真取舍**(加密即失去按哈希共享块)⇒ 停下上抛。
4. 要**改 `RELAY_MAX_HOSTS` / 45% 容量口径值** ⇒ 停下报告。
5. 需要**超出 §3.1 文件集** ⇒ 停下报告(R7)。
6. `E1` 跑不出机器断言 ⇒ 停下报告,⛔ 不许放宽判据凑绿。
7. 零回归三件套任一退化 ⇒ 停下报告。
8. 🔴 ⛔ **不许把 `RELAY_FAILOVER_COOLDOWN_MS=0` 写进任何回滚 / 演练 / 夹具路径**。
---
## §10 未验证项
| # | 项 | 状态 |
|---|---|---|
| 1 | 各网络类型实际占比(决定骨干数量需求) | ⚠️ 上游仍为推演设定 |
| 2 | 骨干上行带宽是否够(家宽上行常远小于下行) | ⚠️ 上游未实测 |
| 3 | E2E 加密与块级共享的取舍 | ⚠️ 本单**未做**,登记待评估 |
---
## §11 指纹与状态
| 项 | 值 |
|---|---|
| 本单 §8 之前正文前缀指纹 | **`dbcbe633aa1aef92f4c35c77fad3ec11`**(口径 = `sed '/^## §8 /,$d' 交接单_骨干稳定选路与加密_20260917.md \| md5sum`) |
| 参数表指纹(立单时) | **`42238175d84319ada99afa56d583f9db`** |
| 代码仓 HEAD(立单时) | `04776af` |
| 本单全文件 md5(立单时) | `f85b86234c2ecfaef5b2e43aa40d86a3`(176 行) |
| 本单全文件 md5(**序㉖ 执行棒回填 §8.1 后**) | **`f49a34275fa6628107e184c7f3a9a52e`**(250 行;§8 前前缀仍 = `dbcbe633aa1aef92f4c35c77fad3ec11` ⇒ 回填未触及依据区) |
| 本单状态 | **待执行** ⇒ 交**序 ㉖ 执行棒** |