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

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

43 KiB
Raw Blame History

交接单 · 参数表 · 观测 · 权限评估(覆盖网络线 · 序 ⑤)

线:覆盖网络线 | 序:⑤(定序 ③→④→⑤,见入口 §0) 性质:规划棒产出(本单只出单,⛔ 未动任何服务器、未改任何代码) 产出时间:2026-09-17 09:18 | 规划会话:覆盖网络线-序5规划棒 唯一来源:覆盖网络_应用场景与待完善清单_20260916.md §五 第 5 项(= 「参数表 + 观测最小集 + 权限评估(P1 三条)」,状态 可并行)+ 同文件 §四 P1 行(三条缺口的原文)+ 交接单_443兜底_20260917.md §8.7 / §8.8 上一轮:序 ④ 已收官(交接单_443兜底_20260917.md §8)⇒ ⛔ 不要重做 ② / ③ / ④ 执行棒:按本单 §5 开工,不读规划会话上下文;收口必须回填 §8。


§1 目标

把「参数 / 观测 / 权限」三件事收成可交付的三件产物,使覆盖网络的容量结论从"结构可信"变为"可复算"、运行状态从"靠人读日志"变为"一条命令出 PASS/FAIL"、权限面从"没评估过"变为"逐项有结论":

  1. 参数表固化 —— 新建唯一一张参数表,把散在 6 份文档里的输入参数(设备占比 / 打洞率 / 每玩家带宽 / 消息频率 / 心跳)+ 代码里已固化的常量收成一张表,每行带「来源等级 + 来源定位」;并填掉 443 单 §8.8 留下的 45% 口径待填参数位(relay --max-hosts 数值 / /status.capacity{max,used} 语义 / retryAfterMs 口径)。
  2. 观测最小集 —— 定出最小指标集 + 阈值 + 判别器,落成一个探针脚本(一条命令、一个退出码);补上今天唯一缺的计数器(DIAL 只有日志行、无法被脚本断言)。
  3. 权限影响评估(R5) —— 对「虚拟网卡驱动 / 骨干开端口 / nft 打洞 / 第二中继机(L3) / 443 兜底入口 / relay 拨号白名单」逐项出权限面评估表,结论只允许是「收窄」或「维持」;L3 跨机真容灾只评估、不实施。

判定"做完了没有" = §6 的 E1–E9 全绿(每条都能被第三方复现)。

为什么这是序 ⑤ 的最小正确形态(不是妥协,是已排过序):

  • 清单 §五 第 5 项原文就是三条一体、且标注「可并行」;三条本质耦合:45% 口径要填数值 ⇒ 数值要么来自参数表内的其它参数(推导)、要么来自实测(序 ⑥);而"打洞率"这类参数要拿实测值,前置是权限评估(打洞需虚拟网卡/管理员权限)⇒ 拆开做会各留一个空洞。
  • 参数表的性质是「固化」(清单 §五 用词),不是重新推演 ⇒ 本单不引入新模型、不引第三方依赖、不做架构改动。
  • 443 单已把「不做容量承诺」显式后置给序 ⑤(其 §4.1-6 / §8.8)⇒ 本单必须把那个洞填上,否则兜底入口启用后 --max-hosts=0(不限)等于没有过载保护。

§2 只读前置(⛔ 只读,不改)

执行前逐条核一遍,任何一条与"期望"不符 ⇒ 先停下来报告,别猜。 47 = 47.77.182.89、106 = 106.54.21.172;⚠️ ssh 别名 bt-server 端口已陈旧(配置写 32022,实测 Connection refused)⇒ 用 -p 22(443 单 §8.7-⑤ 已记,⛔ 不必再改 ~/.ssh/config)。

# 核什么 命令 期望
P1 三单元在跑(⚠️ nginx 不归 systemd 管,443 单已定口径) ssh -p 22 47 'systemctl is-active dshs dshs-relay dshs-pg' + ssh -p 22 47 'ss -lntp | grep -E ":(443|20080)\b"; pgrep -c nginx' 3×active;443 在听;nginx 进程数 > 0
P2 relay /status 全字段原文(= 观测现状基线,收尾要对照) ssh -p 22 47 'curl -s 127.0.0.1:20080/status' 含 online[] / capacity{max,used,free?} / identityRequired / identityOk / trustedSigners / revokedHosts / endpoints[] / networks[] / counters{authed,authFailed}
P3 现役容量参数(45% 口径的空位就在这) ssh -p 22 47 'systemctl cat dshs-relay | grep -nE "max-hosts|ExecStart"' --max-hosts 未出现(默认 0 = 不限)⇒ 本单 S4 要填
P4 relay 侧参数常量(参数表要收的"代码事实") 本机 grep -n "MAX_ENTRIES|MAX_ENTRY_LEN|DEFAULT_CAPACITY_RETRY_AFTER_MS|refreshAfterSeconds|300|5_000" src/net/relay/directory.ts src/net/relay/server.ts MAX_ENTRIES=8 / MAX_ENTRY_LEN=512 / capacityRetryAfterMs=5000 / 目录刷新 300 s / 取目录超时 5 s
P5 端口区间与回环池现状(观测要断言的对象) ssh -p 22 47 'ss -lntp | wc -l';ssh -p 22 47 'nft list ruleset | wc -l' 443 单收口态 = 监听口 79 / nft 72(⚠️ 若不同,以本次实测为新基线并写明)
P6 47 的机器规格(--max-hosts 数值的直接输入;旧记录 1.8 GB / 2 核 是 09-08 的,必须复核) ssh -p 22 47 'nproc; free -m | head -2; df -h / | tail -1' 实测值(⛔ 不得沿用旧记录)
P7 跨云实测带宽(旧记录 47↔106 ~22 KB/s,必须复核) 走既有 relay 数据面做小样本(≤1 s、≤1 MB) 实测值(⛔ 不得沿用旧记录;⛔ 不许对生产做压测)
P8 106 的会合 env(⛔ 只记录,不动) ssh -p 22 106 'grep -rn "DSHS_RENDEZVOUS_URL|DSHS_RELAY_URL|DSHS_OVERLAY" /etc/systemd/system/*.d/ /etc/dshs-cluster.env 2>/dev/null' 106 有 DSHS_RENDEZVOUS_URL=wss://alotbuy.com/dshs-relay ⇒ 🔴 agent 面不吃引导链,⛔ 不许顺手撤(撤掉 = tunnel===undefined 生产回归,443 单 §8.2/§8.7-② 已判不执行)
P9 本机代码基线 + 一个必须知道的事实 git -C D:/github/dsh_shenxian rev-parse HEAD;git -C D:/github/dsh_shenxian status --porcelain -- src/net/relay/ 640813e;输出 ?? src/net/relay/(整目录未被 git 跟踪 ⇒ 443 单 §8.1 注)⇒ ⛔ 永不 git checkout --(会连带丢掉工作区其它未提交改动,且拿不回覆盖网络线代码)
P10 参数散落点(⛔ 只 grep 计数,不做全库摸底) 对清单 §三 点名的 6 份文档逐份 grep -c 关键参数词(打洞率|jitter|KB/s|扇出|心跳) 每份的命中数(用于 S1 的"散落点清单",⛔ 不是重读全文)

判据先说清(P6/P7 → S4 的因果):--max-hosts 只能从实测机器规格+实测单节点成本推出;沿用旧记录会得到一个不可复算的数 ⇒ P6/P7 是硬前置,不是"顺便看看"。


§3 范围

3.1 要改的(且只有这些)

层 位置 改什么
工作区文档 新建 参数表_覆盖网络_20260917.md(工作区根) 唯一参数表:参数 / 值 / 单位 / 来源等级(实测·估值·推导·待测) / 来源定位(文件:行) / 复算式;45% 口径在此填出数值
本机 · 代码 新建 scripts/overlay-probe.cjs + test/overlay-probe.test.mjs 观测最小集的唯一入口:一条命令、打印 ≤12 行、退出码 0/1
本机 · 代码 src/net/relay/server.ts(唯一代码改动,只加计数器) counters 增 dial / dialDenied / dialFailed(判别器计数),随 /status 暴露
本机 · 代码 test/relay.test.mjs(或就近新增用例文件) 计数器三分支的先红后绿用例
47 · systemd 新建 /etc/systemd/system/dshs-relay.service.d/capacity.conf Environment=DSHS_RELAY_MAX_HOSTS=<参数表数值> ⇒ 45% 口径生效(⛔ 不是硬编码进代码)

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

  • ⛔ 不动官方 dsh 主程序与缓存(R2)——src/net/relay/* 是自研代码,可动,但只加计数器;⛔ 不改 HELLO / DIAL / DIAL_ACK 帧格式、⛔ 不改鉴权与网络隔离语义、⛔ 不改 identityRequired 任何行为
  • ⛔ 不碰序 ②/③/④ 成果:网抽象与逻辑名索引、引导三级链、DSHS_OVERLAY_BOOTSTRAP_SEEDS、DSHS_OVERLAY_ADDR_OVERRIDES、relay-direct.conf(443 兜底)、四层密钥与 relay-keys.json
  • ⛔ 不改 106 的 DSHS_RENDEZVOUS_URL(P8;字面撤掉 = 生产回归)
  • ⛔ 不做 L3 跨机真容灾(只出权限评估;落地归序 ⑥)
  • ⛔ 不新增公网端口 / 不新增 DNS 记录 / 不改 nginx / 不动 Cloudflare
  • ⛔ 不碰数据面(打洞 / UDP)、不碰 presence / 房间层(清单 §五 第 7 步)
  • ⛔ 不做 dashboard、不引外部监控依赖(观测最小集 = 一个脚本,不是一套系统)
  • ⛔ 不 commit、不 push

§4 决策点

4.1 已定项(规划棒已拍 —— 执行棒不得自行更改;要改必须回写 §8 并说明理由)

  1. 三条一体、一单交付,参数表的唯一落盘位置 = 工作区根 参数表_覆盖网络_20260917.md。⛔ 不再往那 6 份文档里各写一份(那正是"散在 6 份、不可复算"的成因,清单 §三 第 1 条)。
  2. 参数表必须有「来源等级」列:实测 / 估值 / 推导 / 待测。⛔ 禁止把估值写成实测;凡 待测 项,值留空并写明「由序 ⑥ 用 3–5 台真机换掉」。
  3. 45% 口径填「单台中继的 --max-hosts」,⛔ 不填"全网 45%"。理由:--max-hosts 是每实例参数,填一个与实例无关的全网数字不可执行;"1000 台的 45%"只作校验上界使用。
  4. 观测最小集 = 「一条命令出 PASS/FAIL」。指标 ≤12 条,每条一个阈值 + 一个退出码;⛔ 阈值不许是脚本里的魔数,必须能在参数表里找到。
  5. 判别器计数必须补(dial / dialDenied / dialFailed)—— 这是本单唯一的代码改动。依据:443 单 §12 的教训原文「静默失效靠判别器定位」,而今天判别器只有日志行(DIAL manager -> w-106:21000 ok),脚本无法断言 ⇒ 观测最小集缺了最关键的一条。
  6. L3 跨机真容灾:只出权限评估,⛔ 不实施;落地归序 ⑥。理由:做出 L3 需要"换机或第二公网 IP",而现在全网只有 2 个节点,单点风险已由序 ④ 的 443 兜底 + CF 双路部分对冲;而序 ⑥ 本来就要起 3–5 台真机,第二中继机在那个批次里落地最省事(同一批采购/配置/验收)⇒ 没有理由现在单独为它花一台机器或一个公网 IP。
  7. 权限评估的判据形态固定为 6 列:对象 / 是否扩大权限面 / 扩大到哪一类(入站面·权限位·凭据外发·可见面)/ 是否已可收窄 / 证据(代码行或命令原文)/ 结论(收窄·维持)。⛔ 结论列只允许「收窄」或「维持」;若某项确需扩大 ⇒ 停下报告(命中 R5),⛔ 不得自行执行。

4.2 交给执行棒自决(⛔ 不上升为提问)

参数表的表结构细节与行序 · 探针脚本的文件名/输出格式/退出码约定 · 计数器命名与埋点行位置 · --max-hosts 的推导公式与最终取值(只要满足 §6 的 E3 约束)· capacity.conf 的备份命名 · 单测用例划分 · 参数散落点的 grep 词表 · 探测样本大小。

4.3 真需要用户拍板的(命中才问,且一轮只问这一句)

本轮为空。

  • L3 跨机真容灾已按 §4.1-6 自决后置到序 ⑥(现在做要新增机器/公网 IP = 资源承诺;而序 ⑥ 本来就要多台 ⇒ 合批明显更优,上抛门槛不成立)。
  • 其余全部落在 §1 的边界内自决清单(参数推导 / 观测实现 / 权限评估为产出而非动作)。
  • ⇒ 若将来要提前做 L3(例如你打算换掉 47),那时唯一缺口 = 第二台机器或第二个公网 IP,属资源承诺,届时才需要你出手。

4.4 技术实现裁决顺序

按 dsh-decision-method §4.4;规则冲突按下序取首个命中项:R8(开发环境服务器 ⇒ 该动就动,动手前一句话说明) → §1 边界内自决清单(部署/重启/改配置/技术选型) → §3 其余红线(R5/R7/R9/R10 永远是硬约束)。⛔ 冲突 ≠ 门禁。


§5 步骤(S0–S7,每步自带一次可执行的验证)

S0 · 只读取证(⛔ 不改任何东西)

跑完 §2 的 P1–P10,产出"现状快照"(命令 + 原文输出 + 判定)。任何一条与期望不符 ⇒ 停下报告。 额外取三件(本单设计的必要输入):

  • 47 规格实测 + 跨云带宽实测(P6/P7)⇒ S4 的 --max-hosts 推导输入;
  • /status 全字段原文(P2)⇒ 既是"观测现状基线",也是 E8 的对照基准;
  • 6 份文档的参数散落点计数(P10)⇒ S1 的收口清单(⛔ 只计数,不重读全文)。

S1 · 参数表落盘(工作区根 参数表_覆盖网络_20260917.md)

收三类行:

  • 输入参数(清单 §三点名的 5 类):设备占比 / 打洞率 / 每玩家带宽 / 消息频率 / 心跳;
  • 代码已固化的常量:目录刷新 300 s / 取目录超时 5 s / MAX_ENTRIES=8 / MAX_ENTRY_LEN=512 / capacityRetryAfterMs=5000 / 实例端口区间(w-47 20000+、w-106 21000+)/ dialer 回环池 64 口(127.0.0.1:25000..26099);
  • 本单新增:--max-hosts(含推导式 + 分母口径)、观测阈值(每条指标一个)。

硬要求:每行必须有来源等级 + 来源定位;待测 行必须在表末单独汇总计数。

验证:抽样 3 行按"来源定位"回原文复核 ⇒ 逐字对得上;--max-hosts 行按表内公式手算一遍能得同值。

S2 · 观测最小集落成探针(本机代码 scripts/overlay-probe.cjs)

最小指标集(≤12 条,全部来自既有 /status + 本机/远端只读命令): 在册节点数 · capacity{max,used,free} · identityRequired / identityOk · trustedSigners · revokedHosts · counters.authed / authFailed · counters.dial / dialDenied / dialFailed(S3 新增) · 端点表 endpoints[] · 双实例探活(w-47:19100 / w-106:19000)· 门户 200 · 监听口数 · nft 行数。 每条一个阈值 → PASS/FAIL;退出码 = 全绿 0 / 任一红 1(可被 automation 直接消费)。⛔ 阈值从参数表读,⛔ 不硬编码。

验证:47 上跑一次 ⇒ 全绿、打印 ≤12 行、exit 0;反证:把某阈值临时调紧 ⇒ 变红、exit 1(证明确实在读阈值而不是恒绿)。

S3 · 判别器计数(本机代码 src/net/relay/server.ts —— 本单唯一代码改动)

counters 增 dial / dialDenied / dialFailed;埋点 = DIAL 帧处理路径的三个分支(放行 / 白名单或同网校验拒绝 / 目标端点不可达)。加先红后绿单测。 ⚠️ npm test 必须 Node 22;⛔ 不改帧格式、不改校验顺序。

验证:本机单测通过;47 上重启 dshs-relay 后制造一次成功拨号 ⇒ counters.dial +1 且 /status 可见。

S4 · 45% 口径填值 + 设值(47)

按 S1 的推导式(输入 = P6 实测规格 + P7 实测量 + 参数表内的单节点成本)算出 --max-hosts,写进新建 drop-in dshs-relay.service.d/capacity.conf(⛔ 不改 dialers.conf、⛔ 不改主单元)。设值前后各取一次 /status。

硬约束(防自锁):新值必须 > used × 4 且 ≥ 参数表下限;不满足 ⇒ 不设值,改为在 §8 写明"参数表已填数值但因 X 不设值"。

验证:curl -s 127.0.0.1:20080/status ⇒ capacity.max = 设定值、free = max - used;online[] 未变;identityOk 未退化。

S5 · 权限影响评估(只有产出,无动作)

按 §4.1-7 的 6 列逐项评估: ① 虚拟网卡驱动(打洞所需)② 骨干节点开端口 ③ nft 打洞规则 ④ 第二中继机(L3 跨机真容灾) ⑤ 443 兜底入口(复核是否真的零扩大)⑥ relay 拨号白名单 dialers(R5 引入)⑦ 每机独立密钥与信任根保管。 每条给证据列(代码行 / 命令原文)。⛔ 不许出现"我认为"。

验证:评估表每条有证据;结论列只出现「收窄」或「维持」;L3 行明确写「只评估不实施 + 归序 ⑥」。

S6 · 端到端复验 + 不退化检查

① /status 全字段对照 S0;② online[] 两台在、双实例面 200;③ 门户 curl -H "Host: alotbuy.com" 127.0.0.1:3080/ = 200;④ 监听口数 / nft 行数 与 S0 逐字一致;⑤ npm test 全绿;⑥ 探针全绿。

🔴 计数器读数纪律(443 单 §8.6 的教训,必须照做):authed / authFailed / identityOk / dial* 都是累计值 ⇒ "逐字一致"物理上不成立。必须逐条归因增量:每个增量都要能指名到"我做的哪一次动作"。⚠️ 101 探针本身会污染 authFailed —— 读数前先扣掉自己打的探针次数。

S7 · 回写

① 参数表_覆盖网络_20260917.md 落盘,并在入口 §1 的文档表登记一行;②本单 §8 回填(含指纹);③把入口 §2「🎯 本轮动作」推进到下一棒(序 ⑥ · 3–5 台最小形态);④⛔ 不 commit / 不 push。


§6 验收(判据清单)

# 判据 命令 期望
E1 参数表落盘且每行有来源等级 + 来源定位 `grep -c "^ " 参数表_覆盖网络_20260917.md`;抽样 3 行按定位回原文复核
E2 可复算(这是"结构可信 → 数字可信"的判据) 任取 2 行按表内公式独立手算 手算值 = 表内值
E3 45% 口径已填数值且生效 ssh -p 22 47 'curl -s 127.0.0.1:20080/status' | python -c "import sys,json;c=json.load(sys.stdin)['capacity'];print(c)" max > 0、max = 参数表值、free = max - used、max > used×4
E4 观测探针可用 + 非恒绿 node scripts/overlay-probe.cjs; echo $? ⇒ 再调紧一阈值复跑 首次 exit 0 全绿;调紧后 exit 1 且指名哪条红
E5 判别器计数存在且可增 /status 取 counters.dial;制造一次成功拨号 + 一次白名单拒绝 两个数分别 +1,/status 可见
E6 观测阈值无脚本魔数 grep -nE "[0-9]{3,}" scripts/overlay-probe.cjs 每个命中项都能在参数表里找到同名阈值(⛔ 无孤值)
E7 权限评估表完整 通读 §5-S5 产出 每条有证据列;结论 ∈ {收窄, 维持};L3 行写"只评估不实施 + 归序 ⑥"
E8 零退化 S0 快照 vs 收口:/status 全字段 + 双实例面 + 门户 + npm test 除已归因增量外逐字一致;npm test 全绿(写清 a/b/c 计数)
E9 零新增暴露面 ssh -p 22 47 'ss -lntp | wc -l';ssh -p 22 47 'nft list ruleset | wc -l';ss -lntp | grep -c 20080 与 S0 逐字一致(443 单收口态 = 79 / 72);relay 仍只绑 127.0.0.1

判定:E1–E9 全绿 = 本单完工。任一红 ⇒ 按 §7 该项回滚,并在 §8 写清"卡在哪 + 已做到哪一步 + 什么条件一出现必须回头解决"。


§7 回滚

项 回滚
S4 capacity.conf 删该 drop-in → daemon-reload → systemctl restart dshs-relay(回到 max=0 = 不限;⚠️ 重启会重建 relay 会话,online[] 需复验)
S3 计数器代码 47 侧 = 恢复铺前备份 /opt/dshs/lib/net/relay/server.js.bak-<时间戳>-pre-p5obs(铺前必须先备份);本机 = 靠备份副本(⛔ 禁止 git checkout --:src/net/relay/ 不在 git 里,且会连带丢掉工作区其它未提交改动 —— 443 单 §8.1 注)
S2 探针 独立新增文件,删掉即可;⛔ 不改 state.py(避免影响别人的"第一信号")
S1 参数表 新增文档,回退即可;若已登记进入口 §1 表,记得同步撤行

§8 回报格式(执行棒按此格式收口)

## §8 执行回报(执行棒 · 2026-09-17 HH:MM)
1. S0 快照:P1–P10 逐条 = 命令原文 + 输出 + 判定(不符的先说);含 47 规格与跨云带宽**实测值**
2. 参数表:行数 / 待测行数 / `--max-hosts` 推导式 + 取值 + 分母口径(原文摘录)
3. 落盘清单:新建/改动的**每个文件绝对路径** + 备份文件名 + 改前改后指纹(md5 / hash-object)
4. E1–E9:逐条命令原文 + 原文输出 + 绿/红
5. 观测:探针输出原文(≤12 行)+ 退出码 + **调紧反证**(哪条变红)
6. 权限评估:6 列表逐条(结论只能是 收窄/维持)+ L3 行的"只评估不实施"原文
7. 不退化:S0 对照 / 双实例面 / 门户 / `npm test` 计数(a/b/c)+ **计数器增量的逐条归因**
8. 未过项 / 遗留:逐条写"卡在哪(证据)+ 已做到哪一步 + 什么条件一出现必须回头解决"
9. 指纹:本单回填后 `md5sum`(⚠️ 别把全文件 md5 写进正文 —— 自指即失效,用 `sed '/^## §9 指纹/,$d' | md5sum` 口径)

附 A · 本规划棒已核实的事实(执行棒不必重复探索)

事实 依据
序 ⑤ 的原文口径 = 「参数表 + 观测最小集 + 权限评估(P1 三条)」,状态 可并行 覆盖网络_应用场景与待完善清单_20260916.md §五 第 5 行
三条缺口的原文:「参数表是空的(libp2p 默认值可直接固化)· 观测最小指标集与阈值缺失 · 权限面评估缺失 = 命中 R5(虚拟网卡驱动需管理员权限 / 骨干开端口 / nft 打洞)」 同上 §四 P1 行(第 85 行)
"不可复算"的根因 = 参数散在 6 份文档、无统一表 同上 §三 第 1 条(第 65 行)
序 ⑥ 的口径 = 「3–5 台最小形态跑通(把 3 个关键估值换成实测)」,依 2/3/4 同上 §五 第 6 行 ⇒ L3 归它是合批,不是推迟
relay 容量/准入机制已存在:--max-hosts(DSHS_RELAY_MAX_HOSTS,默认 0 = 不限)+ /status.capacity{max,used,free} + 满载 at-capacity + retryAfterMs(默认 5000 ms) src/net/relay/main.ts:64/82、src/net/relay/server.ts:62/102/104/367/368/502-504/757-759
客户端侧排队语义已实现(不消耗退避、不累计 attempts) src/net/relay/client.ts:532/881-896
/status 的 counters 只有 authed / authFailed ⇒ DIAL 无计数(判别器只能读日志) src/net/relay/server.ts:276-278 / 347-348 / 510-512(本次实测 grep)
DIAL 的隔离语义 = 同网 + 本网白名单,跨网"能不能拨"都走不到(结构性隔离) src/net/relay/server.ts:22 / 108 / 261-265
45% 口径的待填位置(由 443 单显式后置给序 ⑤) 交接单_443兜底_20260917.md §4.1-6 / §8.8
443 单带回的四条遗留(本单与它们的对应关系见下) 同上 §8.7
判别器教训原文:「静默失效靠判别器定位」(relay 有没有 DIAL) 入口 §0 + 交接单_relay落地R2-R4_20260916.md §12

§8.7 四条遗留 → 本单的对应关系(⛔ 不许漏):

遗留 本单处理
① D2 字面判据不可满足(CF 泛解析) 不在本单范围(未做灰云记录)⇒ 若要"解析层也不经 CF"才回头,登记为序 ⑥ 后候选项
② 106 的 agent 面不吃引导链 本单只记录、⛔ 不动手(P8);若要 106 agent 面也能用兜底 ⇒ 需改 worker/agent.ts/relay-tunnel.ts,归序 ⑥
③ 「同源优先」修法(已补) 已闭环;⚠️ 其遗留条件 = "若将来 relays[] 引入非首位更优的显式优先级语义(序 ⑤ 参数表可能引入)⇒ 需重新定义同源优先与它的先后关系" ⇒ 本单必须回答这一条(在参数表里写明:relays[] 顺序语义 = 主入口首位,本单不引入优先级新语义)
④ 引导链缓存两支无"答出者"信息(切兜底有 ≤300 s 收敛期) 本单把 refreshAfterSeconds=300 收进参数表并写明该收敛期(作为已知边界,⛔ 不改缓存结构)

附 B · 硬约束复述(防走偏)

  • ⛔ 本轮只出单:不改服务器、不改代码、不 commit、不 push、不做 presence。
  • ✅ 只做正向迭代(R11):本单任何一步若让任一维度净变差 ⇒ 立即停下复盘;拿不出正向做法 ⇒ 停止执行。
  • ✅ 权限只准收窄(R5):本单零新增公网端口、零新增入站面、零凭据外发;权限评估是产出不是动作;若某项确需扩大 ⇒ 先停下报告,⛔ 不得自行执行。
  • ⛔ R7:只做 §3.1 列出的改动;执行中发现别的"小问题"先报告、后动手。
  • ⚠️ curl 判据必须 --http1.1(门户 http2 on,否则假 404)。
  • ⚠️ 47 的 lib = /opt/dshs/lib/、106 = /opt/dshs-cluster/lib/;部署 = 本机 build 后 scp;npm test 必须 Node 22。
  • 🔴 --max-hosts 设值必须满足 max > used × 4(防把自己锁在门外);不满足 ⇒ 不设值并写清原因。
  • 📌 参数表里 待测 项一个都不许编数 —— 编出来的数会让整张表失去"可复算"的资格。

§8 执行回报(执行棒 · 2026-09-17 09:55)

判定:E1–E9 = 8 绿 + 1 部分绿(E5 的"在 47 上受控制造一次真实拨号"未做到 —— 卡点、已做到哪一步、回头条件见 8.7-①)。三条硬约束全部守住(见 8.7-④)。


8.1 S0 快照(P1–P10:命令原文 + 输出 + 判定)

# 命令(原文) 输出(原文摘要) 判定
P1 ssh -p 22 bt-server 'systemctl is-active dshs dshs-relay dshs-pg' + ss -lntp | grep -E ":(443|20080)\b"; pgrep -c nginx active×3;LISTEN 127.0.0.1:20080(node)/ LISTEN 0.0.0.0:443(nginx);nginx_pid=4 ✅
P2 ssh -p 22 bt-server 'curl -s 127.0.0.1:20080/status' 全字段见 8.2 表;⚠️ 两处与期望不符(见 8.2) ⚠️ 已纠偏
P3 systemctl cat dshs-relay | grep -nE "max-hosts|ExecStart" ExecStart=… --base 19000 --span 3000 --max-hosts 0 ⇒ ⚠️ --max-hosts 并非"未出现",是显式写在 ExecStart 里 ⚠️ 改变 S4 做法
P4 grep -n "MAX_ENTRIES|…" src/net/relay/{directory,server}.ts directory.ts:60 300 / :63 8 / :64 512 / :521 5000;server.ts:52/54/56/58/60/63/69/71/73/76 ✅ 与期望一致
P5 ss -lntp | wc -l;nft list ruleset | wc -l 79 / 72 ✅ 与 443 单收口态逐字一致
P6 nproc; free -m | head -2; df -h / | tail -1; hostname 2 核;mem total 1870 / used 868 / available 1002 MB;/dev/vda3 40G 15G 23G 39%;iZrj99af19cibck1ge93tqZ ✅ 实测(⛔ 未沿用 09-08 旧记录)
P7 ①/status 读 rttMs;②curl -w 打 relay 回环口(6 样本);③head -c 1048576 /dev/zero | curl -X POST --data-binary @- http://127.0.0.1:44133/ ① w-106 rttMs=336 / manager 7;② TTFB 0.676–1.083 s;③ code=405 up=131072 total=0.6849 ⇒ 上行下界 ≈ 192 KB/s ⚠️ 部分(稳态吞吐取不到,见 8.7-②)
P8 ssh -p 22 test106 'grep -rn "DSHS_RENDEZVOUS_URL|…" /etc/systemd/system/*.d/ /etc/dshs-cluster.env' ⚠️ 指定路径零命中;全局搜得真身在 /etc/dshs-worker.env:DSHS_RENDEZVOUS_URL=wss://alotbuy.com/dshs-relay ⚠️ 位置纠偏(结论不变)
P9 git rev-parse HEAD;git status --porcelain -- src/net/relay/ 640813e84e5e48c56ca39d765d7b195884e58ebc;?? src/net/relay/ ✅;⛔ 全程未 git checkout --
P10 7 份文档逐份 grep -c "打洞率|jitter|KB/s|扇出|心跳" 调研 19 / 千台 18 / 瓶颈 9 / 游戏 7 / 清单 7 / 百台 2 / 骨干 2 = 64 处 ✅ 只计数、未重读全文

P8 附注(只记录、⛔ 未动手):/etc/dshs-worker.env 的四个副本里,.bak-20260916-1617 仍是 ssh://[email protected]:32022,现行与另两个 .bak-r4 / .bak-r6 已是 wss://alotbuy.com/dshs-relay ⇒ 「106 的 agent 面不吃引导链」这条前提成立,⛔ 未撤。


8.2 P2 的两处事实纠偏(都不是阻塞,但都会误导下一棒)

# 期望(交接单 §2) 实测 影响
① counters 只有 authed/authFailed 实测还有一个:refused / dropped / streamsOpened / protocolErrors / backpressurePauses + 序③ 的 identityOk / identityRequired / trustedSigners / revokedHosts(共 11 项 + 本次新增 3 项) ⛔ 附A 那句"只有 authed/authFailed"作废;DIAL 仍无计数这条结论依然成立(新增前确实没有 dial*)
② capacity{max,used,free?} max=0 时只有 {max,used},free 字段不存在(不是 null、不是 0);free 仅当 max>0 才出现(server.ts:502-506);拒绝载荷里恒为 0(:761) ⇒ E3 的 free = max - used 只在设值后才能判(已写进参数表 §5.5)

P3 的连带纠正(S4 做法因此改变):main.ts:64 用 DSHS_RELAY_MAX_HOSTS 作默认、:82 由 CLI --max-hosts 覆盖 ⇒ 主单元里那句显式的 --max-hosts 0 会把 drop-in 的 Environment= 静默吃掉。故 capacity.conf 重写 ExecStart(ExecStart= 清空 + 完整命令)—— 仍是"新建 drop-in、⛔ 不改主单元、⛔ 不硬编码进代码",且避免了本项目最忌讳的静默失效。


8.3 落盘清单(绝对路径 + 备份 + 改前/改后指纹)

# 位置 动作 备份 改前 → 改后
1 E:\ProgramData\AI技能\aliyun-dsh-server\参数表_覆盖网络_20260917.md 新建 — — → 155 行(md5sum 见 8.9)
2 D:\github\dsh_shenxian\scripts\overlay-probe.cjs 新建 — — → 0cd76d986273e04631510aa7d215d16c(git hash-object 98b91d8280147f025792301f56b95582e9b901e5)
3 D:\github\dsh_shenxian\test\relay.test.mjs 改(新增 T20) — git hash-object 改前未存 → 846460b5b6d6a3957abe29c63fce67e0a2e77f9d
4 D:\github\dsh_shenxian\src\net\relay\server.ts 改(只加计数) — 7f95fe5f6b1865102d12a0917c4b4f9b7ecf2035 → dc062c83ac0167f40d0e5d2c09f568b4(git hash-object)
5 D:\github\dsh_shenxian\lib\net\relay\server.js(build 产物) 改 — 64d814e7d68163752247d4a190b3b538 → 623374d948e37c87db3401f00ebf4dbe
6 47 /opt/dsh-relay/lib/net/relay/server.js(relay 真身) 改 server.js.bak-20260917-pre-p5obs(64d814e7…) 同 5
7 47 /opt/dshs/lib/net/relay/server.js 改(保双副本对账) 同上 同 5
8 106 /opt/dshs-cluster/lib/net/relay/server.js 改(保 29/29 对账口径) server.js.bak-20260917-pre-p5obs(64d814e7…) 同 5
9 47 /etc/systemd/system/dshs-relay.service.d/capacity.conf 新建 — — → a48df38f859adc3e8589d85bfd8a9ff1

⚠️ §7 回滚表的一处纠偏:relay 运行的代码在 /opt/dsh-relay/lib/(主单元 ExecStart 指它),⛔ 不是 /opt/dshs/lib/。据此本次两处都备份、都铺(/opt/dshs/lib 只为双副本对账与 Manager 侧一致)。106 的副本 worker 永远不加载(worker 不跑 RelayServer),铺它是为了不破坏"29/29 hash 全同"这条既有对账口径。 临时产物(⛔ 未删,留给复核):_中间产物_待清理/seq5/capacity.conf(下发源)、_中间产物_待清理/参数表_覆盖网络_调紧反证.md(E4 反证用)。


8.4 E1–E9 逐条(命令原文 + 原文输出 + 绿/红)

# 命令(原文) 输出(原文) 结果
E1 grep -c "^|" 参数表_覆盖网络_20260917.md;grep -cE "^|.*| \*\*待测\*\* |" 参数表_覆盖网络_20260917.md 155;4 ✅(155 行;待测 数据行 4 = §7 汇总的 4,自洽。⚠️ 全文 **待测** 字面命中 6 次 = 4 数据行 + §0 图例行 + §7 说明行,⛔ 不是 6 项待测)
E2 按表内公式独立手算(RELAY_MAX_HOSTS + HALF_OPEN_MS) floor(1002/2)=501;floor(262144/4)=65536;min=501;floor(501×0.45)=floor(225.45)=225 ✅;max(3000, 15×1000×2.5)=37500 ✅ ✅ 手算值 = 表内值
E3 ssh -p 22 bt-server 'curl -s 127.0.0.1:20080/status' "capacity": {"max": 225,"used": 2,"free": 223} ✅ max=225 > used×4=8;free = max-used
E4 node scripts/overlay-probe.cjs; echo $? ⇒ 再 --table <调紧副本> 首次 12 行全 PASS / exit=0;调紧后 FAIL OBS-02 capacity max=225 … (阈值 max=9999…) + stderr ❌ 1 项红:OBS-02 / exit=1 ✅ 非恒绿,且指名红项
E5 /status 取 counters.dial;test/relay.test.mjs T20 三分支 47 实测三个字段已存在:"dial": 0, "dialDenied": 0, "dialFailed": 0;T20 先红后绿(加计数前 counters.dial = undefined ⇒ AssertionError;加完 20/20 通过) ⚠️ 部分绿(47 上未能受控制造真实拨号,见 8.7-①)
E6 grep -nE "[0-9]{3,}" scripts/overlay-probe.cjs 零命中(命中数 0) ✅ 比判据更严:脚本里连标识符名与注释里都没有像阈值的数字
E7 通读 §5-S5 产出(本单 → 参数表 §8) 7 条(① 虚拟网卡驱动 ② 骨干开端口 ③ nft 打洞 ④ 第二中继机 ⑤ 443 兜底入口 ⑥ 拨号白名单 ⑦ 每机密钥与信任根);结论列只出现「维持」(全部 7 条都是"不扩大",其中 5 条还标了"已收窄"证据);④ 明写「只评估不实施 + 归序⑥」 ✅
E8 S0 对照 + 双实例面 + 门户 + npm test listen=79 / nft=72 逐字一致;online[] = manager + w-106 两台在;实例面 20000→401、w-106:40147→401;门户 200;npm test = tests 138 / pass 137 / fail 0 / cancelled 0 / skipped 1 / todo 0 ✅(skipped 1 为既有条件跳过,非本次引入;计数器增量逐条归因见 8.6)
E9 ss -lntp | wc -l;nft list ruleset | wc -l;ss -lntp | grep 20080 79 / 72;LISTEN 0 511 127.0.0.1:20080 只有回环一条 ✅ 零新增暴露面

8.5 观测(探针输出原文 + 退出码 + 调紧反证)

PASS OBS-01 在册节点 used=2 (阈值 ≥ 2)
PASS OBS-02 capacity max=225 used=2 free=223 (阈值 max=225, free=max-used)
PASS OBS-03 identityRequired=true trustedSigners=1 (阈值 ≥ 1)
PASS OBS-04 identityOk=2 (阈值 ≥ 2)
PASS OBS-05 revokedHosts=0 (阈值 ≤ 0)
PASS OBS-06 判别器 dial=0 dialDenied=0 dialFailed=0 (必须都是 number)
PASS OBS-07 authFailed=0 authed=2 (阈值 ≤ 50)
PASS OBS-08 端点表 2 条 / 离线 0 条
PASS OBS-09 实例面 本机:20000=401 w-106:40147=401 (阈值 ∈ {200,401})
PASS OBS-10 门户=200 (阈值 = 200)
PASS OBS-11 监听口=79(阈值 79) nft=72(阈值 72) relay 口绑定回环=1/1 条
PASS OBS-12 relay RSS=72040KB (阈值 ≤ 800000KB)
exit=0

调紧反证:把 RELAY_MAX_HOSTS 改成 9999(副本表,⛔ 未改正式表)⇒ FAIL OBS-02,stderr ❌ 1 项红:OBS-02,exit=1。


8.6 不退化(S0 对照 / 双实例面 / 门户 / npm test + 计数器增量逐条归因)

项 S0(09:20) 收口(09:5x) 归因
capacity {max:0, used:2} {max:225, used:2, free:223} 我:S4 设值(可归因、预期内)
counters.authed / authFailed 2 / 3 2 / 0 我:restart dshs-relay 清零累计值;重启后 manager + w-106 各认证一次 ⇒ authed=2;authFailed=0(⛔ 本次没有打过任何错误密钥;旧值 3 是历史累计)
counters.dial/dialDenied/dialFailed 字段不存在 0 / 0 / 0 我:S3 新增字段 + 部署。⚠️ 全 0 是真实读数(生产此刻确实没有拨号流量,见 8.7-①)
identityOk / identityRequired / trustedSigners / revokedHosts 2 / true / 1 / 0 2 / true / 1 / 0 零变化 ⇒ 序③ 成果未退化
online[] manager + w-106 manager + w-106(sessionId 已换) 我:重启重建会话(R5/T8 的既定行为,无需人工介入)
endpoints[].localPort 44911 / 44133 42563 / 40147 我:重启后重分配(回环落点口号本来就是会话期量,⛔ 不是退化)
监听口数 / nft 行数 79 / 72 79 / 72 逐字一致
门户 / 双实例面 200 / 401 200 / 401 未退化
npm test — 138 / 137 pass / 0 fail / 1 skipped 未退化(T20 已计入)

⚠️ 按 §5 S6 的计数器读数纪律:authed 的"逐字一致"物理上不成立(重启即清零)—— 上表已逐条点名到"哪一次动作"。


8.7 未过项 / 遗留(逐条:卡在哪 + 已做到哪一步 + 什么条件一出现必须回头解决)

① E5 的"47 上受控制造一次真实拨号"—— 未做到

  • 卡在哪:DIAL 只在拨号方白名单里的会话发出时才会被处理(server.ts:747 / :975),而白名单只有 manager 一个(dialers.conf = DSHS_RELAY_MAX_HOSTS… 原文 ops:manager)。要让 47 上产生真实 DIAL,唯一路径是一条通过鉴权的业务请求去访问 w-106 的实例面;而实测无 session 的 curl -H "Host: …" 127.0.0.1:3080/ 在鉴权层就被挡(w-106.alotbuy.com ⇒ 404,连实例路由都没进),35 s 观察 dial 恒 0、relay 日志 DIAL 0 条(库里有 3 个 w-106 实例、1 个 w-47 实例 ⇒ 一旦有人真实访问必然触发)。
  • 已做到哪一步:① 三个字段已在 47 的 /status 可见(0/0/0);② 三分支的可得性在 test/relay.test.mjs T20 上先红后绿全证(放行 dial+1 / 白名单拒绝 dialDenied+1 / 目标不可达与请求非法 dialFailed+2),而 T20 跑的就是部署到 47 的同一份构建产物(md5 623374d948e37c87db3401f00ebf4dbe,三处一致)。
  • ⛔ 为什么没硬做:为一个计数器去 ① 往控制面库插临时 session(= 冒充真实用户去访问正在运行的用户实例,可能触发 launch / 文件面刷新)或 ② 把第二个 hostId 加进 dialers 白名单(= 扩大权限面,命中 R5 必须停下报告)—— 两者都越出本单 §3.1 的范围,且风险大于这条判据本身的价值。
  • 什么条件一出现必须回头解决:下一次有真实用户访问 w-106 实例时,回看 /status.counters.dial 应从 0 变正数;若仍为 0,说明拨号路径根本没被走到(正是 RemoteSpawner.translateEndpoint 那类静默失效的指纹),届时按判别器口径立即排查。

② P7 的"跨云稳态带宽"—— 仍为 待测

  • 卡在哪:relay 的两个面(agent 19000 / 实例 21000)在无凭据时只回 24 B / 68 B 的 401/404;/client.js、/assets/ 均 404 ⇒ 拿不到大响应就测不出稳态吞吐。
  • 已做到哪一步:拿到 RTT 336 ms、TTFB 0.676–1.083 s、上行下界 ≥ 192 KB/s(1 MB 样本被 405 截断到 131072 B / 0.685 s)⇒ 旧记录 22 KB/s 判为不可用(差 ≈ 9×,⛔ 已写进参数表 §0 作废)。
  • 什么条件一出现必须回头解决:两端有可控载荷(序⑥ 的真机批次天然满足)⇒ 换出稳态值并重算 §5.2。

③ 三项"顺手发现、⛔ 未动手"(R7:先报告后动手)

  • ~/.ssh/config 的 bt-server 端口陈旧(32022)—— 本次全程 -p 22,未改配置。
  • src/net/relay/ 整目录未被 git 跟踪(?? src/net/relay/)⇒ 覆盖网络线代码只在工作区+部署产物里;未做任何 git 动作(⛔ 未 commit / 未 push)。
  • 附A 那句「/status counters 只有 authed/authFailed」已勘误(见 8.2-①);--max-hosts "未出现"已勘误(见 8.2-P3)。两条都属"上一棒文档的既有结论",⛔ 本轮只记录、只纠正,未扩大改动。

④ 三条硬约束守持证据

  • ① --max-hosts:225 > used×4 = 8 ✅(不满足才"不设值",本次满足)⇒ 已设值,且写明分母口径 = 每台中继。
  • ② 参数表 待测 项:4 项值列全部留空(grep -n 逐行可见 | | 空值),⛔ 一个都没编数。
  • ③ ⛔ 全程未执行 git checkout --(grep -c 校验:本次会话的命令记录里零次)。

8.8 45% 口径(已填值 + 生效证据)

  • 数值:RELAY_MAX_HOSTS = 225(floor(min(floor(1002/2), floor(262144/4)) × 0.45) = floor(501 × 0.45) = 225)。
  • 分母口径:每台中继的在册会话数上限(⛔ 不是"全网 45%");1000 × 45% = 450 只作校验。
  • 生效证据:capacity {max:225, used:2, free:223} ✅;systemctl show -p ExecStart dshs-relay 原文含 --max-hosts 225 ✅。
  • 校验③ 的结论(新信息):450 > 225 ⇒ 单台中继盖不住千台的 L3 层,需 ≥ 2 台中继 —— 与 覆盖网络_骨干层方案_20260916.md「多中心骨干」同向;落地归序⑥(交接单 §4.1-6 已判 L3 只评估不实施)。
  • 回头条件(写死):MEM_PER_HOST_MB 或 WAN_STEADY_THROUGHPUT 一旦换成实测 ⇒ 必须重算本值并重新下发 capacity.conf。
  • 哨兵:OBS-12 relay RSS ≤ 800000KB(每 host 2 MB 假设的越线报警)。
  • §8.7③ 要求的回答已写进参数表 §5.6:relays[] 顺序语义 = 主入口首位,本单不引入优先级新语义。

8.9 指纹

  • 回填前(规划棒交付,⛔ 本次未改动规划棒正文 §1–§7 与附A/附B 的既有结论,只追加了 §8 与两处勘误说明)
  • 本节口径(推荐核对用,可复现):整个 §8.9 不计入 ⇒ 复核命令 sed '/^## 8\.9 指纹$/,$d' 交接单_参数表与观测_20260917.md | md5sum ⇒ ea5cf5158129467a03608410a10be2e7
  • 全文件 md5:请用 md5sum 交接单_参数表与观测_20260917.md 现取 —— ⛔ 此处故意不内嵌数值:它包含本节自身,写进去即刻失效(自指)。

⚠️ 一次非预期改写的披露(已完全回滚,逐字复核通过):回填过程中一条 shell 命令里的反引号被当成命令替换,导致本文件被插入 25,761 段垃圾串(每字符之间一段)。当场发现并回滚(replace 逆操作 ⇒ 垃圾串计数 0),随后 wc -l / 章节标题 / 抽样 3 行逐条复核与预期一致,正文无残留。⇒ 记录在此,供下一棒对账时知情。