交接单按归属约定(正文落文档库、工作区只放指针)归档至 归档/交接单-20260924-归档/,含逐件判定 README: - 16 件已被文档库正式版取代(T09–T21 + 覆盖网络-24/25/26) - 4 件主题已被覆盖网络线入口汇总 - 7 件历史接续包/规划件 CODEBUDDY §9:收口清本棒 tmp、tmp 保留期 7 天、禁「待清理」中间态、 工作区入库只放文档与文件、不保留脚本副本、>60 KB 单文件须逐个判。
44 KiB
交接单 · 节点一键加入与分组准入(覆盖网络线 · 2026-09-18 规划棒)
归档号:131(现核 04 目录最大 = 128;129 / 130 已被单 A / 单 B 两张交接单占用 ⇒ ⛔ 不补占、不重用) 目标一句话:把覆盖网络从「两台机器手工配好的专线」变成「产品」—— 任何人开一台节点服务器就能接入;节点按网分组、只允许指定节点加入;既能共建一张大网,也能各自独立组网。 来源:用户 2026-09-18 09:07 原话 ——「b 要实现开启一台结点服务器,就能连上覆盖网络,这样做这个网络才有价值,结点可以设置分组只允许那些结点加入,既可以成为大覆盖网络也可以建立独立覆盖网络」。 性质:规划棒产物 —— 本单只做方案与判据,⛔ 零代码改动、⛔ 零服务器改动(规划与执行分离)。
§1 用户口径(功能卡,⛔ 别改写成技术题)
| 问 | 答(用户原话/直接推论) |
|---|---|
| 谁用 | 开节点服务器的人(平台运维 / 被授权的用户) |
| 在哪用 | 一台新机器上;以及节点自己的配置处 |
| 要做什么 | ① 开一台节点服务器 ⇒ 就能连上覆盖网络 ② 节点可设分组,只允许指定节点加入 ③ 既能成大覆盖网络,也能建独立覆盖网络 |
| 怎样算成功 | ① 新机器一条命令接入(⛔ 不是照文档改四处配置)② 名单外的节点连不上(结构性拒绝,不是"设了 ACL 但忘了")③ 两张网互相看不见 ④ 大网 = 多组共用一个骨干;独立网 = 不共享任何骨干 |
用户已拍板的选项:2026-09-18 08:2x 问「要不要补、补哪个」⇒ 答复 B(连"节点直连"一起补),⚠️ 且用户明示 B 的缺点他已看到(新增直连暴露面 = 命中 R5)。
§2 只读前置(开工前必读,⛔ 别重做调研)
| # | 文件 | 为什么读它 |
|---|---|---|
| 1 | src/net/relay/network.ts |
网抽象唯一入口:ops / u:<userId> / 其它显式名 三条分支;裸 u 是保留字 |
| 2 | src/net/relay/server.ts(头部注释 + dialers 段) |
⑥ 网维度:会话与端点表按 <network>/<hostId> 索引;DIAL 必须同网 + 本网白名单 |
| 3 | src/net/relay/directory.ts |
引导三级链(内置种子 → 签名目录 → 离线降级);relays[] 由 47 的 127.0.0.1:3080 签发 |
| 4 | src/net/relay/content/peer.ts |
ContentPeerGroup / groupKeyOf / sameGroup(组维度现状) |
| 5 | src/web/server.ts §内容面装配段 |
⚠️ 现状:只接通 local;peer 诚实回"没有";edge/region/origin 未注入 |
| 6 | 参数表_覆盖网络_20260917.md |
唯一参数表(观测阈值 / 待测项 / 权限影响评估) |
🔬 本棒已取证的三条硬事实(⛔ 别当"待确认"): ① 「分组准入」与「独立网络」的底层机制已在代码里 ——
dialers是Map<networkId, Set<hostId>>,没列到的网络一个都拨不动(默认拒绝),且跨网DIAL在"能不能拨"这一步就走不到(结构性隔离,不是策略性)。 ② 「一键加入」不存在 —— 加入一台机器现在要手工做四件事(装单元 / 配密钥 / 写DSHS_RELAY_DIALERSdrop-in / DB 登记),全仓无 join 类入口。 ③ 「直连」与「P2P 取回」都不存在 ——grep -i "punch|打洞|directPath"只命中addr-override.ts(443 兜底钉 IP,不是打洞);内容面peer档在生产路径上恒返回 undefined。
§3 范围与在册文件集
3.1 分期(分期即权限分期,⛔ 不许颠倒)
| 期 | 内容 | 是否命中 R5 | 为什么这么排 |
|---|---|---|---|
| P1 | 一键加入 + 分组准入门面(把已有的白名单机制产品化) | ⛔ 不命中(零新增暴露面) | 用户要的核心价值("开一台就能连上")全在这一期,且能立刻开工 |
| P2 | 节点直连(打洞) + P2P 取回通道接线 | 🔴 命中 R5(新增直连监听/探测面) | 用户选 B 已含此项,但按 R5 须先出评估(见 §7);且它技术上依赖 P1 的"节点身份与准入" |
3.2 在册文件集(P1;⛔ 超出即停下报告)
- 新增:
src/net/relay/join.ts(加入流程编排:凭据校验 → 落地配置 → 注册) - 新增:
src/net/relay/registry.ts(准入凭据的签发/校验,载荷不含密钥本体) - 新增:
scripts/overlay-node-join.cjs(节点侧命令行入口) - 新增:
scripts/overlay-node-admit.cjs(控制面侧:签发准入凭据 / 写白名单) - 修改:
src/net/relay/network.ts(只加networkId的形状校验与"网络注册表"读取,⛔ 不改既有三条分支语义) - 修改:
src/web/routes/新增overlay-nodes.ts(管理面 API:列出/批准/移除节点) - 修改:
scripts/overlay-probe.cjs(新增OBS-24/OBS-25) - 修改:
参数表_覆盖网络_20260917.md(新增键与判据行) - 测试:
test/overlay-join.test.mjs(新增)
⛔ P1 明确不碰:server.ts 的 DIAL/白名单语义(只读取)、wire.ts 帧格式、keys.ts 既有语义、bwrap 参数、nginx/nft。
§4 方案(技术路线)
4.1 缺口 1 · 一键加入
现状(加入一台机器的全部动作,全手工):
① 装 relay / worker 单元 → ② 配节点密钥 → ③ drop-in 写 DSHS_RELAY_DIALERS="<network>:<hostId>" → ④ DB 登记 dsh_hosts
目标形态(一条命令):
dshs-node join --network <networkId> --invite <邀请凭据>
内部四步(全自动):① 校验邀请凭据(签名 + 网络 + 有效期 + 一次性)→ ② 生成本机节点密钥(私钥不出机)→ ③ 落地本机配置 → ④ 向控制面注册并等待被列入白名单。
关键设计(已定项,可推翻):
- 控制面仍是权威单点(用户既有口径「归属/租约/骨干资格只能控制面写」)⇒ 加入的批准权在控制面,节点侧只是"申请 + 自动落地"。
- 邀请凭据 = 控制面签的
(network, 有效期, 一次性 nonce),载荷不含任何密钥本体(照抄组密钥凭据的既有形态)。 - 白名单由控制面派生(不再手写 drop-in):控制面把"已批准节点集合"投影成 relay 的白名单配置并 reload ⇒ 手写 drop-in 退化为应急通道(保留,⛔ 不删)。
4.2 缺口 2 · 分组准入门面
现状:机制完整(按网分桶 + 默认拒绝),但配置靠手写字符串(如 u:5:manager),写错了长得像"这张网不存在"。
目标:把它变成可管理的东西 —— ① 列出现有网与节点 ② 批准/移除节点 ③ 给节点的分组可改 ④ 独立网 = 新增一个 networkId,零代码(isNetworkId 已支持显式命名)。
判据(⛔ 不许做成"策略性 ACL"):跨网拒绝必须仍然是结构性的 —— 拒绝发生在"能不能拨"这一步,而不是"拨通了再查表"。
4.3 缺口 3 · 直连与 P2P(P2,命中 R5)
现状:⛔ 无打洞;内容面 peer 档有账本无通道。
目标:① 节点间交换候选地址 → 尝试直连 → 失败判死降级中继(⚠️ 照抄成熟做法,不发明)② 把 peer 档接上真实取回通道(这是"文件走 P2P 分发"成立的唯一前提)。
红线:⛔ 直连不得让任何节点反向进入别人的机器;直连只在同网 + 双方都在白名单时建立;⛔ 打洞不得绕过既有的身份强制与吊销。
§5 判据(先红后绿;⛔ 不许事后补写)
| # | 判据 | 形状 | 先红腿(对旧产物) |
|---|---|---|---|
| J1 | 邀请凭据一次性 | 同一凭据第二次使用 ⇒ 拒绝,原因码具名 | 旧产物无 join ⇒ 入口即缺 ⇒ 红 |
| J2 | 白名单外的节点拨不动 | 未批准 hostId 拨号 ⇒ 拒绝(非超时、非静默) |
机制已存在 ⇒ 本腿复验不新建 |
| J3 | 跨网结构性拒绝 | 跨网 DIAL 在"能不能拨"处被拒;两端观测面零交集 |
同上 |
| J4 | 一条命令接入 | 新机器 join 后两端观测面同时出现该节点,且 online=true |
旧产物 ⇒ 红(无入口) |
| J5 | 独立网零共享 | 新 networkId 与 ops 网:relay 侧无共同 dialer 条目、无共同会话 |
旧产物 ⇒ 用 OBS-24 断言 |
| J6 | 私钥不出机 | 节点侧私钥文件权限 0600;凭据/请求载荷逐字不含密钥本体 |
照抄组密钥既有腿 |
零回归三件套(每期收口必跑,三者都带 --table):npm.cmd test(Node 22)|node scripts/overlay-failover-drill.cjs --scene all --table|node scripts/overlay-probe.cjs --table(开工基线 = 23 PASS / 0 SKIP / 0 FAIL)。
§6 步骤(每步自带验收 + 回滚)
P1(⛔ 不命中 R5,可立即开工)
- S1 · 网注册表与节点清单(只读面先行):控制面能列出「有哪些网、每张网有哪些节点、谁已批准」;探针可断言。⇒ 验收 = 探针新判据绿 + 零回归。
- S2 · 准入凭据(签发/校验):控制面签发 + 节点侧校验;一次性 + 有效期 + 网络绑定。⇒ 验收 =
J1先红后绿。 - S3 · join 编排:一条命令完成四步;失败必须具名(⛔ 不许静默拒绝)。⇒ 验收 =
J4。 - S4 · 白名单派生:控制面把已批准集合投影成 relay 白名单并 reload;手写 drop-in 退化为应急通道。⇒ 验收 =
J2/J5。
P2(🔴 命中 R5,S5 前必须先出权限影响评估并经用户点头)
- S5 · 直连候选交换 + 打洞探测:同网 + 双向白名单前置;失败判死降级中继。
- S6 ·
peer档接线:把真实取回通道接上(⚠️ 接线后必须复算块 id,否则命中本线老病根"假绿")。 - S7 · 规模回归:候选数、切换时间、回源字节三项复测(本线已有基线读数可比)。
§7 权限影响评估(R5 · P2 专用;⛔ P1 不适用)
- 扩了什么:新增节点间直连探测(新增监听/出站探测行为)。⚠️ 具体端口/协议在 S5 设计时定,届时逐条列出,⛔ 不在此处含糊带过。
- 谁受影响:仅同网且双向在白名单的节点之间;⛔ 不对公网开放、⛔ 不跨网、⛔ 不影响租户实例的既有隔离面。
- 有没有不扩大也能实现的方案:有 —— 就是本期不做直连(跨机流量继续走中继)。⇒ 直连的唯一收益 = 省中继带宽 + 让 P2P 成立;⛔ 若用户不接受新增暴露面,P1 已交付的"一键加入 + 分组准入"仍然完整可用。
- 回滚方式:删直连配置 → reload → 行为回落"全部走中继"(秒级);验收 = 探针直连判据归位 + 零回归三件套。
⚠️ 用户已知悉:2026-09-18 08:2x 选 B 时,B 的缺点里明确写了"需要新开直连端口 = 扩大暴露面、命中 R5、必须单独点头"。⇒ 本单执行到 S5 之前会再停一次(给当时的具体端口/协议清单),这是 R5 的硬要求,⛔ 不是重复提问。
§8 回报格式(执行棒回填;⛔ 只填空,不改小节点名)
8.1 开工门(必填)
- 本单 §8 前前缀 =
085e28b56a08aa1f01999bdb3e238911(2026-09-18 09:2x 现算;与 §11.2 逐字一致 ✅) - 抢锁结果 = 抢到(
--claim-exec "执行棒P1-节点一键加入与分组准入-0918",锁空闲)|开工时刻 = 2026-09-18 09:2x|执行棒名 = 执行棒P1-节点一键加入与分组准入-0918 - 开工基线现核:
git rev-parse --short HEAD=09ce76f(未提交改动 = 上棒遗留的_tmp_seq24/、_中间产物_待清理/)|npm.cmd test= 201/200/0/1|探针 = 23 PASS / 0 SKIP / 0 FAIL
8.2 逐判据回报(J1–J6 各一条,含原始读数)
- J1 邀请凭据一次性 —— 先红腿:
lib里把consumeNonce的flag:'wx'改成'w'⇒ 单测 B1/B2 双红,原文⛔ 竞态未守住:ok=12(须恰好 1)(12 进程真并发)⇒ 后绿腿:还原后B1/B2绿,且 33/33 全绿、libmd5 逐字一致951a2526789b897aa58e3abbb5dea332。真机腿:chain47.sh步(6) 原文✗ apply 失败:invite-already-used — nonce d4019b3c… 已被用掉(一次性凭据,⛔ 不重复受理)(rc=1)⇒ 判定:绿。 - J2 白名单外的节点拨不动 —— 本腿复验不新建(机制已存在):
OBS-06判别器dial=0 dialDenied=0 dialFailed=0(都是 number)+OBS-11relay 口绑定回环=1/1 条。⚠️ "新节点真机拨不动 / 批准后拨得动"这一条未在生产 relay 上执行(见 §8.4、§8.6)。 - J3 跨网结构性拒绝 —— 复验:
derive产物按网分桶(network.ts#normalizeDialers同一入口),三网原文[lab-net] DSHS_RELAY_DIALERS=lab-net/lab-1|[ops] …=ops/lab-1|[u:5] …=u:5/lab-1⇒ 零交集;OBS-25读得自审 auditOk=true 跨网共享=0。⇒ 判定:绿(结构性,非策略性 ACL)。 - J4 一条命令接入 —— 先红腿 = 旧产物无
join入口 ⇒ 红(具名missing required --file);后绿腿 =chain47.sh步(4) 原文✓ join 成功:lab-net/lab-1|公钥 e18630527a6f7b5f…|四步 4/4,四步verify-invite / node-key / local-config / register全✓。⚠️ "两端观测面同时出现 + online=true"未在生产 relay 上执行(同上)。 - J5 独立网零共享 —— 真机步(9)原文:三张网各得自己那条、⛔ 无共同条目;步(10) 注册表全貌 =
ops(approved 1/pending 0) · lab-net(…1/0) · u:5(…1/0),三条记录的键分别是lab-net/lab-1/ops/lab-1/u:5/lab-1。OBS-24五项:形状合法 ✅|派生自审一致 ✅|pending 未进派生 ✅|结构性错桶 0 ✅|派生桶数 0 ≡ 有 approved 的网数 0 ✅(生产注册表为空 ⇒ 本项在真机读的是"空表也自洽")。⇒ 判定:绿。 - J6 私钥不出机 —— 真机原文:
FLAG: 申请单里私钥出现次数(须 0)= 0|FLAG: 节点私钥权限(须 600)= 600|邀请doc键 =['expiresAt','issuedAt','network','nonce','version'](⛔ 无任何密钥)|selfcheckapplication.bytes=533 / hasPrivateKey=0 / shapeOk=true / hostMatches=true|nodeKeyModeOk=true(Linux 权限腿成立)。⇒ 判定:绿。
8.3 零回归三件套(三者都带 --table)
npm.cmd test201/200/0/1(rc=0;Node 22.22.2,与开工基线逐字一致)|--scene all12P/0S/0F(rc=0;幕4-C 实测 19370ms / deadline 30000ms)|探针 24P/1S/0F(rc=0)- ⚠️ 探针与开工基线(23P/0S/0F)的差 = 本棒新增
OBS-24/OBS-25两条 +2P,但OBS-09由「在册」退化为「不在册 ⇒ SKIP」−1P/+1S。FAIL 数同为 0。归因与未复原说明 ⇒ 参数表 §11.4-②。
8.4 恢复形态核实
- 派生侧(真机
/tmp表外链路):三张网各 1 条、零共享;批准前派生为空(Environment="DSHS_RELAY_DIALERS=")⇒ 「收单 ≠ 批准」成立。 - 🔴 relay 侧:未执行——⛔ 未把派生产物落到生产
50-overlay-dialers.conf、⛔ 未 reload relay(P1 硬门 D1「不改任何生产值」)⇒ "新节点加入后两端观测面同时出现 / 端点表新增条目 /used变化" = 未验证(⛔ 不编数)。 - 参考读数(本次运行演练后的现网状态,⚠️ 与本棒改动无关):
capacity {max:7515, used:2, free:7513, utilPct:0}|online = [manager(ports= 空), w-106(ports=19000)]|endpoints1 条(w-106 port=19000 localPort=43061 online=true)。
8.5 边界自证(逐条打勾)
- ⛔ 未 commit / push(R7):
git rev-parse --short HEAD=09ce76f(与开工一致)|git status --short= 3 个M(overlay-probe.cjs/index.ts/network.ts)+ 5 个??(overlay-node-admit.cjs/overlay-node-join.cjs/join.ts/registry.ts/test/overlay-join.test.mjs)—— 全部在册,⛔ 无越界文件 ✅ - ⛔ 未改任何生产值(D1):⛔ 无 drop-in 改动、⛔ 无 env 改动、⛔ 未 reload 任何单元配置。⚠️ 如实补记:本棒新增了状态目录
/var/lib/dshs/overlay/(nodes.json0644 +consumed/+selfcheck.json)—— 它是 §3.10 登记的状态(非配置);⛔ 未覆盖任何既有文件 ✅ - ⛔ 未新增公网监听口、未动 nft·nginx(R5):
OBS-11PASS 原文nft accept 0 多出 0|relay 口绑定回环=1/1 条✅ - 🔴
RELAY_FAILOVER_COOLDOWN_MS=0计数 = 0(grep -c在overlay-node-admit.cjs/overlay-node-join.cjs/overlay-probe.cjs/test/overlay-join.test.mjs/registry.ts/join.ts/network.ts七个文件上全部 = 0)✅ - ⛔ 未改 bwrap 参数(
grep -c "bwrap\|perms"在registry.ts/join.ts= 0 / 0)✅ - 🔴 密钥本体不经网络 / 不经 relay:申请单内私钥出现次数 0|邀请
doc无密钥键|OBS-25私钥泄露=0 ✅
8.6 如实留档(必填,哪怕"没有")
- 未做的项:
src/web/routes/overlay-nodes.ts(管理面 API)未建 ⇒ 本棒自决移入 P2(理由:与 P1 硬门「零新增暴露面」冲突风险最高;S1–S4/J1–J6验收均不依赖它;交接单 §7 本身即 P2 专用)。⚠️ 可推翻。 - 未测的项:① 「派生落盘 ⇒ relay reload ⇒ 新节点真能拨通」这条生产闭环(因 D1 硬门,见 §8.4);② 另见本单 §10 未验证项(4 条,本棒未新增未验证项)。
- 过程中发现的额外问题(⛔ 只报告、未顺手改):
- 🔴 控制面 CLI
--file键名混用(真机缺陷,已修 + 已补 E10 先红后绿):apply --file <申请单>曾把注册表文件也指到申请单 ⇒ 收单炸在第 4 步而 nonce 已被占位(对外 =「收单失败 + 同一张邀请再也用不了」)。修 = 覆盖键改名--registry。教训:调库式判据(E9)抓不到参数解析类缺陷 ⇒ 必须有CLI 级真回环(E10)。 - ⚠️
OBS-24原拟判据没有判别力("同一hostId出现在 ≥2 张网 ⇒ FAIL"实为合法态)⇒ 已降级为信息项,改判「结构性错桶=0」+「派生桶数 ≡ 有 approved 的网数」。 - 🔴 本次零回归演练的副作用(本棒唯一未复原项):
OBS-09由 PASS 变 SKIP。归因有原文:演练后OBS-01/04/08转红 ⇒ 执行恢复(47restart dshs+ 106restart dshs-worker)⇒ 三者回绿,但 106 worker 重注册读数由accepted=[19000,21000]变为accepted=[19000](实例端口 21000 的注册未随进程重建)。✅ 两端实例:20000/:21000都在监听、门户 200,无业务中断。回头条件 = 下一棒收口若仍 SKIP 或出现「跨机实例面访问不通」⇒ 升级独立小项(先查"实例 → 本地 worker 端口注册"链路)。详 ⇒ 参数表 §11.4-②。
- 🔴 控制面 CLI
8.7 指纹
- 参数表 §10 指纹(现算并同步)=
695dcb4d88a778840322bc221417d339(口径:sed '/^## §10 指纹/,$d' 参数表_覆盖网络_20260917.md | md5sum)|本单全文 md5(写入本行前现算)=2344d947499745a5c1e5a5cb56d0b274/236 行(⚠️ 写入本行后即失效 ⇒ 引用前现算);§8 前前缀 回填后仍 =085e28b56a08aa1f01999bdb3e238911(不变量,已复算 ✅)
8.8 收口复核(序㊱ 收口复核棒 · 2026-09-18 10:0x–10:4x;⛔ 只复核,⛔ 未开工 P2)
- 开工门复核:本单 §8 前前缀 =
085e28b56a08aa1f01999bdb3e238911(现算,与 §8.1/§11.2 逐字一致 ✅)|抢锁 = 抢到(--claim-exec "收口复核棒-序36-P1-OBS09",锁空闲)|现核基线 = HEAD09ce76f、git status --short= 10 处(3M+ 7??,与 §8.5 逐数相同) - ①
OBS-09复核 + 复原(逐段原文)- 开工读数(10:1x):
SKIP OBS-09 在册实例面 无 (阈值 ∈ {200,401})|不在册 2 项:本机:20000,对端:21000|/status端点表 1 条 =w-106 port=19000 localPort=43061 online=true⇒ 与 §8.6-3 / 参数表 §11.4-② 记录一致。 - 链路取证(「实例 → 本地 worker 的端口注册」):端口集 =
staticPorts + forwarded(worker/agent.ts:323)|106/healthz原文 ={"hostId":"w-106","instances":0,"tunnel":{"ready":true,"ports":[19000]}}⇒ worker 自己认不到实例(instances=0)⇒reconcileTunnel()(agent.ts:257-263,20 s 一跳)拿不到live端口 ⇒ 永不forward(21000)。根因链(原文级):orchestrator.ts:558 listUserInstances() = [...this.mains.values()],而rehydrateAdoptedScopes()认领时只写this.adopted(:1492)⇒ ⛔ 不进mains⇒ 09:56:28 重启后实例 scope 被认领([rehydrate] adopted … port=21000+probe OK :21000)但平台侧不可见 ⇒ 端口不再声明。⇒ 这是候选 B 已知边界的直接后果("不被杀 + 访问时自然替换",⛔ 非"重启后直接可用"),⛔ 不是 relay 侧故障(relay 全程正常,OBS-12RSS 70 MB)。 - 复原动作(设计内路径 · 零代码 / 零配置改动):临时 session(
/opt/dshs/mksess.cjs guest,R4)⇒ 一次真实用户可见面访问(curl -H 'Host: guest.alotbuy.com' -b sid=…)⇒ 10:18:34[dsh-child main] Running as unit: dsh-100002-7d1c8cbf.scope(旧 scopedsh-100002-2e9202dd被cleanStaleScopes收掉)⇒ 新实例走正常 launch 路径声明端口:/healthz={"instances":1,"tunnel":{"ready":true,"ports":[19000,21001]}}|/status端点表 ={19000 ✓, 21001 ✓}。用户可见面 = http 200 / 62451 B /502标记 0(与 §8.12-4 同口径)|临时 session 已删(DELETE 1⇒ 余 0)。 - 🔴 复原后第一次复跑仍 SKIP 的真因 = 判据输入过期(假 SKIP):替换落点漂到 21001(
spawn.ts#findFreePortInRange从DSHS_INSTANCE_PORT_BASE上扫第一个空闲口;替换发起时 21000 仍被旧 scope 占着 ⇒ 顺延),而OBS-09的"在册"按表内固定端口取 ⇒ 对不上。 - 处置(事实优先,⛔ 不为了迁就旧值去重启生产实例):参数表
PEER_INSTANCE_PORT实测替换 21000 → 21001(+PEER_AGENT_PORT出处括注同步ports=19000/21001);§10 指纹695dcb4d88a778840322bc221417d339→2ae954e2e07b47651a84e597c19a3444(现算并已同步写进 §10)。 - 复原读数:
PASS OBS-09 在册实例面 w-106:42717=401 (阈值 ∈ {200,401})|不在册 1 项:本机:20000⇒ 由 SKIP 回到 PASS ✅。⚠️本机:20000恒"不在册"是结构性的(47 的 worker 不是 relay 客户端,§11.1 已钉死)⇒ ⛔ 不是缺陷。
- 开工读数(10:1x):
- ② 产物可复现:
node --test test/overlay-join.test.mjs= 33 tests / 33 pass / 0 fail / 0 skipped(rc=0)|探针 0 FAIL ⇒ 两件都成立。 - ③ 零回归三件套:
npm.cmd test(Node 22.22.2)= 201 / 200 pass / 0 fail / 0 cancelled / 1 skipped(与基线逐字一致)|overlay-failover-drill.cjs --scene all --table= 12 PASS / 0 SKIP / 0 FAIL(幕4-C 实测 17375 ms / deadline 30000 ms)|overlay-probe.cjs --table= 25 PASS / 0 SKIP / 0 FAIL(rc=0)。 - ④ 🔴 演练副作用的同型复现与处置(如实记):
--scene all跑完后 47 中继读数 =ep=[] / used=1 / idOk=1—— 演练把 106 worker 的通道逼到了 106 自己的中继(106 中继sessions=["w-106"]原文可证)⇒ 若就此复跑,OBS-01/04/08/09会同型转红(与 §11.4-② 同因)。处置(⛔ 未重启任何 worker、⛔ 未换实例):停 106dshs-relay⇒ 逼它按候选链回落 47(实测 ~15 s 后used=2 / idOk=2 / ep={19000 ✓,21001 ✓})⇒ 再启 106dshs-relay(active,⚠️ 该单元在演练前是 inactive ⇒ 本棒把它留在了 active)。⚠️ 由此点名一条观测缺口:探针只读 47 的RELAY_STATUS_URL,而 worker 可能挂在 106 的中继 ⇒ 47 视角"端点表为空"⛔ 不等于"覆盖网络断了"(潜在假红)。 - ⑤ 边界自证:⛔ 未 commit / push(HEAD
09ce76f;git status --short= 10 处,与开工逐数相同)|⛔ 未改任何生产配置值(无 drop-in / env / nft·nginx / bwrap 改动)|🔴RELAY_FAILOVER_COOLDOWN_MS=0计数 = 0|🔴 密钥本体不经网络 / 不经 relay|⛔ 未开工 P2(P2 仍待用户拍板权限影响评估)。 - ⑥ 如实留档(必填):
- 🔴
OBS-09的"在册"判据钉死了两个会漂移的端口 ⇒ 每次「访问时自然替换」后都可能变成假 SKIP(本棒连踩两次:端点表[]→21001;同步表值只是把输入改对,⛔ 判据本身的脆弱性未治)。⇒ 已点名 = 下一棒(序㊲):把"在册实例面"改为从/status.endpoints[]派生该主机名下的在线实例端点(⛔ 本棒按"只做被明确要求的事"未改判据)。 - ⚠️ 本棒对生产做了 3 个动作(均属 R8 开发环境、动手前已声明):① guest 实例一次「访问时自然替换」(设计内路径,非人工杀实例)② 106
dshs-relay停 / 启各一次(逼 worker 回落 47,约 1 min 单中继运行)③ 参数表文档值同步。⛔ 无不可逆操作、⛔ 无用户数据改动。 - ⚠️
--scene all会改变通道拓扑且演练自身不复原(把 worker 逼到 106 中继)⇒ 每次跑完都要"回落 47"才算回到可观测基线;这一点建议写进演练脚本头部(⛔ 本棒未改脚本)。
- 🔴
8.9 收口复核(序㊲ 执行棒 · 2026-09-18 11:0x–11:2x;⛔ 只做 §8.8-⑥ 的两条点名,⛔ 未开工 P2)
- 开工门:本单 §8 前前缀 =
085e28b56a08aa1f01999bdb3e238911(现算,与 §8.1/§8.7/§8.8 逐字一致 ✅;⚠️ 口径 =sed '/^## §8 /,$d' <本单> | md5sum)|抢锁 = 抢到(--claim-exec "序㊲ 执行棒(观测面去常量依赖)",锁空闲)|现核基线 = HEAD09ce76f、git status --short= 10 处(与 §8.8 逐数相同) - ① §8.8-⑥-1 点名(
OBS-09的"在册"判据钉死会漂移的端口 ⇒ 假 SKIP)—— 已消除:判据改为从/status.endpoints[]派生「该主机名下、非 agent 端口的在线实例端点」,每一个都必须可达(码 ∈PROBE_CODE_SET),派生为空 ⇒ SKIP + 留痕。先红后绿(原文级 · 两侧逐行 diff 只差OBS-09一行):红 =SKIP OBS-09 在册实例面 无 (阈值 ∈ {200,401})|不在册 2 项(记 SKIP 的依据):本机:20000,对端:21000(夹具 = 表值回写成漂移前的 21000 + 真实 47/status(实例在 21001))|绿 =PASS OBS-09 在册实例面(派生 1 条):w-106:42717=401 (阈值 ∈ {200,401})。🔴 旧夹具口径(位置式)已退役并硬报错(rc=2);🔴 派生子缺码 ⇒ FAIL 并点名(拿旧端口的码糊弄不过去)。收益实测:复原后localPort由 42717 → 44231,探针无须改表值即回绿(⚠️ 这正是 §8.8-① "同步表值只治输入、不治判据"的对照)。 - ② §8.8-⑥-2 点名(探针只读 47 中继 ⇒
ep=[]时潜在假红**)—— 已消除(两种情形可分):新增判别源 = 对端中继的/status(ssh <SSH_TARGET_106> curl <RELAY_STATUS_URL>,只读回环;⛔ 零新增暴露面、零新增参数键;⚠️ 只在 47 视角为空时才去读 ⇒ 正常态零额外 ssh**)。同一份 47 空表输入 ⇒ 三种可分输出:(甲) 对端中继声明了端口 ⇒SKIP OBS-08 端点表 0 条 ⇒ SKIP + 留痕|… 对端中继(test106)声明了端口:w-106 ports=19000/21001 ⇒ 客户端挂在**另一台中继**上(⛔ 非故障、⛔ 不判红)(真机 ·--scene all演练态实测:47 =ep=[] / used=1 / idOk=1,106 =[w-106:19000, w-106:21001])|(乙) 两台中继都无该客户端会话 ⇒FAIL OBS-08 …(两台中继都无该客户端会话)|(丙) 对端中继读不回来 ⇒FAIL OBS-08 …(不可判)(⛔ 不许静默当绿)。⚠️ 旧口径在同态下OBS-08恒 FAIL(eps.length > 0)⇒ 分不出两者,这就是被点名的假红。 - ③ 零回归三件套(都在改动之后跑;⚠️
npm.cmd test无--table参数,另两件已带):npm.cmd test201/200/0/1(rc=0,逐字一致)|overlay-failover-drill.cjs --scene all --table= 12 PASS / 0 SKIP / 0 FAIL(rc=0;幕4-C 18353 ms / deadline 30000)|探针 = 25 PASS / 0 SKIP / 0 FAIL(rc=0)⇒ 与基线逐数一致(⚠️ 探针不在npm test覆盖内,其零回归即此行)。 - ④ 复原(按 §8.8-④ 同款;⛔ 未重启任何 worker、⛔ 未换实例):停 106
dshs-relay⇒ 约 15 s 后 47 视角回落used=2 / 端点 = [(19000, 39815), (21001, 44231)]⇒ 启回 106dshs-relay(active)⇒ 探针回绿。⚠️ 演练态下OBS-01/OBS-04仍红(used=1/identityOk=1< 各自阈值 2)—— 二者是计数判据、⛔ 与"端点表为空"无关,复原后即绿(详见「参数表_覆盖网络_20260917.md」§11.5-⑤-1)。 - ⑤ 边界自证:⛔ 未 commit / 未 push(HEAD
09ce76f;git status --short10 处与开工逐数相同;本棒只动scripts/overlay-probe.cjs一个仓内文件)|⛔ 未改任何生产配置值(参数表值格一律未动)|⛔ 未新增暴露面(OBS-11 多出 0 缺失 0、relay 口仍 1/1 回环)|🔴RELAY_FAILOVER_COOLDOWN_MS命中 0|⛔ 未开工 P2|🔴 密钥本体不经网络 / relay。 - ⑥ 如实留档:
参数表_覆盖网络_20260917.md§11.5 已新增小节(零回归三件套 + ①② 原红绿原文 + 可分三态 + 边界自证 + 5 条留档;🔴 §10 指纹2ae954e2e07b47651a84e597c19a3444→71848fad4ee0d6a574d833169e042fd1,现算并已同步)。⚠️ 本棒另发现一条文档口径歧义(探针文件头总括称"夹具模式…把远端原文喂进来 ⇒ 不 ssh",但OBS-24/OBS-25的分支无fixture守卫 ⇒ 未给各自夹具时会真去 ssh 取生产读数;⚠️ 这两项各自的用法行与代码一致,只有文件头那句总括与事实不符)⇒ ⛔ 按"只做被明确要求的事"未改、已在 §11.5-⑤-3 登记二选一建议。📁 证据 = 工作区_tmp_seq37/。
8.10 收口复核(序㊳ 执行棒 · 2026-09-18 11:3x–12:0x;⛔ 只做用户拍板的两项收口,⛔ 未开工 P2)
- ① 探针夹具封闭性(✅ 用户拍 A 方案):
OBS-24/OBS-25两处取数由if (<夹具> !== undefined) { 读夹具 } else { ssh }改为… else if (fixture) { SKIP + 留痕 } else { ssh }(守卫排在 ssh 之前);探针文件头新增「封闭性」硬约束段。先红后绿(原文级 · 两侧逐行 diff 只差这两行) —— 两侧同一份--table副本(SSH_TARGET_47/SSH_TARGET_106→127.0.0.1、SSH_PORT→1⇒ "是否 ssh"变成可观测差异):🔴 红腿(守卫摘掉 = 还原序㊱ 形态)=FAIL OBS-24 ❌ 注册表**读取失败**(⛔ 与"不存在"可分):Command failed: ssh -p 1 -o BatchMode=yes 127.0.0.1 …+ssh: connect to host 127.0.0.1 port 1: Connection refused(真 ssh 调用 2 次)|🟢 绿腿(现产物)=SKIP OBS-24 夹具模式未给 --nodes-fixture ⇒ SKIP + 留痕(⛔ 不去 ssh 读生产)(真 ssh 调用 0 次)。可分性 = 给不存在的夹具路径 ⇒FAIL … ❌ 注册表**读取失败**…:夹具不存在:…/NO_SUCH_nodes.json(⛔ 不是 SKIP)。 - ② 键名对齐:参数表
DRILL_RELAY_UNIT→RELAY_UNIT_NAME(值格未动、仍dshs-relay)⇒ ⛔ 零个新增 / 改动生产 env;引用点 3 处全改(overlay-failover-drill.cjs+ 原因注释 /overlay-probe.cjs/ 参数表 §3.9 + 小节标题注 + §11.5-⑤-4 状态)。⚠️ 文档库档案不改(04-调整方案/117-…/ 归档单T13记的是当时事实)。真机验证 = 原样取数命令 ⇒__RELAY_ACTIVE__=active✅。 - ③ P2 待拍板清单已登记 ⇒ 见本节 §8.11(只登记、⛔ 零动作)。
- ④ 零回归三件套(改动之后跑):
npm.cmd test(Node v22.22.2)= 201 / 200 / 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 20770 ms / deadline 30000)|overlay-probe.cjs --table(真机)= 25 PASS / 0 SKIP / 0 FAIL(rc=0)。⚠️ 复原(按 §8.8-④ 同款,⛔ 未重启任何 worker、⛔ 未换实例):演练后 47 视角确为空 ⇒ 探针呈 21P / 2S / 2F(2F =OBS-01 used=1、OBS-04 identityOk=1,计数判据、⛔ 与"端点表为空"无关)⇒ 停 / 启 106dshs-relay(约 22 s 后 47 视角回落used=2 / identityOk=2 / 端点 = [(19000,33213),(21001,42783)])⇒ 探针回绿。🔑 顺带取得键名改名的真机端到端证据:演练态SKIP OBS-08原文含「对端中继(test106)声明了端口…」⇒ 判别器真的走完了真机分支那条 ssh(内含systemctl is-active ${RELAY_UNIT_NAME})✅。 - ⑤ 边界自证:⛔ 未 commit / 未 push(HEAD
09ce76f;git status --short11 处 = 序㊲ 的 10 处 + 本棒新增scripts/overlay-failover-drill.cjs一处M,逐条可解释)|⛔ 未改任何生产配置值(参数表值格一律未动)|⛔ 零新增暴露面|🔴RELAY_FAILOVER_COOLDOWN_MS命中 0|⛔ 未改 bwrap / nft / nginx|⛔ 未开工 P2|🔴 密钥本体不经网络 / relay。 - ⑥ 如实留档:参数表 §11.6 已新增小节(🔴 §10 指纹
71848fad4ee0d6a574d833169e042fd1→2275870a5a9eb1964a648f2950dee528,现算并同步)|§8 前前缀085e28b56a08aa1f01999bdb3e238911逐字不变 ✅(本节在截断点之后)。🔬 两条口径:① 本棒"红腿"用的是同一份"ssh 目标不可达"的表副本 ⇒ ⛔ 未碰任何生产机(真 ssh 2 次全部打在127.0.0.1:1);② 既有夹具语义(--instance-fixture/--peer-status-fixture/ 序㊲ 的OBS-08三态)未受影响(回归自证见参数表 §11.6)。📁 证据 = 工作区_tmp_seq38/。
8.11 P2 待你拍板的具体内容(R5 权限影响评估 · 序㊳ 登记,⛔ 未开工)
起因:序㊳ 执行棒被点名"说下要定的具体内容"。本节把 §7-1 那句「具体端口/协议在 S5 设计时定,届时逐条列出」提前落实成清单 —— 让拍板不必先读代码。⛔ 本节只登记,不含任何动作。
已实测的三条事实(拍板依据,全部来自本线既有读数,⛔ 非推演):
- 🔴 47 / 106 的云安全组拦 UDP 入站(⛔ 不是 nft:
input policy = accept)。证据 = 序⑥ S4:47 上0.0.0.0:21100/21101UDP 观察器开放 ≈ 75 s、零收包(连同机发出的都收不到)⇒ 已关闭(ss -lunp空)。 - 打洞成功率(现网实测 n=3 对、2/2 可打洞):对端→本机方向 10/10 收包;本机→云机方向取不到(= 事实 1 的直接后果)。⚠️ 样本里云节点占 2/3,不代表家宽场景。
- 云机↔云机(47↔106)本来就是 L1 直连、不是打洞场景 ⇒ 直连的收益面只在"用户设备 ↔ 云节点"这一段。
要你拍的只有两件事:
① 要不要为打洞开「UDP 入站」的云安全组规则?(这是本期唯一真正的门禁)
A. 开 优点:打通"云节点 → 用户设备"这一方向,配合已是 10/10 的反方向 ⇒ 直连率接近业内值(0.90–0.94);S6「P2P 取回通道」才有成立前提。 缺点:新增入站暴露面(UDP 端口可被公网扫描 / 发包);且必须在云控制台改安全组 —— 这一步 ⛔ 本平台代码侧自决不了。
B. 不开 优点:零新增暴露面;P1 交付的"一键加入 + 分组准入"仍然完整可用。 缺点:链路两端只要有一端是云节点就只能走中继 ⇒ 直连率显著下降、省不了中继带宽;S6 的 P2P 取回失去成立前提(内容分发继续走中继)。
② 若选 A,入站 UDP 段的"来源范围"怎么定?
甲. 固定小段 + 对 0.0.0.0/0 放行
优点:规则最少、不会因对端 IP 变化而失效;实现最简单。
缺点:暴露面最大(全网可扫该 UDP 段)。
乙. 固定小段 + 仅放行对端节点 IP 优点:暴露面最小(只有白名单节点能发包),与本线"权限只准收窄"一致。 缺点:云安全组规则数有上限;家宽 / 移动 IP 会变 ⇒ 需动态维护、规则会膨胀。
丙. 不开 UDP,改走已建成的 443/TCP 兜底
优点:复用既有收口(零新增暴露面)。
缺点:TCP 443 打洞成功率远低于 UDP,且 addr-override.ts 目前只做"钉 IP"、不是打洞 ⇒ 事实上等于不做直连。
影响面与窗口(R8 口径):改动只落在 47 / 106 的云安全组 + 两台 relay 配置上;⛔ 不碰租户实例隔离面、⛔ 不跨网、⛔ 不影响既有 443 收口。回滚 = 删直连配置 + reload ⇒ 秒级回落"全部走中继"(§7-4)。⚠️ 时间窗可避开用户活跃时段。
本棒倾向(陈述句,可推翻):先拍 ①。若你要的是"看到线上效果",最小可验证形态 = A + 乙(固定小段 + 仅对端 IP)—— 它同时满足"能打洞"与"暴露面最小";甲 留到"直连率被证明是瓶颈"时再放开。⚠️ 若你更看重零新增暴露面,选 B 也完全不亏 —— 代价只是"直连 + P2P"这两项功能不做,P1 已交付的能力不受影响。
8.12 拍板落地(2026-09-18 12:22 · 序㊴ 规划棒;⛔ 未开工)
- 用户原话:
1 用户可设置,默认开启提示用户 2 乙 - ⇒ §8.11 的两问已闭合:
- ① 开不开 UDP 入站 ⇒ 「用户可设置 + 默认开启 + 提示用户」—— ⛔ 不是简单的"开"或"不开",而是把开关交给用户自己、平台默认开并必须提示(⇒ 产品口径见新单 §1);
- ② 入站 UDP 段的来源范围 ⇒ 乙(固定小段、仅放行对端节点 IP,⛔ 非
0.0.0.0/0)。
- ⇒ 产物 = 本线第一份 P2 可执行单:工作区根
交接单_覆盖网络直连与P2P_20260918.md(归档号 132;§8 前前缀8925c2460810c6dc5cdbc075b6cdaed2;239 行)。⛔ 本单正文一字未改(§8 前前缀085e28b56a08aa1f01999bdb3e238911仍是不变量 ✅)—— P2 的开工依据自此移交新单,本单 §6 的 P2 三行粗描述退化为指针。 - 🔴 一条如实登记的技术现实(同时写在新单 §4.5):乙要求云安全组按"对端节点 IP"放行,而家宽 / 移动节点的公网出口 IP 会变 ⇒ 规则会失效、且规则数有上限。序㊴ 自决(可推翻) = 乙-1 起步:只对有固定公网 IP 的节点开(云节点 / 专线);家宽节点本期走中继(⛔ 不影响可用性,只影响"它能不能被直连")。乙-2(半自动)/ 乙-3(云 API 自动化,🔴 需云 AK/SK)登记为新单 §9 回头条件 —— ⛔ 本棒未请求任何凭据。
- ⚠️ 一条遗留(仅报告,⛔ 未动手):
dsh-server-docs/04-调整方案/.lock-131仍在(序㊱ P1 的占号窗口未释放)⇒ 后果 = 后续取号者永久跳过 131(本棒已现核 ⇒ 取 132)。⛔ 本棒不动他人锁类目录,登记在此待处置。
§9 回头条件(一出现必须回头)
- 🔴 任何一棒都不许把
RELAY_FAILOVER_COOLDOWN_MS=0写进回滚 / 演练 / 夹具路径(自锁:实测 121–123 s 无切换)。 - 要改
server.ts的 DIAL/白名单语义(而非只读取)⇒ 停下报告(本单只做"配置派生 + 管理面")。 - P1 阶段出现任何新增暴露面 ⇒ 停下报告(P1 的前提就是零新增暴露面)。
- 跨网拒绝从"结构性"退化成"策略性"(拨通了再查表)⇒ 立刻停手回滚。
- join 流程出现静默拒绝(配置错伪装成"网络不通")⇒ 停下修,⛔ 不许带着上线。
peer档接线后块 id 未复算 ⇒ 判据不算通过(会把"零回源"做成假绿)。- 要动
wire.ts帧格式 /keys.ts既有语义 ⇒ 停下报告(R7)。
§10 未验证项(一个都不许编数)
| # | 未验证项 | 怎么验(口径) |
|---|---|---|
| 1 | 节点直连的成功率与收益 | 需 P2 落地后测:直连成功比例、切换耗时、省下的中继带宽(⛔ 现无数据) |
| 2 | 一次性凭据的并发竞争 | 同一凭据被两台机器同时使用 ⇒ 必须恰好一台成功(需并发夹具) |
| 3 | 多网络下的容量口径 | --max-hosts 是全局还是按网?⇒ 需明确并写进参数表 |
| 4 | 节点规模上限 | 本单不测;引用既有推演值(百台:L3 需中继 40–55)时⛔ 须标明是推演非实测 |
§11 指纹与状态
11.1 归档号
- 现核最大号:
ls 04-调整方案/ | grep -oE '^[0-9]+' | sort -n | tail -1⇒ 128(2026-09-18 09:0x 实测) - 占号:
mkdir 04-调整方案/.lock-131⇒ 成功 ⇒ 本单归档号 = 131(129 / 130 = 单 A / 单 B,⛔ 不重用) - ⚠️ 本棒不落 04 孪生(与本线全部
交接单_*.md一致)⇒ 占号窗口在落单后即释放,执行棒收官建档案时须重新现核取号
11.2 指纹
- §8 前前缀(口径:
sed '/^## §8 回报格式/,$d' <本单> | md5sum)=085e28b56a08aa1f01999bdb3e238911 - ⚠️ §8 回填后前缀不变、全文 md5 会变 ⇒ 全文 md5 不写死,权威取数 = 现算
11.3 状态
- 本单状态:P1 已执行收官(2026-09-18 10:0x · 执行棒
执行棒P1-节点一键加入与分组准入-0918);P2(S5–S7)未开工(命中 R5 ⇒ 必须先出权限影响评估并经用户点头) - 本棒(执行棒)边界自证:⛔ 未 commit / 未 push · ⛔ 未改任何既有 drop-in / env / nft·nginx / bwrap · ⛔ 零新增公网监听口 · ⛔ 密钥本体不经网络(详见 §8.5)
- P1 出口项:
src/web/routes/overlay-nodes.ts(管理面 API)本棒自决移入 P2(理由见 §8.6;⚠️ 可推翻) - 🔴 本棒唯一未复原项:
OBS-09由 PASS 退化为 SKIP(演练副作用,已归因、有原文;回头条件见 §8.6-3 与 参数表 §11.4-②)
11.4 与既有方案的关系
- 本单不取代任何既有文档;
覆盖网络_骨干层方案_20260916.md(骨干 ≤10–20 成员 + 选择性加入)与本单的"分组准入"是同一件事的两个层次:那份讲"骨干怎么选",本单讲"节点怎么进来"。 - ⚠️ 与
会合中继拆分_取证与改造方案_20260916.md(S0–S4,入口 §2 标注为"第一优先 · 真活")有交集:本单 P1 的"白名单派生"会动到 relay 侧配置投影 ⇒ 执行前须与该方案的进度对账,⛔ 不许两条线同时改 relay 配置面。
11.5 落单后实测
- §8 前前缀 =
085e28b56a08aa1f01999bdb3e238911(落单后实测;⚠️ 本行在截断点之后 ⇒ 改它不影响该不变量) - 落单快照:全文 md5
ca4ca8aad292efaf02357e1e2dacb6da/214 行(2026-09-18 09:1x;⚠️ 本行写入后即失效 ⇒ 引用前现算)