Files
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

1283 lines
184 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-09-18 工作区日志(append-only)
## 序 ㉗ 执行棒(覆盖网络线 · E3 候选数可查)· 00:3x 收官
**依据** = 用户 2026-09-17 23:1x 拍板「**2、B**」⇒ 授权改 `src/worker/relay-tunnel.ts` / `src/web/server.ts` 以暴露候选数(序㉖ 因超 §3.1 在册集、按 §9-5 停下的那一步)。本棒**无独立交接单**,报告写在入口 `接续入口_覆盖网络线_20260916.md` §0 最新行。
### 做了什么
- `src/worker/relay-tunnel.ts`:新增 `CAND_OBS_PREFIX` / `candidateObsMs()` / `candHostsOf()` / `RelayCandidateSnapshot` / **`RelayCandidateObservation`**(固定 key 序观测行;**只读、零网络 I/O**;周期重发上次快照、`unref`);接入 `RelayTunnel` 构造 + `ensureMaster()`(**非阻塞**一枪 `observeCandidatesOnce()`,⛔ 启动不依赖网络)+ `close()`。
- `src/web/server.ts`:C1(Manager 拨号)接入同一观测器;新增 `resolveChain()` 把 `candidates` 与 `refreshOverlay` 两处调用收口(注释里写了**字级等价证明**)。
- `scripts/overlay-probe.cjs`:新增 **`OBS-21`**(双判据:该路径有观测行 + `count ≥ CAND_MIN`)+ `--candidates-fixture-47` / `--candidates-fixture-106`;前缀**不硬编码**(`r.need('CAND_OBS_PREFIX')`)。
- 参数表 `参数表_覆盖网络_20260917.md`:§3.7 新增 5 键 + §6 新增 `OBS-21` + §11.1 补记。
- `test/relay-failover.test.mjs`:新增 **O1–O4**(锁 key 序/防刷屏/「从未解析」≠「解析出 0 条」+周期重发零网络/env 解析不静默变 NaN)。
### 验收
- **先红后绿(原文级)**:夹具四段 RED ⇒ `FAIL`(47=3 ✓ / 106=1 ❌)|GREEN ⇒ `PASS`|NOLINE ⇒ FAIL(**原文 225 字节 / 含前缀 0 条**)|WRONGSCOPE ⇒ FAIL(**154 字节 / 含前缀 1 条**)。全夹具 RED vs GREEN **只差 `OBS-21` 一行**。
- **真机先红**:新探针对**旧生产** ⇒ `FAIL OBS-21` 三路径全部「无观测行(原文字节 0)」。
- **零回归三件套**:`npm test` **201/200/0/1**(基线 197/196/0/1 + O1–O4)|`--scene all --table` **12 PASS/0 SKIP/0 FAIL**|`overlay-probe --table` **19 PASS/1 SKIP/1 FAIL**。
### 部署
`lib/worker/relay-tunnel.js` + `lib/web/server.js` scp 到 **5 处**(47 `/opt/dshs/lib`+`/opt/dsh-relay/lib`、106 `/opt/dshs-cluster/lib`+`/opt/dsh-relay/lib`、本机编译产物),md5 全同 = **`b1bc3e042d8eff1aafc657142e2e99d3`** / **`cf844a2ee312fc167ebadf5b2b9884f2`**;重启 47 `dshs`+`dshs-worker`、106 `dshs-worker`(动手前取证 47 无活跃实例 scope);回滚点 = 两机 `/opt/dsh/backups/seq27-20260918-000259/`。⚠️ **重建两次** —— tsconfig `declaration: true` 且**无** `removeComments` ⇒ TS 注释**会**进 `lib/**` ⇒ **纯注释改动也会改产物 md5**。
### 关键发现(本棒最大产出)
🔴 **「判据成立」与「冗余建成」已被彻底分开**:`dshs@47 count=3 hosts=3 ✓` | **`dshs-worker@106 count=1 hosts=1 ❌`**。
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)⇒ 目录永远被拒 ⇒ 候选链恒退化为内置种子单点。
⇒ **「建成冗余(≥2 条独立路径)」点名独立后续项 = 序 ㉙**(⛔ 未放宽 `CAND_MIN` 凑绿)。
### 三条留档(避免后人误判)
1. ⚠️ **47 的 `nginx.service` / `bt-server` 显示 inactive 是正常态** —— 宝塔托管:真正在跑的是 **`bt.service`=active**,443 上 nginx 在听(pid 在册)、`/dshs-relay` WS 握手 **101**、门户经 443 **200**。⛔ 别拿 `systemctl is-active nginx` 判 47 relay 死活(本次演练后核对时差点误判为"演练没还原")。
2. ⚠️ `OBS-21` 的 `hosts` = **主机名个数**,⛔ **不是独立物理路径数**:真机 `count=3` 时 `hosts=3`(`alotbuy.com` 与 `relay-direct.alotbuy.com` 摘名不同、**同落 47**),而**机器级**独立路径只有 **2**。首轮把它当"独立路径数"写错,已在 4 处修正(类注释 / `candHostsOf` / `Snapshot.hosts` / 探针块注释 / 参数表 §6)。
3. 🔴 **`CAND_OBS_UNITS_47` = `dshs=manager` 一项**:47 的 `dshs-worker` **不是** relay 客户端(`grep -c DSHS_RENDEZVOUS_URL /etc/dshs-worker.env` = **0**、`systemctl show … -p Environment` 里 RENDEZVOUS 计数 **0**)⇒ 它根本不建 `RelayTunnel`(`w-47` 的跨机面走 Manager 本机 `LocalRendezvous`)⇒ 列进去只会读到"永远无观测行" = **假红**。
### 登记与边界
- **下一棒 = 序 ㉙(执行棒 · 106 候选链建成冗余)**,automation **`cca5c43c-80e6-4bfb-8638-25185608cbbe`**(一次性 · **2026-09-18 03:00**,排在序 ㉘〔01:30〕之后并留足间隔)。⚠️ 已写死:**序 ㉘ 收官时 ⛔ 不要再注册 ㉙**(号已被占用)⇒ ㉘ 若还有下一棒顺延为 **序 ㉚**。
- **边界自证**:⛔ 未改任何生产值 · ⛔ 未新增公网口(`OBS-11` relay 仍只绑回环 1/1、集合多出 0)|⛔ 未改 nft·nginx · 🔴 `COOLDOWN_MS=0` 计数 **0** · ⛔ 未 commit / 未 push(HEAD **`04776af`**)。
- **指纹**:参数表 `4c78912d03f4f209d417407a80018361` → **`7fc5889341b99fb26bd05cad0313960b`**(口径 = `sed '/^## §10 指纹/,$d' 参数表_覆盖网络_20260917.md | md5sum`)|全文 md5 = `527b896b11328e68b8e1d2935722cd2c`(481 行)|改动源码 md5 = `relay-tunnel.ts e7689823a715ca4e489be16fef6761fd` / `web/server.ts ebd0bb2284644f3d873118383556f128` / `overlay-probe.cjs e9a6120087a2f0a5719a46372ad04a7f` / `relay-failover.test.mjs d533b1819bd38c0c6969f5020dfcce98`。
- 证据目录 = `_tmp_seq27/`(`fx*-red/green/noline/wrongscope.txt`、`leg-*.txt`、`probe-before/after/final.txt`、`drill-after.txt`、`npmtest-after.txt`、`deploy.sh`)。
## 收口后更正:接续时刻按用户明令压缩(2026-09-18 00:2x ~ 00:3x)
- **用户两次追问排期**(原话:「**为什么时间要定在3:00**」→「**㉘ 规划棒 = 01:30 也还有1个多小时呢**」→「**5-8分钟即可**」)。
- **认定 = 我排错了**:㉘ 是**规划棒**,实测同类(序 ⑦/⑨/⑪/⑯/⑱/㉔ 规划棒)**6–13 分钟**即完成 ⇒ 给它排 2 小时("收口 + ~2 h")**违反纪律**(「接续间隔 = 收口时刻 + 2~5 分钟」,⛔ 不许拿"留追改窗口"当理由拉长);我随后还按"留 1.5 h 余量"把它推到 03:00 ⇒ **错上加错**。
- **更正结果**:㉘ `b60d5358…` **01:30 → 00:35**(原话 03:00 那一版 ㉙ 也一并前移);㉙ `cca5c43c…` **03:00 → 00:43**(= ㉘ + **8 分钟**)。
- **已同步**:接续入口 §0 + §2 两处 ㉙ 行改为"本行为准 ⇒ ㉘=00:35 / ㉙=00:43"+ §2 第 2 棒行的 ㉘ 时刻加更正标注;本日志本行。
- ⚠️ **代价(如实登记)**:间隔 8 min < 规划棒耗时上限 ⇒ 若 ㉘ 超时未释放锁,㉙ 会按 R9「抢不到 ⇒ 只报告并停」**空转一棒**。这是**用户明令**换来的排期,判据 = 用户原话「5-8分钟即可」。
## 再更正:排期模型理解错了两处(2026-09-18 00:3x)
- **用户第三次澄清(原话)**:「**我说的是首个接续任务 5-8分钟,最好不要建立多个接续任务,一个会话结束时在排下一个**」。
- 🔴 **我的两处错误**:
1. **把「5-8 分钟」当成"棒与棒之间的间隔"** —— 实际是「**从本会话收口到"第一个(唯一一个)"接续任务**」的间隔。
2. 🔴 **同时挂了两个接续棒**(㉘ + ㉙)—— 违反「**同一时刻只挂一个**,下一个由当棒会话收官时再排**」。
- **处置**:⛔ **已删除 ㉙ 的 automation**(`cca5c43c-80e6-4bfb-8638-25185608cbbe`,用 `automation_update --delete`,⛔ 未用任何文件系统手段);㉘ 单独保留,时刻 = **2026-09-18 00:37**(≈ 收口 + 5~8 min)。
- **E3 冗余项没有丢**:已把它整段改写进 `接续入口 §0 + §2` 的 **⏭️ 本线下一项(⛔ 不预登记)** 行 —— 含根因 / 取证要求 / 改动首选 / R5 停手条件 / 验收基线,**由 ㉘ 在其收官时照此立棒**(正好符合"一个会话结束时再排下一个")。
- **规则已固化到 `MEMORY.md §三`**:⛔ **同一时刻只挂一个接续棒**;间隔 = 收口 + 5~8 分钟(到首个接续棒)。
## 接续规则加固:两条铁律从「状态层」升到「规则本体」(2026-09-18 00:3x–00:4x)
**触发**:用户「看最新会话『覆盖网络线-序27执行棒-E3候选数可查』最后 3 轮对话,发现问题 更新接续会话的规则」。
**取证**:`dd6abea4-3254-4499-a33a-0ac8307efb00.jsonl` 的最后 3 轮(记录索引 488 / 491 / 534)。
🔴 **真问题(比"我当场理解错"更深一层)**:上一轮虽然把两条铁律写进了 `MEMORY.md §三`,但 **规则本体(技能 `dsh-auto-handoff-chain §3.1.1`)里仍是用户已推翻的旧值「2~5 分钟」,且完全没有"同一时刻只挂一个"这一条** —— 换会话 / 换项目时,旧值会被**原样再执行一遍**。⇒ **规则只写进状态层 = 没落地。**
**改动(4 个文件 + 1 个镜像)**
- 技能 `dsh-auto-handoff-chain/SKILL.md`(`1.3.2 → 1.4.0`;md5 `c16975a0ba31291a5f3318d9ba914ff2`,22058 B,纯 LF):§3.1.1 重写为**排期两条铁律**(① 首个/唯一接续棒 = 收口 + 5~8 分钟,⛔ 不是"棒与棒之间" ② 同一时刻只挂一个,⛔ 不预登记队列)+ 边界表 + **一棒之内两处都犯的事故表**;§2 骨架收尾行 / §3 四件套 ② / §4 防护⑤(并**新增⑥「预登记多个接续棒」**,五条 → 六条)/ §6 落地清单 同步
- 三处同步完成:本机 ↔ 文档库 `dsh-server-docs/skills/` ↔ 47 镜像 `/opt/dsh/docs/skills/` —— **md5 全同**
- `会话接续规范_20260916.md` §3.2.2 链路图 `(+2 分钟)` → `(收口 + 5~8 分钟)`,并补「登记的两条铁律」表
- `dsh-server-docs/INDEX.md` 三处:§22 指针行 / §174 `04-125` 摘要(旧值 2~5 加更正)/ §185 技能清单(五条 → 六条)
- 工作区 `CODEBUDDY.md §2` 指针表新增一行「登记接续棒 / 排下一棒」⇒ 指向技能 §3.1.1
**⚠️ 生效性差异(重要)**:技能是**调用时现读** ⇒ 本改动对下一棒**立即生效**;`CODEBUDDY.md` 的改动需**完全重启 WorkBuddy** 才重载。
⚠️ `MEMORY.md` 注入**已实测被截断**(>7–8 千字符)⇒ 规则实体不能只靠它承载(本报告的真正动机)。
## 日志底座评估:amber vs Loki/Alloy vs 不装(2026-09-18 20:4x,用户提问触发)
**问题**:能否在 106 装 amber 日志服务,它是不是本项目最适合的开源日志服务。
**取证(只读,两机实测)**:
| 机 | CPU | 内存(可用) | 磁盘空闲 | journald 占用 | docker / go |
|---|---|---|---|---|---|
| **106** | 4 | 3.6 G(**2.2 G 可用**) | 30 G | 130 M | **none / none** |
| **47** | 2 | 1.9 G(**975 M 可用**) | 23 G | 768 M | docker ✓ / go ✗ |
**amber 事实(`github.com/yaop-labs/amber`,官网+repo 实测)**:OTLP 原生日志/追踪/指标单一二进制,Apache-2.0,**273 commits、单一维护者 `dmedovich`、最后提交 2026-08-29**;**无预编译 release**(只能 `make build` / `docker build`);metrics 引擎**自标 alpha**(int64 存储、scale 默认 1000);落盘**异步**、OTLP **at-least-once 且 0.4 明确不做去重**;默认 `retention.*` 全 0(= 不限制),`disk_stop_free_bytes` 1 GiB。
**判定**:✅ 能装(106 是两机里唯一放得下的);❌ **但不是最适合,作为本项目日志底座不合适**。三条理由:
① **它不是采集器**——journald → OTLP 仍要一个 agent(Alloy / Vector / otel-collector)⇒ 组件数并不比 Loki 方案少;
② **成熟度与本项目判据链冲突**——本项目大量验收判据是**日志原文的字节数/行数**,而 amber 是"异步落盘 + at-least-once + 不去重 + 无预编译产物",判据来源会从"系统事实"降级为"新项目 API 输出";
③ **部署链路更绕**——两机都无 Go 工具链,而 47 只剩 ~975 M 内存 ⇒ 构建本身就是代价。
**正解排序**:C(不装,先补"跨机只读拉取")→ A(**Grafana Alloy + Loki 单机版**,装 **106**;Alloy 是 Promtail 的官方后继,**Promtail 已于 2026-03-02 EOL**,Alloy 用 `loki.source.journal` 直读 journald)→ B(amber 仅作 106 旁路试点,只绑回环、不进判据链)。
**触发条件(到点才上 A)**:实例数 ≥ ~10 或日志量涨到 GB 级/周,或"跨机时序对齐"类排障反复发生。
🔴 **任何新监听口都只绑回环**(或走既有 nginx 443 反代)⇒ 不新增公网口、不动 nft/nginx(R5 边界);⛔ **日志底座不放 47**(内存不够)。
## 日志可观测面落地:方案 C 实现(2026-09-18 21:0x–21:4x)
**用户决策**:选 C(不装 Amber / 不装 Loki+Alloy,用现有 journald + 自研观测面),并要求实现"多节点日志存储 + 实时 bug 监控 + 事后溯源"。
**交付物**:代码仓 `scripts/dshlog.mjs`(Node 22 单文件、零第三方依赖、8 个子命令)+ 档案 `dsh-server-docs/04-调整方案/129-日志采集与巡检-方案C实现.md`。
**关键取证(都实测过,别凭记忆)**
- `journalctl --version`:47 = **systemd 239**、106 = 255;两机 NTP 均 `NTPSynchronized=yes`;journald 均 persistent(47 = 769 MB / 106 = 130 MB,**都未设上限**)。
- 🔴 **实例 `dsh --profile` 的 stdout 不进 journald** —— `_SYSTEMD_UNIT=dsh-<uid>-<hash>.scope` 与 `journalctl _PID=<实例pid>` **双证为空**,实例 `fd/1` 指向 `socket:[…]`(被 Worker 接管)⇒ **dshlog 抓不到实例层日志**,这是本方案最大剩余缺口。
- 当日日志分布:47 = `dshs` 29k / `dshs-worker` 7.3k / `sshd` 3.8k;106 = `user@0` 2.6k / `init` 1.6k。
**踩坑(已写进档案 §6,下次别再犯)**
1. 🔴 **`ssh -C` 是硬前提**:47 出方向未压缩实测 **~20 KB/s**(1.6 MB 要 **84 s**),开压缩后 **11 s**(7.6×)。不加会**看起来像代码 hang**(第一版 3 h 回填超 5 分钟被外层超时杀掉,误判成死锁)。
2. **数据/元信息必须双通道**:远端 `awk` 转发 stdout(数据)+ 行数写 stderr(`__DSHLOG_LINES__`)+ `__DSHLOG_EOF__ rc= errbytes=` ⇒ "确无日志"与"拉取失败"天然可分。
3. **归档必须同步写 + gzip 多成员**:`createWriteStream` 异步管道在收尾时可能未 flush ⇒ 静默丢最后一批;改 `appendFileSync(gzipSync(...))`。
4. **回填去重靠 `__CURSOR` Set**(实测跳 8100 行重复),**续拉靠 `--after-cursor`**(精确不重不漏)。
5. **校时用 NTP 状态,别自造往返估算**:第一版给出 **+1337 ms 假偏移**(47 的 ssh RTT 3.7 s 且不对称)⇒ 拿它校正会**制造**错序。
6. **版本号解析必须 `awk 'NR==1{...}'`**:`journalctl --version` 是多行 ⇒ 变量变 `"239\n0"` ⇒ `[: integer expression expected` ⇒ **静默退回全字段(体积翻倍)**。
7. **巡检规则收紧**:`fatal` 裸写会被 sshd 的 `ssh_dispatch_run_fatal` 天天命中;`Stopped .*` 裸写会把实例**正常退出**当崩溃 ⇒ 都已收紧 + 排除 `sshd/crond/systemd-logind` 噪声单元。
**验收(真机)**:增量续拉 19.5 s/回填 6 h = 2 分 28 秒/归档 47 = 35,603 行 · 106 = 5,988 行;`watch` 在 106 上**真报**出 `⚠ 取目录全部失败`(= 序 ㉗ 记录的 E3 缺口),非误报。
**边界自证**:新增公网监听口 **0** · 服务器侧新增常驻进程/安装 **0**(只用系统 `journalctl`,脚本走 `bash -s` stdin)· 生产值改动 **0** · 归档在 `E:/dsh-logs/`(不在仓库内)。
**顺带改的一处**:技能 `dsh-instance-diagnose` 加「第 0 层 · 跨机日志取证」(指向 dshlog)+ 三条必知,`1.0.0 → 1.1.0`;三处同步 md5 一致。⚠️ 同处**如实记下**:实例层缺口的说明写在技能里,避免下次又以为"日志能覆盖实例"。
**待用户拍板**:是否挂周期 automation 做"实时"巡检(A 每 30 min ≈ 250–430 积分/天 · B 每天 1 次 · C 不挂、按需手动)。我的倾向 = C,理由 = 实时性的积分成本与当前规模不匹配,且已有 `overlay-probe.cjs` 承担判据式主动探针。
### 澄清(用户 21:2x 追问"日志存哪 / 存本机吗 / 启动了哪些服务")
⚠️ **这个误会值得记**:用户问"启动了哪些服务",答案 = **什么都没启动**。实测取证:
- **服务器侧零安装**:47 上 `/opt/dsh/scripts/dshlog.mjs`、`/usr/local/bin/dshlog*`、`/etc/systemd/system/*dshlog*` **全不存在**。
- **本机零常驻**:node 进程数 **0**、无 pid 文件。
- ⇒ 当前是**纯手动按需**:跑一条命令 → ssh 拉一批 → 退出。**不跑就没有新数据**。要"实时"必须挂 automation(= 上面那个未定的拍板项)。
**数据存在两处(不是"搬走了")**:
| 位置 | 内容 | 现状 |
|---|---|---|
| **服务器本机**(原始·权威) | `/var/log/journal/<machine-id>/`,系统自己写,**未被动过** | 47 = **769 MB** · 106 = 130 MB;`journald.conf` 无上限设置 |
| **本机镜像**(副本·便捷) | `E:/dsh-logs/<host>/<日期>.ndjson.gz`(**gzip 压缩**、按天分片) | 47 = 1.15 MB · 106 = 218 KB · `state.json` 825 B(游标/时钟状态) |
⚠️ 两个体积**不可直接比** —— 服务器那份是**全机全历史未压缩**(所有单元所有天),本机那份是**已抽取的压缩副本**。
### 日志保留策略 = 3 天落地(用户 21:2x 明令「把旧日志清理 只保留3天的日志就行」)
**动作**:47 / 106 各写 drop-in `/etc/systemd/journald.conf.d/10-retention.conf`(⛔ 不覆盖主配置)+ `journalctl --vacuum-time=3d` + `restart systemd-journald`;本机 `dshlog prune --keep 3`(默认值已从 14 改为 3)。
`MaxRetentionSec=3d` | `SystemMaxUse`:47 = **512M** / 106 = **192M** | `SystemMaxFileSize`:47 = **32M** / 106 = **8M**。
**结果**:47 **768 → 160 MB**(释放 608 MB)|106 **130 → 70.3 MB**(释放 59.5 MB)。两台 journald 均 active,配置已生效。
🔴 **踩坑(务必记住,下次别再按错判据估)**:`--vacuum-time` 的判据是
**「分片**起始**记录时间」**,且按**整片**删除 ⇒ **实际保留期 = 3 天 −(0 ~ 一片跨度)**。
- 我执行前误判为"按末记录判",首次估算偏差 ⇒ **删多了**:
- 47 保留跨度只剩 **1.35 天**(09-17 13:05 起)—— 因默认分片 128M ≈ 1.5 天/片
- 106 保留跨度 **2.44 天**(09-16 11:29 起)—— 因分片 47M ≈ 3 天/片
- **永久丢失区间**(本机归档当时未覆盖 ⇒ 无副本):47 = 09-15 13:33 ~ 09-17 13:05;106 = 09-13 12:54 ~ 09-16 11:29。
- **修法** = 压小分片提高精度(已做);47 的跨度会随新数据每日增长约 1 天,**约 1.7 天后自然恢复到 3 天**,无需人工干预。
- 已把该判据 + 偏差留档写进 `04-调整方案/129-…md §8.1`(并 scp 到 47,md5 一致)。
---
## 覆盖网络线 · 序 ㉘ 规划棒收官(00:37–00:5x)· 两项架构级改造各出一份执行单
**本棒 = 只做规划:⛔ 零代码改动 · ⛔ 零服务器改动**(仅两机各一次只读 `systemctl list-units --type=scope` + `systemctl show -p Environment`)。
**产物(工作区根,各带 8 段模板)**
- `交接单_退出路径不杀实例_20260918.md`(**单 A** · 归档号 **129** · §8 前前缀 `c9aeac82381f569fc8b9effad0fefa99` · 落单快照全文 md5 `b1b93471081e10d9be9c28aa9b5c1d3c`/387 行)
- `交接单_组密钥加密_20260918.md`(**单 B** · 归档号 **130** · §8 前前缀 `149596460288ca1a5b7abddc490f7909` · 落单快照全文 md5 `40d35357a501596dcd1e6d7e70e8b371`/349 行)
**归档号现核(⛔ 教训:prompt 写"至 112"已过期)** = `ls 04-调整方案/ | grep -oE '^[0-9]+' | sort -n | tail -1` ⇒ **128**;`mkdir .lock-129` / `.lock-130` 均成功 ⇒ 号可用;⛔ **63 空号不补占**已复核。⚠️ 取证:本线 16 份 `交接单_*.md` **均无** 04 孪生(带孪生的是**方案类**文档,如 `123-` ↔ 根同名 md5 同值)⇒ 占号窗口落单后释放,收官建档案须**重新现核**。
**本棒三条取证事实(已写进 §0,⛔ 与上游结论冲突时以本棒为准)**
1. **处② `RemoteSpawner.teardown()` 已经就是 no-op**(与 `git show HEAD:` 逐字相同,函数体只有一行注释)⇒ 用户口径"三处一起改"与"处② 已符合"**不矛盾**:三处都纳入在册 + 都加机器断言,但处② **语义改动 ≈ 0**。
2. **两机活跃实例 scope = `47:0` / `106:0`** ⇒ 单 A 的验收**必须先在真实形态夹具上做**,⛔ 不许拿 `0 == 0` 当绿(已写成 §5 反例条款)。
3. **`idle-reap` 缺省启用且 TTL 很长**(`config.ts:300-302`:60 s / **7 天** / 每 host 4;两机 env 未覆盖)⇒ 单 A 的前提"进程长期不重启时由 idle-reap 收"**成立但很慢**。
**单 A 设计要点**:三处逐处最小差异(① 真改 ② 只补断言 ③ 拆开"停心跳"与"停实例")|**判据三层 P0→P1→P2**(🔴 **P1「判据成立」与 P2「回收链建成」必须分开报**:`scanned==0` 而计数守恒 ⇒ 必须点名,⛔ 不得写全绿)|**对冲项**(静态 grep + 动态单测 + `OBS-22` 三判据)|`C` 单列一段(含 4 条回头信号 + B→C 四步迁移路径)|与 lease/配额/端口区间/`dsh_hosts`/provisioner 逐项关系。
**单 B 设计要点**:组 = 复用序㉔ `(network,group)`;算法栈 = 复用 `src/crypto.ts` 的 AES-256-GCM;**确定性(收敛)加密**保证组内密文一致 ⇒ 按哈希共享块仍成立。🔴 **本棒纠正一条上游前提**:「块 id 用明文哈希 ⇒ **跨组也能共享同一块**」**在本方案下不成立**(组外没有组密钥 ⇒ 拿到密文也解不开;且 `E5` 本就要求跨组 0 穿透)⇒ 明文哈希**净收益 ≈ 0、只剩泄漏块存在性的缺点** ⇒ **推荐密钥相关/密文相关哈希(`sha256(密文)`)**,其唯一代价 = **轮换 ⇒ 全量回源**(写成参数下限 + 回头条件)。🔴 分发受**节点密钥是 `ed25519`、做不了公钥包裹**这一硬约束 ⇒ 本阶段密钥本体**不经 relay**(走 `0600` 文件),规模化自动分发登记为回头条件。
**收尾**:下一棒 = **序 ㉚ 执行棒 · 单 A**,automation **`11982d2f-6608-456a-b500-a6dfcc6bbb0f`**(一次性 · **2026-09-18 00:50** = 收口 +6 min);**㉙(106 候选链冗余)本轮⛔ 未登记**(同一时刻只挂一个棒;`cca5c43c` 已用 `automation_update list` 复核确认**确已删除**),由单 A 收官时登记。**定序写死:单A → ㉙ → 单B**(单A 是用户 23:3x 纠错的直接产物且结清待拍板阻塞项;㉙ 最小却能让探针转绿、恢复判据灵敏度;单B 最大放最后)。入口 §0/§2 已推进。
**顺带(系统要求)**:`MEMORY.md` 注入被截断 ⇒ 就地合并压缩 **8101 → 7782 字符**(合并 §一 表格 8 行→5 行、更新过期值:归档号 112→**128**、参数表指纹→`7fc5889341b99fb26bd05cad0313960b`、`npm test` 基线 176→**201**),并新增两条:**`ACTIVE` ≠ 待跑**、**本线交接单不带 04 孪生**。⚠️ 四类硬规则(R10/bwrap/数据分层/buffer.write/Playwright/接续两铁律/`COOLDOWN_MS=0`)已逐条复核仍在。
**边界自证**:⛔ 零代码改动 · ⛔ 零服务器改动 · ⛔ 未改任何生产值 · ⛔ 未新增公网口 · ⛔ 未改 nft·nginx · 🔴 `COOLDOWN_MS=0` 计数 **0** · ⛔ 未 commit / 未 push(HEAD **`04776af`**)。
---
## 2026-09-18 05:3x · 序 ㉚ 执行棒(单 A:退出路径不杀实例)收官
**定位**:覆盖网络线 · 序 ㉚(执行棒 · 单 A),依据 = 工作区根 `交接单_退出路径不杀实例_20260918.md`(归档号 129)。
**锁**:`覆盖网络线-序30执行棒-单A`(00:50 抢到 → 05:4x 释放)。
**做了什么**
- 三处 `teardown()` 最小改动:`LocalSpawner.teardown()` **真改**(删掉"逐 uid 停实例"循环);`RemoteSpawner.teardown()` 取证已是 no-op(与 HEAD 逐字相同)⇒ 只补守卫标记;`LeasedSpawner.teardown()` 拆开"停心跳"与"停实例"(保留 `stopHeartbeat()`)。
- 新增 `test/orchestrator-teardown.test.mjs` **9 用例**(⛔ 刻意不进 `npm test` —— 那是硬编码文件列表)。
- 探针新增 **`OBS-22`**(静态守卫读部署产物 + 认领面);参数表 §3.8 +6 键(`TEARDOWN_*`,只被探针读、⛔ 不写 env)、§6 +1 行。
- 单 A 回填 §8.1–§8.12(§8.12 = 回滚路径补正,⛔ §6 正文一字未改以保 §8 前前缀)。
**判据(可复现)**
- 先红后绿:P1 对旧生产 `1 → 0`(两机);`OBS-22` 对旧产物 FAIL 并点名 `orchestrator「await this.stop(userId);」`;单测红 3(T1/T5/T6,逐处点名)/ 绿 9,还原为字节级。
- 主判据 **P1 + P2 双绿**:47 worker `1 → 1`、106 worker `1 → 1`、47 Manager `1 → 1`(**非判别腿**);`{"scanned":1,"adopted":1,"stopped":0}` 自洽 + `probe OK :20000 / :21000`。
- 零回归三件套:`npm test` **201/200/0/1** | `--scene all --table` **12P/0S/0F** | 探针 **21P/0S/1F**(1F = `OBS-21` 线内在册缺口,⛔ 非本单引入;`OBS-09` SKIP → PASS)。
**部署 / 回滚**
- 4 处(47 `/opt/dshs/lib` + `/opt/dsh-relay/lib`;106 `/opt/dshs-cluster/lib` + `/opt/dsh-relay/lib`),md5 全同:`orchestrator.js = 1456d1609f5872c2d635ae7cf4f5c26b` / `remote-spawner.js = 0438afd5738176999d3a4f4f404c9435` / `leased-spawner.js = 12ba045990b2054aaeebeb2c95d70f83`(05:3x 复核未漂移)。
- 回滚点 = 两机 `/opt/dsh/backups/seq28-20260918-005909/`(6 文件,含 `relay-*.js` 旧值)。
- 重启 47 `dshs`+`dshs-worker`、106 `dshs-worker`(⛔ 未重启 `dshs-relay`);门户 200。
**指纹**
- 参数表:`7fc5889341b99fb26bd05cad0313960b` → `3b295a8c43aafc4a62c6f1a59ea0b43a`
- 单 A §8 前前缀:`c9aeac82381f569fc8b9effad0fefa99`(**回填后逐字不变** = 截断点口径自证);全文 md5 `681b84d503a1578178707a9ba6d87351`/584 行(现算)
**三条必须记住的**
1. 🔴 47 的 `dshs` = `LeasedSpawner(new RemoteSpawner(…))`(`DSHS_DEPLOY_MODE=cluster`)⇒ **不构造 `LocalSpawner`** ⇒ 「47 `restart dshs`」是**非判别腿(恒绿假判)**;判别腿只在两台 `dshs-worker`。
2. 🔎 `/opt/dsh-relay/lib/supervisor/` 两机各有一份副本(曾落后 2 版)⇒ **铺 relay 类产物要四处都铺**;本次一并铺平,旧值存同一回滚点。
3. ⭐ B 的收益边界:**实例不被杀 + 用户访问时自然替换**,⛔ **不是**"重启后可直接进"(`launchToken` 不可恢复 ⇒ `enter` 可能 503);替换窗口内会短暂留 1 条 stale 端点条目(`OBS-08`/`OBS-11` 该窗口内会红,**自愈时延未测**)。
**独立后续项(登记,⛔ 本棒未扩大)**
- `summary.probeOk` 恒 0(既有观测面缺陷 ⇒ 判据⛔ 不可用 `probeOk ≥ 1`;修法 = summary 挪到探活回调之后)。
- 「自然替换窗口内 stale 端点条目」自愈时延未测。
- 单 A §10 的 6 条未验证项(一条未验)+ 架构目标 `C`(§7.2,登记不执行)。
**接续**
- 下一棒 = **序 ㉙(执行棒 · 106 候选链建成冗余)**,automation **`22170b0b-a606-461c-82a7-2f72fea68082`**(一次性 · **2026-09-18 05:45** = 收口 +~7 min)。**定序仍写死:单 A → ㉙ → 单 B(组密钥加密)**;单 B 由 ㉙ 收官时登记。
- 入口 §0 已插最新刷新行、§2 首段已改写为序 ㉙ 口径(原序 ㉚ 段降级为存档)。
**边界自证**:⛔ 未改任何生产值(§3.8 六键只被探针读取)· ⛔ 未新增公网监听口 · ⛔ 未改 nft·nginx · 🔴 `COOLDOWN_MS=0` 计数 **0** · ⛔ 未 commit / 未 push(HEAD `04776af`)· ⛔ 未删 `cleanStaleScopes(uid)` · ⛔ 未改 bwrap 参数。
---
## 05:3x · 会话数据规模评估 + 「能否同步到 dsh_shenxian_session.git」取证(只读,未动手)
**问题**:用户问「这个工作空间的 session 有多大,能否同步到 `[email protected]:maogeigei/dsh_shenxian_session.git`」。
**会话库落点**(🔴 判据):`E:\ProgramData\.workbuddy\projects\<cwd 编码名>\` —— 本工作区 = `e-ProgramData-AI技能-aliyun-dsh-server\`
(按工作区根路径编码命名;旧 `D:\AI技能\...` 另有 `d-AI技能-aliyun-dsh-server\` 一份残留)。
每会话三件:`<id>.jsonl`(正文,追加式)+ `<id>.meta.json` + `<id>.file-rollback.ndjson`(41 份)。
**实测(2026-09-18 05:3x)**:187 文件 / **245,991,247 B ≈ 235 MiB** / **69 个会话** / 活动跨度 **09-13 14:37 → 09-18 05:38**(≈5 天)
⇒ 粗算 **≈49 MB/天(原始)**;最大单份 `3c12a818….jsonl` = **24.2 MB**;gzip 实测 **6.2x**(24.2 → 3.9 MB)⇒ 全量压后 ≈ 40 MB。
旧 D: 盘那份 `d-…-aliyun-dsh-server` = 76,784,415 B ≈ 73 MiB(09-15 搬迁前)。
**🔴 敏感串计数(只数不打印原文)**:`ak_…` 6 | `sk-…` 15 | `Bearer …` 15 | `dsh-auth-…` 699 | `mksess` 6133 | `password` 1130
⇒ 会话正文**确实含可用凭据**(含 Redfox API Key)。**git 历史永久** ⇒ 推上去即不可撤回。
**目标仓库现状(与用户假设不同,必须先说)**:
`dsh_shenxian_session.git` 与 `dsh_shenxian_doc.git` 的 `HEAD/main` **同为 `9b57578`**(2026-09-14 23:01 maogeigei "docs: 档案 98 治本")
⇒ 该仓现在装的是 **09-14 冻结的文档库快照**(162 文件、`04-调整方案` 最大到 **98**),**不含任何 session**;
09-15 文档库并入代码仓(`dsh_shenxian.git`)后,这两份独立文档仓即**不再更新**。⇒ 往里推会话属"改用途",且**不是空仓**。
**技术可行性**:✅ 可推 —— 仓库存在、SSH 可读、单文件最大 24 MB 远低于常见上限;`--depth 1` 浅克隆实测 935 KB。
**未做**:未写库、未推、未删该仓既有内容(删除不可逆 ⇒ 需用户明确指令);`/e/tmp/dsh_session_probe` 探针副本已清理。
**待拍板(已在回复末节按 A/B/C 竖排给出)**:凭据处理方式 —— 原样 / 脱敏 / 加密归档。**我的技术已定项**:落位用 `sessions/<工作区名>/` 子目录,不动既有快照。
---
## 06:3x–06:4x · 复盘「为什么又弹授权窗」(用户质问:不是已经授权了吗)
**结论**:那个弹窗**不是 WorkBuddy 的授权闸门**,是 **git 自己的凭据管理器 GCM**。
**证据链**:
1. `E:\\ProgramData\\.workbuddy\\audit-log\\spool\\audit-spool-40716-2026-09-18.jsonl` ⇒ `06:32:34 | command-safety.sandbox-executed | allowed`,
内容正是我那条 `CNB https 探测` ⇒ **平台已放行**(同日 06:30:27 的 hot.ts 探测同样 allowed)。
2. `~/.gitconfig`:`[credential] helper = !"…/mingw64/bin/git-credential-manager.exe"`(**全局挂 GCM**)+ `[credential "https://work.alotbuy.com"] provider = generic`
+ `[http]/[https] proxy = 127.0.0.1:10800`。⇒ `work.alotbuy.com` 一直走 **SSH**(key 已通、**不进 GCM、从不弹**);
`cnb.cool` 是**没配过凭据的新 HTTPS 主机** ⇒ GCM 必弹。**"授权过 SSH" ≠ "所有 git 主机都授权了"。**
3. **反证/验证**:改用 `git -c credential.helper= + GIT_TERMINAL_PROMPT=0 + SSH` 重试 ⇒ **秒级明确报错、不再挂起**(原来"无输出"就是 GCM 挂着等凭据)。
**🔴 新增取证位置**:当日审计**未 flush** 时,正式分片 `YYYY-MM-DD.N.jsonl` **滞后**,最新事件只在
`audit-log/spool/audit-spool-<pid>-<date>.jsonl` ⇒ 判"某命令到底有没有被平台拦"必须查 spool(此前只查分片 ⇒ 会误判"无记录 = 被拦")。
**今天唯一一次"平台弹窗"** = `00:04 file-safety.bulk-delete.approved`(删 D 盘残留那条),**逐次生效、不具传染性**。
**已沉淀**:`PLAYBOOK-实例与插件坑.md` 新增 **§13**(GCM 坑 + 判据 + 修法 + ⛔ 别全局关 helper)。
**未做**:会话数据仍**未动**(A/B/C 待拍板);`cnb.cool:maogeigei/dsh-ai1net-session.git` 未确认存在/权限(SSH 返回 `Could not read from remote repository`,可能是仓库名不对或无权限)。
---
## 覆盖网络线 · 序 ㉙ 执行棒收官(2026-09-18 05:45–06:1x):`OBS-21` 由红转绿
**一句话** = 106 侧 relay 候选链从"退化单点"变成 **3 条**,探针 **22 PASS / 0 SKIP / 0 FAIL(rc=0)** ⇒ 本线**在册红项清零**。⛔ 未放宽 `CAND_MIN`。
**根因(序㉗ 已取证、本棒复核一致)**:106 `/etc/dshs-worker.env` **无** `DSHS_OVERLAY_DIR_PUBKEYS` ⇒ 目录**取到了也一律被 `no-trusted-keys` 拒**(106 日志近 3h 166 行可证)⇒ 候选链恒退化为内置种子单点。
**改动(全序唯一一处)** = 106 `/etc/dshs-worker.env` **追加一行** `DSHS_OVERLAY_DIR_PUBKEYS=fd81a6dd…`(= 47 同一把;47 真值来源 = `/etc/systemd/system/dshs.service.d/overlay-dir.conf`,私钥 `/etc/dshs/overlay-dir-key.pem`)+ `restart dshs-worker`。回滚点 = 106 `/opt/dsh/backups/seq29-20260918-054852/dshs-worker.env.orig`(md5 `6b509ce447d9519241aa73243e07a69f`)。
**🔬 先取证再动手(⛔ 未采信"补钥即绿",两条路径各自取证)**:**(a) 公网路径** `https://alotbuy.com/dshs-overlay/bootstrap` 从 106 `curl` = **200 / remote `172.67.194.206`(CF)/ 443 字节**;且 106 的失败码是 **`no-trusted-keys`** —— 该码在 `directory.ts#fetchDirectory` 里**位于 `fetch → res.json() → parseDirectory` 之后** ⇒ **"取到了、只是验不过"** ⇒ 可达性成立。**(b) 直连 `47.77.182.89:3080`** = **timeout(000)**(已知 `TCP_BLOCKED` 边界)⚠️ 但**取目录流程根本不用 3080**(域名 → 443 → nginx → 回环 3080)⇒ **不构成阻塞**。
**验收**:`OBS-21` 两条路径 `count=3 ≥ 2`、rc=0 | `npm.cmd test` **201/200/0/1** | `--scene all --table` **12P/0S/0F** | 探针 **22P/0S/0F**。
**边界自证**:⛔ 未改任何生产值(106 上 `RELAY_FAILOVER_|HB_SEC|PRESENCE_|DSHS_REHYDRATE_` 计数 **0**;env 与备份 `diff` = 只多 4 行注释+1 行键)· ⛔ 未新增监听口(`OBS-11` 实际 **77** / 多出 0 / nft accept 多出 0)· ⛔ 未动 nft·nginx · 🔴 `COOLDOWN_MS=0` 两机计数 **0** · ⛔ 未 commit / 未 push(HEAD `04776af`,`git status` **33** 逐数未变)· 参数表指纹 `3b295a8c43aafc4a62c6f1a59ea0b43a` **未变**(改动落 §11 补记区 ⇒ 在 §10 口径之外)。
**🔴 两条必须记住的(后续任何一棒都别踩)**:**① 孤儿端点条目不会自愈**(序㉚ 留的"自愈时延未测"至此有答案):`relay-tunnel.ts#cancel()` 只撤**当前进程** `forwarded` 集里的端口 ⇒ **上一进程遗留的条目无人撤销**,只能靠**后续一次"重新登记同一端口号"**覆盖回 `online=true`。实测:重启 106 `dshs-worker` 后旧条目 `21000→41831` 恒 `online=false` **约 10 min 未自愈**;停掉实例、由 Manager 自动重新拉起(新实例落回 **21000** 基址)后才转绿。⇒ **重启 worker 后 `OBS-08`/`OBS-09` 必红**,⛔ 不要误判成"等一会儿就好了";正确的收口顺序 = `restart dshs-worker` → `restart dshs`(两客户端归零回 47 relay)→ **一次真实访问**(`mksess` 临时 session + `enter`)→ 实例落回 21000 基址 ⇒ 全绿。**② `DSHS_OVERLAY_DIR_CACHE` 106 仍为 0**(47 亦为 0 **且 47 侧一直绿**)⇒ 本棒**与"已验证有效"的 47 配置逐项对齐**;⛔ 按"只做被明确要求的事"**未扩范围**。缺它的后果 = 目录不可达时**没有"过期缓存"档**、直接落种子兜底(候选退回 1 条)⇒ 属**冗余韧性**而非本项判据,若要"CF 抖动期也保住 ≥2"需单独立项。
**文档落点**:本棒无独立交接单 ⇒ 回报在 `接续入口_覆盖网络线_20260916.md` §0 最新行 + §2 | 参数表新增 **§11.2**(状态改判)| 下一棒 = **单 B 执行棒(组密钥加密)**,依据 = 工作区根 `交接单_组密钥加密_20260918.md`(§8 前前缀 `149596460288ca1a5b7abddc490f7909`)。
---
## 06:4x–07:0x · 会话日志同步到 CNB 私有仓(已完成)
**目标仓**:`https://cnb.cool/maogeigei/dsh-ai1net-session`(**Private**,09-17 22:26 创建;用 `https://api.cnb.cool/<owner>/-/repos` 才查出 —— 我先前猜的 `dsh_shenxian_session` / `dshai1netsession` 等 7 个名字**全是 404**)
**凭据**:用户名 `cnb` + 用户提供的 token ⇒ 已 `git credential approve` 存入 **GCM**(加密),并补 `.gitconfig` 的 `credential.https://cnb.cool.provider = generic` ⇒ **日后不再弹授权窗**(这就是用户上一轮问的那个弹窗的根治)。
**最终远端** = **`d83ed62`**「init: WorkBuddy 会话日志全量原样镜像」—— **原样未脱敏**,218 文件;本地 `.git` 66 MB,推送约 300 MB / 53 秒。
**内容落位**:`sessions/e-ProgramData-AI技能-aliyun-dsh-server/`(当前工作区)+ `sessions/d-AI技能-aliyun-dsh-server/`(搬迁前旧工作区);每会话三件(`.jsonl` 正文 / `.meta.json` / `.file-rollback.ndjson`)。附 `.gitattributes = * -text` 防 CRLF 改行尾破坏 JSONL 结构。
**用户口径演变**:`不用脱敏,这个是私有仓库`(原始意图)→ 中途 `脱敏就脱敏吧不用重复处理了` ⇒ **最终保留原样版**(= 首次指令),未再重复处理。中间那版脱敏(938 处命中)已被覆盖。
### 🔴 本轮五个坑(都已实证)
1. 🔴 **批量删除闸门会让脚本「静默半途而废」** —— 单次删除 >50 文件 ⇒ `[safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED]`,**删除被拦但脚本继续往下跑**:
`os.makedirs` 撞已存在目录 ⇒ 脚本报错退出(复制一步没执行);`rm -rf .git` 同样被拦 ⇒ `git init` 变 **re-init** ⇒ **新提交叠在旧内容上**,产出「提交信息写着原样、内容其实还是脱敏」的**假绿**。
✅ 正解 = **改用全新目录名**,不依赖删除;删完必须 `[ -d ]` 复检。
2. 🔴 **杀掉后台任务 ≠ 撤销它已发出的推送** —— TaskStop 报 `killed` 之后,远端 sha 仍从 `8e88cd2` 变成 `d83ed62` ⇒ **终止后必须重新核对远端状态**,不能只看 kill 回执。
3. 🔴 **`git -c credential.helper=` 会把「已存好的凭据」也一起禁掉** ⇒ `could not read Username for 'https://cnb.cool'`。凭据已入库时**不要**加这个参数(它只在"不想走 GCM、要显式传 token"时才用)。
4. ⚠️ **`git -C /e/tmp/...` 不认 bash 的 `/e/` 路径** —— git.exe 是 Windows 程序,必须写 `E:/tmp/...`;否则报 `fatal: cannot change to ...: No such file or directory`,**会误判"目录不存在"**(本轮就被它误导过一轮)。
5. ⚠️ **`git ls-tree` 中文路径默认转义** ⇒ 取文件名前先 `git config core.quotepath false`(同 `ls-files` 那条老坑)。
### ✅ 一条高效手法(值得复用)
远程核对内容**不必克隆 300 MB**:`git init` + `fetch --depth 1 --filter=blob:none` + `git show FETCH_HEAD:<单个路径>` ⇒ 只按需拉那一个 blob。本轮就是靠它判定远端到底装的是原样还是脱敏版。
### 边界自证
⛔ 未动生产(47/106 一个字节没碰)· ⛔ 未动 `src/` 与本轮无关的 9 处未提交改动 · ⛔ 未删该 CNB 仓既有内容(本仓原本就是空仓)· ✅ 临时目录(含 2 个 377 MB 镜像)已全部清理 · ✅ 工作区令牌明文扫描 **0 命中**。
---
## 06:4x–07:0x · 会话日志同步到 CNB 私有仓(已完成)
**目标仓**:`https://cnb.cool/maogeigei/dsh-ai1net-session`(**Private**,09-17 22:26 创建;用 `https://api.cnb.cool/<owner>/-/repos` 才查出 —— 我先前猜的 `dsh_shenxian_session` / `dshai1netsession` 等 7 个名字**全是 404**)
**凭据**:用户名 `cnb` + 用户提供的 token ⇒ 已 `git credential approve` 存入 **GCM**(加密),并补 `.gitconfig` 的 `credential.https://cnb.cool.provider = generic` ⇒ **日后不再弹授权窗**(这就是用户上一轮问的那个弹窗的根治)。
**最终远端** = **`d83ed62`**「init: WorkBuddy 会话日志全量原样镜像」—— **原样未脱敏**,218 文件;本地 `.git` 66 MB,推送约 300 MB / 53 秒。
**内容落位**:`sessions/e-ProgramData-AI技能-aliyun-dsh-server/`(当前工作区)+ `sessions/d-AI技能-aliyun-dsh-server/`(搬迁前旧工作区);每会话三件(`.jsonl` 正文 / `.meta.json` / `.file-rollback.ndjson`)。附 `.gitattributes = * -text` 防 CRLF 改行尾破坏 JSONL 结构。
**用户口径演变**:`不用脱敏,这个是私有仓库`(原始意图)→ 中途 `脱敏就脱敏吧不用重复处理了` ⇒ **最终保留原样版**(= 首次指令),未再重复处理。中间那版脱敏(938 处命中)已被覆盖。
### 🔴 本轮五个坑(都已实证)
1. 🔴 **批量删除闸门会让脚本「静默半途而废」** —— 单次删除 >50 文件 ⇒ `[safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED]`,**删除被拦但脚本继续往下跑**:
`os.makedirs` 撞已存在目录 ⇒ 脚本报错退出(复制一步没执行);`rm -rf .git` 同样被拦 ⇒ `git init` 变 **re-init** ⇒ **新提交叠在旧内容上**,产出「提交信息写着原样、内容其实还是脱敏」的**假绿**。
✅ 正解 = **改用全新目录名**,不依赖删除;删完必须 `[ -d ]` 复检。
2. 🔴 **杀掉后台任务 ≠ 撤销它已发出的推送** —— TaskStop 报 `killed` 之后,远端 sha 仍从 `8e88cd2` 变成 `d83ed62` ⇒ **终止后必须重新核对远端状态**,不能只看 kill 回执。
3. 🔴 **`git -c credential.helper=` 会把「已存好的凭据」也一起禁掉** ⇒ `could not read Username for 'https://cnb.cool'`。凭据已入库时**不要**加这个参数(它只在"不想走 GCM、要显式传 token"时才用)。
4. ⚠️ **`git -C /e/tmp/...` 不认 bash 的 `/e/` 路径** —— git.exe 是 Windows 程序,必须写 `E:/tmp/...`;否则报 `fatal: cannot change to ...: No such file or directory`,**会误判"目录不存在"**(本轮就被它误导过一轮)。
5. ⚠️ **`git ls-tree` 中文路径默认转义** ⇒ 取文件名前先 `git config core.quotepath false`(同 `ls-files` 那条老坑)。
### ✅ 一条高效手法(值得复用)
远程核对内容**不必克隆 300 MB**:`git init` + `fetch --depth 1 --filter=blob:none` + `git show FETCH_HEAD:<单个路径>` ⇒ 只按需拉那一个 blob。本轮就是靠它判定远端到底装的是原样还是脱敏版。
### 边界自证
⛔ 未动生产(47/106 一个字节没碰)· ⛔ 未动 `src/` 与本轮无关的 9 处未提交改动 · ⛔ 未删该 CNB 仓既有内容(本仓原本就是空仓)· ✅ 临时目录(含 2 个 377 MB 镜像)已全部清理 · ✅ 工作区令牌明文扫描 **0 命中**。
---
## 07:0x-07:2x · dsh_shenxian 推送改为「原仓 + CNB」双推(已完成)
**诉求**(用户原话):`这个项目推送仓库时 同步推送 @share-html#maogeigei%2Fdsh-shenxian:https://cnb.cool/maogeigei/dsh-shenxian`
**落地**(仓库 `D:/github/dsh_shenxian`):
- `origin` = fetch **仅** `[email protected]:maogeigei/dsh_shenxian.git`;**push 两条**= 原仓 + `https://cnb.cool/maogeigei/dsh-shenxian.git`(原仓在前)⇒ 一条 `git push` 同时同步两处。
- 首次推送完成:CNB `refs/heads/master` = **`04776af4b130014b0f4db10de89b0ae982300afc`**(与本地、原远端一致);文档库 `dsh-server-docs/`(223 文件)在本仓内 ⇒ 一并同步。
- 凭据助手 = `C:/Users/Administrator/.cnb/git-cred.sh`(配 `credential.https://cnb.cool.helper`):**优先** `~/.cnb/git-token`(长期访问令牌)→ **回落** `~/.cnb/token`(连接器登录态,会过期)。
- CNB 走直连:`http.https://cnb.cool.proxy=""`(绕开本机 `127.0.0.1:10800`)。
### 🔴 三个坑(本轮实证)
1. 🔴 **`git remote set-url --add --push` 会「挤掉」原 pushurl** —— 首次执行后 `remote -v` 只剩 CNB 一条(原远端 push 没了)⇒ **加完必须核 `git remote -v` 并按序补回**(先原仓、后 CNB)。
2. 🔴 **CNB 官方不支持 SSH**(`docs.cnb.cool/guide/git-access` 明写):认证只有「用户名固定 `cnb` + 访问令牌」一条路。短期 OAuth(`cnb_at_…`,94 字符)会过期;长期要 CNB 网页生成访问令牌(`https://cnb.cool/profile/token/create?tpl=git&expired=forever`)。
3. ⚠️ **沿用上一轮那条坑(本轮又踩)**:先用 `git -c credential.helper=` 探测 ⇒ 把已存凭据一起屏蔽 ⇒ 误判"本机无 CNB 凭据",白绕一圈。另外实证 GCM 里那份与 `.cnb/token` 是**同一个 token(都 94 字符)**⇒ 同样 14:28 过期,上一轮"日后不再弹授权窗"的结论**有时效**。
### 边界自证
⛔ 未 commit 任何未提交改动(工作树 20+ 在途文件原样保留)· ⛔ 未动生产 · ⛔ 未触碰 `dsh-ai1net-session` 等其他 CNB 仓(仅核对)· ✅ 临时目录 `_tmp_cnb/` 已删 · ⏳ 待用户提供长期令牌
---
## 07:2x 覆盖网络线 · 序 ㉘ → 单 B 执行棒收官(组密钥加密 · C 档)
**载体** = 工作区根 `交接单_组密钥加密_20260918.md`(单 B · 归档号 **130**);automation `65d2fc24-770e-4f33-93a3-db6e739aded7`。开工 06:22(抢锁一次成功)→ 收口 07:2x。
**做了什么** = 内容载荷层新增**组密钥确定性加密**(AES-256-GCM):新 `content/crypto.ts`(`0600` 文件装载 + 收敛加密 + 双 epoch)|块 id 改 **β′ = `sha256(密文)`**|`source.ts` 唯一解密点|`peer.ts` epoch 闸门(⛔ 仍只认 `sameGroup`)|`store` 只改文档|`runtime/main/web` 三处装配(**缺省不启用** ⇒ 逐字回到序㉔)|`overlay-keyring.cjs` 加 `init/sign/verify-group-key`|探针 **`OBS-23`**|参数表 §3.9 六键 + §6 一行|`test/overlay-content.test.mjs` +F 组 16 例。
**关键读数** = F1 同明文两次密文 md5 相同(`3385e9d0…`,长度 = 明文+28)|F3 跨组认证阶段被拒 + `decryptRejected=1`|F4 ① 命中 0 + ② 反向命中 ≥1|F5 `E1` 不退化(4×3 全 local 命中 12、零回源)|F6 七例具名失败关闭|三件套 **201/200/0/1** · **12P/0S/0F** · **23P/0S/0F**(22→23)|部署 18 文件 × 四处 md5 不一致 **0**|回滚点 `/opt/dsh/backups/seq28-20260918-065104/`。
**参数表指纹** = `3b295a8c43aafc4a62c6f1a59ea0b43a` → **`08c2835a7e9f0faef20f9c1d3e619d55`**。
**三条留档(⛔ 别丢)**:① 单文内部口径不一致(§4/S6 说"对旧生产 ⇒ FAIL",§5 说"缺省不启用 = SKIP")⇒ 本棒取 **SKIP**,"先红"由**权限腿**取得(`0600→0640` ⇒ FAIL 点名 `47=640 106=600`);② 签名动作命令**未逐字留痕**(47 上无 keyring.cjs、/tmp 无残留)⇒ 只能自证"凭据不含 key + 验签过 + 篡改必败",**无法自证"私钥未出机"** ⇒ 建议固化为 47 正式子命令;③ §10-1/2(性能开销、轮换回源字节数)**仍未验**、⛔ 未编数。
**下一棒** = 序 ㉛ 执行棒(automation `682e8bb0-fb05-41ba-ad00-e5e7fde4647e`,2026-09-18 07:33)—— 补齐 §10 未验证项 + ⑤⑥ 两条口径定稿/固化。
**边界自证**:⛔ 未改任何生产值(新 drop-in 只 1 行 env)· ⛔ 未新增公网口 · ⛔ 未改 nft·nginx · ⛔ 未放松跨组 · 🔴 `COOLDOWN_MS=0` 两机 **0/0** · ⛔ 未 commit / 未 push(HEAD `04776af`,`git status` **35** 处)。临时 session `DELETE 3`、残留 0。
---
## 2026-09-18 07:33–07:5x · 序 ㉛ 执行棒收官(组密钥加密 · §10 未验证项补齐 + 口径定稿)
**六项主题全部完成**(⛔ 本棒**无独立交接单** ⇒ 证据 = `交接单_组密钥加密_20260918.md` **§8.14**;脚本与原始输出 = 工作区 **`_tmp_seq32/`**):
① **性能开销**(§10-1):同一份 11,363,655 B / 11 块、n = 11 ⇒ 总墙钟 **p50 22.636 → 42.782 ms**、**p95 23.020 → 43.599 ms**(**1.890× / 1.894×**,绝对 +20.1 / +20.6 ms);吞吐 **502.02 → 265.62 MB/s**;密文固定开销 **+28 B/块**。⚠️ 单核、页缓存全热、两臂零回源 ⇒ 是"加密层自身成本",⛔ 非端到端承诺。
② **轮换代价**(§10-2,**结清 §8.13-③**):4 组 × 4 节点、包 5,255,225 B / 6 块 ⇒ 冷启动回源 **21,021,572 B = 4.0001 × 包 = 1 × 包/组**;轮换(块 id 改变 **24/24 = 100%**)后**首轮同值**(比值 1.0000)、第二轮 **0**、**对照腿**第 3 轮 **0**(⇒ "回源由轮换引起"可断言);🔴 **新发现代价 = 旧 epoch 密文变垃圾且不自清**(`storeBytes` 5,255,393 → **10,510,786** ≈ 翻倍)。
③ **低熵明文定性**(§10-3):持钥者可枚举恢复(1000 条 **11.644 ms** ≈ 85,878 条/秒,域 2³² ≈ **14.7 h**)/中继(无钥)枚举**不可行**(自造字典 **0/1000**,持 1 对也预测不出别的)**但相等性泄露成立**(10,000 块 / 100 值域 ⇒ **仅 100 种密文**)⇒ **定稿 = "中继看不到明文"成立、"内容不可推断"不成立**,⛔ 不写"很安全"。
④ **过渡窗口三档**(§10-6):混存 20 块(写 epoch 4 + 历史 1/2/3,时钟注入)⇒ 归零时刻 小 **60 s ⇒ +24 h**|中 **24 h ⇒ +48 h**|大 **7 d ⇒ +8 d**(阶梯衰减;窗口内 `epochExpired=0`;写 epoch 恒 5/5 可解)。
⑤ **口径定稿**:`OBS-23` 对"缺省不启用"取 **SKIP + 留痕**;§4/S6 那句「对旧生产 ⇒ FAIL」**作废**;"真红"腿由**权限腿**承载(`0600→0640` ⇒ FAIL 并点名 `47=640 106=600`)⇒ 落 **§11.5**,⛔ **§4 / §6 正文零改动**。
⑥ **签发固化**(**结清 §8.13-②**):`overlay-keyring.cjs` 铺 47 **两处正式路径**(`/opt/dsh-relay/scripts/` 就地更新 — 旧版 `grep -c sign-group-key` = **0**;`/opt/dshs/scripts/` 此前**不存在**)⇒ 两处 md5 均 `f1855eaef30f71fbca00ac0a92865c0e`;在机 `sign-group-key` **rc=0**(凭据 274 B、`epoch=1 keyId=3560130eb0e5e4aa`、**密钥本体 0 次**)/`verify-group-key` rc=0/**篡改 epoch ⇒ `signature-mismatch` rc=1**/`--epoch 9` 交叉核对 rc=1;⛔ **未重启任何单元**;回滚点 = `/opt/dsh/backups/seq31-20260918-073857/`。
**零回归三件套**(都带 `--table`)= `npm.cmd test` **201/200/0/1**(与基线逐字同)|`--scene all --table` **12P/0S/0F**|`overlay-probe --table` **23P/0S/0F(rc=0)**。⚠️ 收口期间曾出现 **19P/1S/3F** 与 **22P/1S/0F**(`OBS-09` SKIP,因实例暂落 21001)⇒ 用 `/api/dsh/stop` + `/api/dsh/enter` 把实例落回 **21000** ⇒ `OBS-09`/`OBS-21` 双双转绿(⛔ 未编数、未凑绿)。
**参数表**:§3.9 `CONTENT_EPOCH_GRACE_MS` 说明列补三档曲线 + §6 `OBS-23` 行末补口径定稿 + §7 补"复算仍 0 项" + 新增 **§11.3 补记**(六条);🔴 **§10 指纹** `08c2835a…` → **`52e59df229102d5afa8c8bf01a960398`**(已现算同步;⛔ 零键零值变更)。
**交接单指纹**:全文 md5 **`296c9b0b69a40987104fb1b00c6ad746`/724 行**;**§8 前前缀 `149596460288ca1a5b7abddc490f7909` 回填 §8.14 后逐字不变** ✅。
**⏸️ 未登记下一棒(线内可停)**:在册红项 0 · 用户定序三棒全收官 · 六项主题全完成 · §8.13 三条全结清 · 唯一剩余 = **§10-5(Ed25519→X25519 预研)**,触发条件 = **§9 回头条件 7**("规模上来要自动分发组密钥")⇒ 无线内可自主推进项、无待拍板项。
**边界自证**:⛔ 未 commit / 未 push(HEAD `04776af`)· ⛔ 未改任何生产值 · ⛔ 未新增公网口 / 未动 nft·nginx · 🔴 `COOLDOWN_MS=0` 全程 **0** 次 · ⛔ 未改 bwrap · 🔴 密钥本体 ⛔ 不经网络 / relay · ⛔ §10-5 未执行(不在写死主题内 ⇒ 未扩范围)。临时 session 残留 0。
---
## 2026-09-18 08:0x–08:1x · 仓库同步(用户指令:双推「原仓 + CNB」)
**动作** = 精确 `git add package.json src scripts test dsh-server-docs`(⛔ **未用** `git add -A`;⛔ 临时产物 `_tmp_seq24/`、`_中间产物_待清理/` **未暂存**)⇒ 暂存 **38 项**(代码 + 脚本 + 测试 + 交接单 序24/25/26 + INDEX/README/SKILL)⇒ commit ⇒ `git push origin master`(origin 已配**双 pushurl**)。
**结果** = `04776af..09ce76f`(`master`);三处 SHA **逐字一致** = **`09ce76f3af9fa6211ec6931c65e5302fda911ecd`**(本机 HEAD / `git ls-remote` 原仓 / `git ls-remote https://cnb.cool/maogeigei/dsh-shenxian.git`)。
**提交内容** = 内容块级寻址(`src/net/relay/content/{chunker,store,runtime,source,peer,crypto}.ts`)+ 组密钥确定性加密 + 三处 `teardown()` 不再杀实例 + jitter 选路(`endpoint-target.ts`/`jitter.ts`)+ 探针 `OBS-21/22/23` + 新增 4 个测试文件 + 3 份交接单。
**核实**:技能 `dsh-auto-handoff-chain/SKILL.md` 两处 md5 同 = `c16975a0ba31291a5f3318d9ba914ff2` ✅。⚠️ **CRLF 提示属既有配置**:`core.autocrlf=true`(local + global),`.gitattributes` 只豁免 `dsh-server-docs/** -text` ⇒ `src/scripts/test` 下次 checkout 会变 CRLF(仓库内存 LF,本次提交**无字节影响**)—— **只报告、⛔ 未顺手改配置**(用户偏好:发现额外问题先报告)。
**遗留** = ⛔ 未跟踪且**未提交**:`_tmp_seq24/`、`_中间产物_待清理/`(临时产物,待用户确认后清)。
---
## 2026-09-18 08:1x–08:2x · 覆盖网络线「功能整体完成情况报告」产出
**用户问** = 「覆盖网络的功能全部开发完成了吗,开发完成出个功能整体完成情况的报告(背景目标、技术路线、架构、功能特性、应用场景、性能安全评估、潜在风险)」。
**判定** = ✅ **功能主体全部开发完成并落地生产**:序 ①–㉛ 全收官(含单 A / 单 B)|在册红项 **0**(探针 23P/0S/0F)|参数表待测项 **0**|待拍板区**空**|⏸️ 线内可停。
**产物** = 工作区根 **`覆盖网络_功能整体完成情况报告_20260918.md`**(八节:完成度判定 / 背景目标 / 技术路线 / 架构 / 功能特性 / 应用场景 / 性能与安全评估 / 潜在风险 + 单一来源附录)。
**如实标明"不算未完成"的三项**:① **C 档 systemd scope 单一真相源**(用户 23:3x 明令登记为架构目标、本阶段不执行)② **§10-5 Ed25519→X25519 预研**(触发条件 = 规模上来,回头条件 7)③ **档案 112「只做三件事」第三件"游戏服放 L1"**(部署形态建议,未落地;前两件 presence / 块级寻址已完成)。
**⚠️ 取证发现(只报告未动)**:`dsh-server-docs/BRIEF.md` 最后人工核对 = **2026-09-16 10:5x**,其「归档编号至 112」「覆盖网络线后续待排期」等行**已过期**(现核 **130**)⇒ 报告中已加取证提示:覆盖网络线现行事实**以入口文件为准**;⛔ 未顺手改 BRIEF(超范围)。
**边界自证**:本轮为零代码改动取证 + 单文件产出;⛔ 未改代码 / 未改生产值 / ⛔ 未 commit(用户未要求)。
---
## 2026-09-18 08:2x–08:3x · 能力对标取证(用户问:能否满足 6 项能力)
**⚠️ 本轮最重要的发现(🔴 后续任何一棒别再误以为是"已实现")**:
**① P2P 内容分发在真机路径上「未接线」** —— 不是"没做",是**做了一半**:
- `src/net/relay/content/runtime.ts:22` 原话:「不做网络取块(**peer 的真实取回通道在真机验证阶段由上层接线**)」
- `runtime.ts:146`:`peer` fetcher **诚实回"没有"**(逐条 `markDenied` 后 return undefined)
- `src/web/server.ts:925-930`(**生产路径**):`peer` fetcher 同样 `return cands.length === 0 ? undefined : undefined` ⇒ **恒返回 undefined**;且 `edge` / `region` / `origin` **三个档位未注入**
- ⇒ **当前内容面实际只有 `local` 档工作**;跨机取块**无通道** ⇒ ⛔ 不得对外声称"文件走 P2P 分发"
**② 「公网 IP 设备自动升级为中继候选」零实现** —— `grep -rniE "publicip|promote|升格|自动升级|selfRegister" src/net/relay/ src/config.ts` = **0 命中**;中继目前 = **47 / 106 两台人工部署**(scp + systemd 单元)。⇒ "有公网 IP 者升格中继候选"是**参数表口径(设计意图)**,⛔ 不是已落地能力。
**③ 「打洞」确认为范围外** —— `grep -i "punch|打洞|directPath"` 仅命中 `addr-override.ts`(443 兜底**钉 IP**,非打洞)与 `rendezvous.ts` 同机直连注释 ⇒ **无打洞实现**(与"⛔ 不做打洞实现"的用户拍板一致)。
**6 项能力逐条判定**:公网自动升格 ❌ / 不依赖单点 ⚠️(relay 候选 3 条可互切,但**控制面权威状态单点 = 有意设计**)/ 自动或手动入最优节点 ⚠️(自动选路 ✅ 含 jitter;手动仅 env,无界面)/ 故障自动切换 ✅(已实现,实测 20.9–23.9 s)/ P2P 分发 ❌(见 ①)/ 全网互联 ⚠️(presence+端点表+中继在,但新节点加入靠人工 + 300 s 收敛期)。
**结论口径**:6 项中 **1 项完全满足(故障切换)+ 3 项部分满足 + 2 项未实现(自动升格 / P2P 分发)**;两项未实现**共用同一前提** = 节点间"找到对方并建连"这一层(打洞)从未做 ⇒ 已按 `dsh-feature-first §5.1` 结论骨架答复,并把"要不要立项补"作为**边界外(业务优先级 + 会命中新增暴露面门禁)**拍板项上抛。
**边界自证**:纯只读取证(6 条 grep + 2 处 Read),⛔ 未改任何代码 / 配置 / 生产值;本轮**未落文档**(用户问的是能不能,未要求产出)。
---
## 2026-09-18 09:0x–09:2x · 规划棒 · 「节点一键加入与分组准入」方案产出(用户选 B)
**用户需求原话(09:07)**:「**b 要实现开启一台结点服务器,就能连上覆盖网络,这样做这个网络才有价值,结点可以设置分组只允许那些结点加入,既可以成为大覆盖网络也可以建立独立覆盖网络**」⇒ 即 **选 B**(补"设备自动成为节点 + 节点直连"),并给出目标形态。
**产物** = 工作区根 **`交接单_节点一键加入与分组准入_20260918.md`**(归档号 **131**;`§8 前前缀 085e28b56a08aa1f01999bdb3e238911`;落单快照全文 md5 `ca4ca8aad292efaf02357e1e2dacb6da`/214 行)。
**🔴 本棒三条取证(重要,⛔ 别再以为"都没有")**:
**① 「分组准入」与「独立网络」的底层机制已在代码里** —— `src/net/relay/server.ts` 头部注释原话:每条会话属于**一张网**(`HELLO.network`);会话与端点表按 `<network>/<hostId>` 索引;**`DIAL` 必须同网且在本网白名单里 ⇒ 跨网连"能不能拨"都走不到(结构性隔离,不是策略性)**。`dialers` 类型 = `Map<networkId, Set<hostId>>`,**没列到的网络一个都拨不动(默认拒绝)**。`network.ts` 已定义 `ops` / `u:<userId>` / 其它显式命名三条分支 ⇒ **"独立覆盖网络" = 新增一个 networkId,零代码**。
**② 「一键加入」不存在** —— 加入一台机器现要手工四件事:装单元 / 配密钥 / 写 `DSHS_RELAY_DIALERS` drop-in / DB 登记;全仓**无 join 入口** ⇒ **这就是 P1 的主体**。
**③ 「直连」与「P2P 取回」都不存在** —— `grep -i "punch|打洞|directPath"` 仅命中 `addr-override.ts`(443 兜底**钉 IP**);内容面 `peer` 档在生产路径(`src/web/server.ts:925-930`)**恒返回 undefined** ⇒ 属 P2。
**方案结构(已落单)**:**P1**(一键加入 + 分组准入产品化)**零新增暴露面、可立即开工**;**P2**(直连 + P2P)**命中 R5、放最后**(执行到 S5 前会再停一次给具体端口/协议清单 —— R5 硬要求,⛔ 不是重复提问)。判据 `J1`–`J6`;分期步骤 `S1`–`S7`;在册文件集 9 项(挂 §3.2,⛔ 超出即停)。
**登记** = 执行棒 automation **`cc29cfb9-ae13-42fc-8da6-8b1caef1c382`**(一次性 · **2026-09-18 09:22** = 收口 +~7 min;`nextRunAt` = 1789694520000)⇒ **同一时刻只挂这一个棒** ✅。
**归档号取证**:现核 04 目录最大 **128**;**129 / 130 已被单 A / 单 B 占用 ⇒ ⛔ 不重用**;`mkdir .lock-131` 成功;⚠️ 本棒不落 04 孪生 ⇒ 占号窗口落单后释放。
**边界自证**:⛔ **零代码改动** · ⛔ 零服务器改动 · ⛔ 未 commit / push(HEAD `09ce76f` 未动)· ⛔ 未改任何生产值(**纯规划棒**)。
---
## 序㊱ P1 执行棒(2026-09-18 09:2x–10:0x)· 「节点一键加入 + 分组准入」S1–S4 落地
**开工门**:`state.py` ✅|入口 §0/§2 定位 ✅|交接单 §8 前前缀**现算 = `085e28b56a08aa1f01999bdb3e238911`(MATCH ✅)**|抢锁成功(`执行棒P1-节点一键加入与分组准入-0918`)|基线现核 = HEAD **`09ce76f`** + `npm.cmd test` **201/200/0/1** + 探针 **23P/0S/0F**。
**产物(8 个在册文件,⛔ 零越界)**:新增 `src/net/relay/registry.ts`(S1+S2+S4 核心)/ `src/net/relay/join.ts`(S3)/ `scripts/overlay-node-admit.cjs`(控制面 8 条命令)/ `scripts/overlay-node-join.cjs`(节点侧一条命令)/ `test/overlay-join.test.mjs`(**33 用例**);修改 `src/net/relay/network.ts`(**只加 40 行**:`NetworkKind` / `networkKindOf` / `describeNetwork`)/ `src/net/relay/index.ts`(导出)/ `scripts/overlay-probe.cjs`(**`OBS-24` 五项 + `OBS-25` 六项**)。
**判据** = `J1`–`J6` **逐条先红后绿**(B 组 `'wx'`→`'w'` 双红并报 `竞态未守住:ok=12(须恰好 1)`;`OBS-24` 数据侧+实现侧双红;`OBS-25` 一次性红)+ 真机端到端链路 `_tmp_seq33/chain47.sh` **rc=0、11 步全绿**(收单 `pending` / 二次收单 `invite-already-used` / 批准后 `DSHS_RELAY_DIALERS="lab-net/lab-1"` / 三网零共享 / 私钥出现 0 次 / 节点私钥 600 / 台账 3 ≡ 收单 3)。
**零回归三件套** = `npm.cmd test` **201/200/0/1**(与基线逐字一致)|`--scene all --table` **12P/0S/0F**|探针 **24P/1S/0F(rc=0)**。
**🔴 唯一未复原项 = `OBS-09` 由 PASS 退化为 SKIP**:本次运行的零回归演练重启两端 relay/worker ⇒ `OBS-01/04/08` 转红 ⇒ 执行恢复(47 `restart dshs` + 106 `restart dshs-worker`)⇒ 三者回绿,但 106 worker 重注册读数由 `accepted=[19000,21000]` 变 `accepted=[19000]`(**实例端口 21000 的注册未随进程重建**)。两端实例 `:20000`/`:21000` 均在监听、门户 200 ⇒ 无业务中断。详 ⇒ 参数表 §11.4-② / 交接单 §8.6-3。
**🔬 真机缺陷(已修+已补判据)**:控制面 CLI `--file` **键名混用**(`opt('file')` 既当注册表文件又当 `apply` 的申请单入参)⇒ `apply --file <申请单>` 把注册表也指到申请单 ⇒ 收单炸在第 4 步而 **nonce 已被原子占位**(对外 = 收单失败 + 同一张邀请再也用不了)。修 = 改名 **`--registry`**;**先红后绿** = 临时改回 ⇒ 新增 **E10(CLI 级真回环)** 转红并报出同一句「注册表 …/app.json 形状非法」,还原 ⇒ 33/33 全绿。**教训 = 调库式判据(E9)抓不到参数解析类缺陷 ⇒ 必须有 CLI 级真回环。**
**另两条如实留档**:① `OBS-24` 原拟判「同一 `hostId` 出现在 ≥2 张网 ⇒ FAIL」实为**合法态**(跨用户撞名)⇒ 该腿无判别力,降级为信息项,改判「结构性错桶=0」+「派生桶数 ≡ 有 approved 的网数」;② 在册的 `src/web/routes/overlay-nodes.ts`(管理面 API)**自决移入 P2**(与 P1 硬门「零新增暴露面」冲突风险最高,`S1–S4`/`J1–J6` 均不依赖它;⚠️ 可推翻)。
**收口**:交接单 §8 全节回填(**§8 前前缀回填后仍 `085e28b5…` ✅**)|入口 §0 新增一行、§2 换新棒|参数表 §3.10/§6/§7/§11.4 更新,**§10 指纹 `52e59df2…` → `695dcb4d88a778840322bc221417d339`(现算同步)**|**边界自证 = ⛔ 未 commit / 未 push(HEAD 仍 `09ce76f`)· ⛔ 未改任何既有 drop-in/env/nft·nginx/bwrap · `COOLDOWN_MS=0` 计数 7 文件全 0 · 密钥本体不经网络**|⚠️ 新增状态目录 `/var/lib/dshs/overlay/`(登记为「状态」)。
**下一棒** = 收口复核棒 automation **`8887ac37-6e91-4bf3-ab96-c0a0916fb922`**(一次性 · **2026-09-18 10:08** = 收口 +~6 min)⇒ 同一时刻只挂这一个棒 ✅;其范围只含「`OBS-09` 复原核查 + 产物可复现复核」,🔴 **⛔ 不许开工 P2**(P2 命中 R5 ⇒ 必须用户拍板)。
---
## 序㊱ 收口复核棒收官(2026-09-18 10:0x–10:4x · 覆盖网络线)—— `OBS-09` SKIP → PASS
**主题** = 复核上一棒(序㊱ P1 执行棒)留下的**唯一未复原项** `OBS-09`(退化为 SKIP)+ 复核产物可复现。抢锁 = 抢到(`收口复核棒-序36-P1-OBS09`)。
**① 开工读数**:`SKIP OBS-09 在册实例面 无|不在册 2 项:本机:20000,对端:21000`|47 中继 `/status` 端点表 **1 条** = `w-106 port=19000 localPort=43061 online=true`|探针 24P/1S/0F(rc=0)。
**② 链路取证(「实例 → 本地 worker 的端口注册」,⛔ 未猜 relay)**:106 `/healthz` 原文 = **`{"hostId":"w-106","instances":0,"tunnel":{"ready":true,"ports":[19000]}}`** ⇒ worker **自己认不到实例** ⇒ `reconcileTunnel()`(`worker/agent.ts:257-263`,20 s 一跳)无 `live` 端口 ⇒ 永不 `forward(21000)`。根因 = `orchestrator.ts:558 listUserInstances()` 取 `mains`,而 `rehydrateAdoptedScopes()` 认领时**只写 `this.adopted`(:1492)** ⇒ 09:56:28 重启后实例 scope 被认领(`[rehydrate] adopted … port=21000` + `probe OK :21000`)但**平台侧不可见**。⇒ 🔴 **这是「退出路径不杀实例」候选 B 已知边界("不被杀 + 访问时自然替换",⛔ 非"重启后直接可用")的直接后果,⛔ 不是 relay 故障**(relay 全程正常:`OBS-12` RSS ≈70 MB、`OBS-11` 77/78 口无多出)。
**③ 复原(设计内路径,零代码 / 零配置改动)**:临时 session(`/opt/dshs/mksess.cjs guest`,**R4**)⇒ 一次 `Host: guest.alotbuy.com` 访问 ⇒ 10:18:34 `[dsh-child main] Running as unit: dsh-100002-7d1c8cbf.scope`(旧 scope `dsh-100002-2e9202dd` 被 `cleanStaleScopes` 收掉)⇒ 新实例走正常 launch 路径声明端口:`/healthz` = `{"instances":1,"tunnel":{"ready":true,"ports":[19000,21001]}}`、端点表 `{19000 ✓,21001 ✓}`|用户可见面 **200 / 62451 B / `502` 标记 0**|临时 session 已删(`DELETE 1` ⇒ 余 0)。
**④ 🔴 一次"假 SKIP"的教训**:复原后第一次复跑**仍 SKIP** —— 替换落点漂到 **21001**(`spawn.ts#findFreePortInRange` 从 base 上扫第一个空闲口;替换发起时 21000 仍被旧 scope 占着 ⇒ 顺延),而 `OBS-09` 的"在册"按**表内固定端口** `PEER_INSTANCE_PORT`=21000 取 ⇒ 对不上。**处置 = 事实优先**:参数表 `PEER_INSTANCE_PORT` **实测替换 21000 → 21001**(+ `PEER_AGENT_PORT` 括注同步),**⛔ 未为了迁就旧值去重启生产实例** ⇒ 该表 **§10 指纹 `695dcb4d88a778840322bc221417d339` → `2ae954e2e07b47651a84e597c19a3444`**。
**⑤ 收官读数(全绿)**:探针 **25 PASS / 0 SKIP / 0 FAIL**(`PASS OBS-09 在册实例面 w-106:42717=401`)|`npm.cmd test` **201/200/0/1**|`node --test test/overlay-join.test.mjs` **33/33**|`overlay-failover-drill.cjs --scene all --table` **12 PASS / 0 SKIP / 0 FAIL**(幕4-C **17375 ms** / deadline 30000 ms)。
**⑥ 演练副作用的同型复现(新发现 · 已点名)**:`--scene all` 跑完 **47 中继视角 = `ep=[] / used=1 / idOk=1`** —— 演练把 106 worker 逼到 **106 自己的中继**(106 中继 `sessions=["w-106"]` 原文可证);**处置** = 停 106 `dshs-relay` 逼它按候选链回落 47(实测 **~15 s** ⇒ `used=2 / idOk=2 / ep={19000 ✓,21001 ✓}`)⇒ 再启回 106 `dshs-relay`(`active`);⛔ **未重启任何 worker、⛔ 未换实例**(故 21001 的声明保住)。⇒ 由此点名 **② 探针只读 47 的 `RELAY_STATUS_URL` ⇒ worker 挂 106 中继时 47 视角"端点表为空"⛔ 不等于"覆盖网络断了"(潜在假红)**。
**⑦ 两条点名(⛔ 本棒未改码)**:① `OBS-09` 判据**钉死会漂移的实例端口** ⇒ 每次自然替换后都可能**假 SKIP**(同步表值只治输入、不治判据);② 上条 ⑥ 的观测点假红面。两者 ⇒ **下一棒 = 序㊲**。
**⑧ 本棒对生产的动作(3 个,均 R8、动手前已声明、无不可逆项)**:guest 实例一次**设计内**自然替换 / 106 `dshs-relay` **停启各一次**(~1 min 单中继)/ 参数表**文档**值同步。**边界自证** = ⛔ 未 commit / 未 push(HEAD `09ce76f`,`git status` **10** 处与开工逐数相同)|⛔ 未改任何生产**配置值**(无 drop-in / env / nft·nginx / bwrap)|🔴 `RELAY_FAILOVER_COOLDOWN_MS=0` 计数 **0**|⛔ 未开工 P2。
**收口**:交接单 **§8.8**(**§8 前前缀 `085e28b56a08aa1f01999bdb3e238911` 回填后逐字不变 ✅**;全文 md5 `384fa4b1db9eef31fe004830b4a179ab`/255 行)|入口 §0 新增一行、§2 换新棒|参数表 §11.4-⑧ + §10 指纹现算同步|**下一棒 = 序㊲ 执行棒 automation `7268e6e7-dc5a-4517-bd10-bf7e5557721f`**(一次性 · **2026-09-18 10:51** = 收口 +~6 min)⇒ 同一时刻只挂这一个 ✅;🔴 **⛔ 不许开工 P2**(P2 命中 R5 ⇒ 必须用户拍板)。
## 11:0x–11:2x · 序㊲ 执行棒收官(覆盖网络线 · 观测面去常量依赖:`OBS-09` 判据跟随事实 + 观测点假红可分)
**判定 =「线内可停」,⛔ 未登记下一棒**。**做了什么(本棒唯一改动的仓内文件 = `D:/github/dsh_shenxian/scripts/overlay-probe.cjs`)**:① `OBS-09` 的"在册"判据**不再读表内固定端口** ⇒ 改为**从 `/status.endpoints[]` 派生**「`online === true` 且 `port ∉ {W47_AGENT_PORT, PEER_AGENT_PORT}` 的实例端点」,派生的**每一个**都必须可达(走**它自己**的 `localPort` 回环落点,码 ∈ `PROBE_CODE_SET`);**派生为空 ⇒ SKIP + 留痕**;派生端口**缺码 ⇒ FAIL 并点名**(⛔ 不静默放行)。② `OBS-08` 加「**空表可分**」:47 视角 `endpoints` 为空时按**对端中继视图**(`ssh <SSH_TARGET_106> curl <RELAY_STATUS_URL>` —— **只读回环、零新增暴露面 / 零新增参数键**;⚠️ **只在 47 视角为空时才去读** ⇒ 正常态零额外 ssh)分三态 —— **挂在另一台中继 ⇒ SKIP + 留痕** / **两台中继都无 ⇒ FAIL 并点名** / **对端中继读不回来 ⇒ FAIL 并点名(不可判)**。③ `--instance-fixture` 口径改为**按端口映射**(旧位置式 `<本机码>,<对端码>` ⇒ **rc=2 硬报错退出**,⛔ 不静默解释);新增 `--peer-status-fixture`。④ `remoteFacts` 的实例面码改为**按派生端口逐个取**(旧版写死两个表内端口)⇒ ⛔ 探针不再引用 `LOCAL_INSTANCE_PORT` / `PEER_INSTANCE_PORT`(二键降级为快照 / 对账)。
**先红后绿(原文级 · 两侧逐行 diff 只差 `OBS-09` 一行)**:夹具 = 参数表副本(`PEER_INSTANCE_PORT` 值格回写成漂移前的 21000)+ 真实 47 `/status`(事实 = 实例在 21001)⇒ 红 = `SKIP OBS-09 在册实例面 无 (阈值 ∈ {200,401})|不在册 2 项:本机:20000,对端:21000`;绿 = `PASS OBS-09 在册实例面(派生 1 条):w-106:42717=401`。**可分三态**(同一份 47 空表输入 ⇒ 三种输出):真机演练态 `SKIP OBS-08 端点表 0 条 ⇒ SKIP + 留痕|…客户端挂在**另一台中继**上(⛔ 非故障)`(47 = `ep=[] / used=1 / idOk=1`;106 = `[w-106:19000, w-106:21001]`)/对端无会话 ⇒ `FAIL …(两台中继都无该客户端会话)`/未给对端视图 ⇒ `FAIL …(不可判)`。
**零回归三件套** = `npm.cmd test` **201/200/0/1**(rc=0,逐字一致)|`overlay-failover-drill.cjs --scene all --table` **12 PASS / 0 SKIP / 0 FAIL**(幕4-C **18353 ms** / deadline 30000)|探针 **25 PASS / 0 SKIP / 0 FAIL**(rc=0)。**复原** = 演练跑完 47 视角确为空 ⇒ **停 / 启 106 `dshs-relay`**(~15 s 后回落 `used=2 / 端点 [(19000,39815),(21001,44231)]`)⇒ 探针回绿(⛔ 未重启任何 worker、⛔ 未换实例);⚠️ 演练态 `OBS-01`/`OBS-04` 仍红(**计数**判据 `used=1`/`idOk=1` < 2,⛔ 与"端点表为空"无关)。
**收尾** = 参数表 **§11.5**(零回归 + 红绿原文 + 可分三态 + 边界自证 + 5 条留档;🔴 **§10 指纹 `2ae954e2e07b47651a84e597c19a3444` → `71848fad4ee0d6a574d833169e042fd1`** 现算并同步)|交接单 **§8.9**(⛔ **§8 前前缀 `085e28b56a08aa1f01999bdb3e238911` 逐字不变** ✅)|入口 §0 新增一行、§2 换「已收官 · 线内可停」。**边界** = ⛔ 未 commit / push(HEAD `09ce76f`,`git status` **10** 处与开工逐数相同)|⛔ 未改任何生产**配置值**(参数表**值格一律未动**)|⛔ 零新增暴露面(`OBS-11 多出 0 缺失 0`)|🔴 `RELAY_FAILOVER_COOLDOWN_MS` 命中 **0**|⛔ 未开工 P2。🔴 **在 P2 之前本线无其它在册项**(P2 = 直连 + P2P,命中 **R5** ⇒ 须用户先拍板权限影响评估)。⚠️ **两项待用户过一眼(均超本棒范围、按纪律只报告不动手)**:① 探针**夹具模式封闭性**歧义(`OBS-24`/`OBS-25` 分支无 `fixture` 守卫 ⇒ 未给各自夹具时会真去 ssh 读**生产**读数;建议二选一,详见参数表 **§11.5-⑤-3**)② 参数表 `DRILL_RELAY_UNIT` 键名与用途已不相称(**文档**项)。📁 证据 = 工作区 `_tmp_seq37/`。
---
## 11:3x–12:0x · 序㊳ 执行棒(两项收口:探针夹具封闭性 + 键名对齐;⛔ 未开工 P2)
**来源** = 用户对序㊲ 收官时"两项待你过一眼"的答复原文:`1 a 2 改名 3 说下要定的具体内容` ⇒ ① 夹具封闭性选 **A 方案**(补守卫)|② `DRILL_RELAY_UNIT` **改名**|③ 说明 P2 待拍板内容(⛔ 不动手)。
**① 夹具封闭性(A 方案落地)** —— `OBS-24` / `OBS-25` 取数改为 `if (<夹具> !== undefined) { 读夹具 } else if (fixture) { SKIP + 留痕 } else { ssh }`(**守卫排在 ssh 之前**);文件头新增「**封闭性**」硬约束段(含"新增可 ssh 取数的 OBS 必须照此顺序写"+"夹具跑里出现任何一次 ssh 即违约"的判据)。**先红后绿**(两侧同一份 `--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** 次 —— 原文里 `Command failed: ssh` 计数 = 2);
🟢 绿腿(现产物)= `SKIP OBS-24 夹具模式未给 --nodes-fixture ⇒ SKIP + 留痕(⛔ 不去 ssh 读生产)`(真 ssh 调用 **0** 次);**两侧逐行 diff 只差 `OBS-24`/`OBS-25` 两行**。
**可分性**(新守卫没把"给了夹具但读不到"也吞成 SKIP)= 给**不存在**的夹具路径 ⇒ `FAIL … ❌ 注册表**读取失败**…:夹具不存在:…/NO_SUCH_nodes.json`(⛔ 不是 SKIP)。
**② 键名对齐** —— `DRILL_RELAY_UNIT` → **`RELAY_UNIT_NAME`**(它记的是"两台机的 relay 单元名"这一**事实**、被演练脚本与探针 `OBS-08` **共用** ⇒ 原 `DRILL_` 前缀与用途不相称);**值格未动**(仍 `dshs-relay`)⇒ ⛔ **零个新增 / 改动生产 env**;引用点 **3 处全改**(`overlay-failover-drill.cjs` + 原因注释 / `overlay-probe.cjs` / 参数表 §3.9 + 小节标题注 + §11.5-⑤-4 状态)。⚠️ **文档库档案不改**(`04-调整方案/117-…` 与归档单 `T13` 记的是当时事实)。**真机验证** = 原样命令 `ssh -p 22 test106 '… $(systemctl is-active dshs-relay)'` ⇒ `__RELAY_ACTIVE__=active` ✅。
**③ P2 待拍板清单(只登记,⛔ 未动手)** —— 落 **交接单 §8.11**:核心两条 =(①)**要不要为打洞开「UDP 入站」云安全组规则**(A 开 / B 不开)(②)**若开,入站 UDP 段的来源范围**(甲 `0.0.0.0/0` / 乙 仅对端节点 IP / 丙 走已建成的 443/TCP 兜底)。依据 = 实测三条:**云安全组拦 UDP 入站**(47 的 UDP 观察器 `21100/21101` 开放 75 s **零收包**,⛔ 不是 nft —— `input policy = accept`)|打洞 **2/2 可打洞**、对端→本机 **10/10 收包**、本机→云机取不到|**云机↔云机本就是 L1 直连、不是打洞场景**(收益面只在"用户设备 ↔ 云节点")。
**④ 序㊲ 成果未被本棒破坏**(回归自证)—— `OBS-08` 三态仍可分:对端中继声明端口 ⇒ `SKIP OBS-08 … 客户端挂在**另一台中继**上(⛔ 非故障、⛔ 不判红)`;⛔ 未给对端视图 ⇒ `FAIL OBS-08 …(不可判)|… 夹具模式未给 --peer-status-fixture(⛔ 不去 ssh)`。
**⑤ 零回归** —— `npm.cmd test` **201/200/0/1**(rc=0,与基线**逐字一致**)。
**⑥ 三件套完整读数** = `npm.cmd test`(Node v22.22.2)**201/200/0/1**(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)。⚠️ **演练副作用与复原**(⛔ 未重启任何 worker、⛔ 未换实例):演练后 47 视角 `ep=[] / used=1 / idOk=1` ⇒ 探针 **21P / 2S / 2F**(2F = `OBS-01` / `OBS-04` —— **计数**判据,⛔ 与"端点表为空"无关)⇒ **停 / 启 106 `dshs-relay`** ⇒ 约 **22 s** 回落 `used=2 / idOk=2 / 端点 [(19000,33213),(21001,42783)]` ⇒ 探针回绿。⚠️ `localPort` 又漂(42717 → **42783**;改动后首次真机跑为 **44231**)⇒ **判据跟着事实走、⛔ 无须改表值** ← 序㊲ 的收益再次自证。🔑 **键名改名的真机端到端证据** = 演练态 `SKIP OBS-08` 原文含「对端中继(test106)声明了端口…」⇒ 判别器**真走通了**含 `systemctl is-active ${RELAY_UNIT_NAME}` 的那条 ssh(比手跑单条命令更强的证据)。
**⑦ 收尾** = 参数表 **§11.6**(红绿原文 + 可分性 + 三件套 + 演练复原 + 边界 + 证据;🔴 **§10 指纹 `71848fad4ee0d6a574d833169e042fd1` → `2275870a5a9eb1964a648f2950dee528`** 现算并同步)|交接单 **§8.10**(本棒收口)+ **§8.11**(**P2 待拍板清单** = 用户点名的"要定的具体内容";⛔ **§8 前前缀 `085e28b56a08aa1f01999bdb3e238911` 逐字不变** ✅)|入口 **§0 新增一行 + §2 换「序㊳ 已收官」**。**边界** = ⛔ 未 commit / push(HEAD `09ce76f`;`git status --short` **11** 处 = 序㊲ 的 **10** 处 + 本棒新增 `scripts/overlay-failover-drill.cjs` 一处 `M`,**逐条可解释**)|⛔ 未改任何生产**配置值**(参数表**值格一律未动**)|⛔ 零新增暴露面(`OBS-11 多出 0 缺失 0`、relay 口 **1/1** 回环)|🔴 `RELAY_FAILOVER_COOLDOWN_MS` 命中 **0**|⛔ 未改 bwrap / nft / nginx|⛔ 未开工 P2。📁 证据 = 工作区 `_tmp_seq38/`。
---
## 12:2x–12:3x · 序㊴ 规划棒(P2 拍板落地 + 本线首份 P2 可执行单;⛔ 零代码 / 零服务器改动)
**触发** = 用户对 §8.11 两问的答复原文:`1 用户可设置,默认开启提示用户 2 乙`。
**用户口径(⛔ 不许改写)**:**①** 打洞能力**交给用户自己控制**(可设置)+ **默认开启** + **必须提示用户**(⇒ ⛔ 不是简单的"开 / 不开")|**②** 云安全组 UDP 入站**仅放行对端节点 IP**(⛔ **非** `0.0.0.0/0`)。
**产物** = 工作区根 **`交接单_覆盖网络直连与P2P_20260918.md`**(**归档号 132** · **239 行** · **§8 前前缀 `8925c2460810c6dc5cdbc075b6cdaed2`**)。结构 = §1 用户口径(含 3 条自决的产品口径:设置项落两处 / 缺省开 / 提示必须可行动)|§2 **8 条只读前置(全部已取证)**|§3 在册文件集(5 新增 / 4 修改 / 不改清单)|§4 技术路线(候选交换**复用既有 wss** + 内建 `dgram` 打洞 + 开关与提示 + peer 接线 + 乙方案落地点)|§5 判据 **D1–D8**|§6 步骤 **S5 / S6 / S7**|§7 权限影响评估(**已拍板**)|§8 回报格式|§9 回头条件(8 条)|§10 未验证项(5 条)|§11 指纹与状态。
**新增取证(⛔ 与旧印象冲突时以本节为准)**:
1. 🔴 **`src/` 零 UDP / 穿透代码** —— `grep -rniE "candidate|punch|directPath|natType"` 在 `src/net/relay/*.ts` **仅命中 `directory.ts` 的中继候选**(⛔ 不是节点候选地址)⇒ 直连**从零建**。
2. 打洞**可行性**脚本 = `scripts/overlay-holepunch.cjs`(序⑥ S4;⛔ **一次性、不进产品路径**,头部明写"⛔ 不是本系统打洞成功率")。
3. `peer` 档**有账本、传输走 wss** —— `peer.ts` 头注原话:「不做传输(取块走 `source.ts` 注入的 fetcher,**底层复用既有 wss 通道**)」⇒ **S6 的真正含义 = 把 fetcher 接上直连**。
4. ⛔ **不引入新依赖**(`client.ts` 用 Node 22 内建 `WebSocket`;`addr-override.ts` 头注明写"不引入 `undici` / `ws`")⇒ 打洞**只用内建 `dgram`**。
5. `src/web/routes/overlay.ts`(83 行)= **P0-2 引导目录端点**(只读无鉴权),⛔ 与"管理面 API" `overlay-nodes.ts`(未建、归 P2)**不是同一物**。
**🔴 一条如实登记的技术现实(同时写进新单 §4.5)**:**乙**要求云安全组按"**对端节点 IP**"放行,而**家宽 / 移动节点的公网出口 IP 会变** ⇒ 规则会失效、且云安全组**规则数有上限**。序㊴ **自决(可推翻)** = **乙-1 起步**:只对**有固定公网 IP** 的节点开(云节点 / 专线);**家宽节点本期走中继**(⛔ 不影响可用性,只影响"它能不能被直连")。**乙-2**(半自动:平台算清单 + 人工 / 脚本写控制台)与 **乙-3**(云 API 自动化,🔴 **需云 AK/SK**)登记为新单 **§9 回头条件** —— ⛔ **本棒未请求任何凭据**。
**收尾** = 新单落盘(占号 **132**:现核正式档案最大 **128**、`.lock-131` 仍在 ⇒ ⛔ 不重用 129/130/131)|旧单 **§8.12 拍板落地**(⛔ 正文一字未改 ⇒ **§8 前前缀 `085e28b56a08aa1f01999bdb3e238911` 逐字不变** ✅)|参数表 **§8 新增 ⑩ 行 + 附注口径更新** + **§11.7 补记** + **§10 指纹 `2275870a5a9eb1964a648f2950dee528` → `d408d640246a980f702fe7b0a2895219`**(现算并同步)|入口 **§0 新增一行 + §2 登记下一棒**。
**下一棒 = 序㊵ 执行棒(P2 · S5:直连候选交换 + 打洞探测)**,automation **`d4fa99ac-6f7b-400f-89e7-797de3b7681a`**(一次性 · **2026-09-18 12:33** = 本棒收口(12:2x)+~6 min ⇒ 落在 5~8 分钟窗口内;`nextRunAt` = **1789705980000**)。
**边界** = ⛔ 零代码改动 · ⛔ 零服务器改动 · ⛔ 未 commit / push(HEAD `09ce76f`)· ⛔ 未改任何生产**值**(参数表**只加不改**)· ⛔ 未开工 P2。⚠️ **一条遗留(仅报告,⛔ 未动手)**:`04-调整方案/.lock-131` 仍在(序㊱ P1 占号窗口未释放)⇒ 后续取号者会**永久跳过 131**。📁 证据 = 工作区 `交接单_覆盖网络直连与P2P_20260918.md`。
---
## 11:4x-12:0x · 安装 Brevo(海外邮箱)官方 MCP 服务(已配好并实测通过)
**诉求**(用户原话):`安装海外brevo邮箱 的mcp服务`(附 API Key 的 base64)。
**结论:可用,已写入配置。** 用户给的是**普通 API Key**(`xkeysib-…`,89 字符),经实测**可直接驱动 Brevo 官方托管 MCP** —— 但**必须走 `api-key` 头**,走 `Authorization: Bearer` 会被拒(`invalid_token / Invalid or expired OAuth session`)。官方文档只写了 Bearer + 专用 MCP 令牌,**没写这条路**。
**配置落点**:`E:/ProgramData/.workbuddy/mcp.json`(=本机 `WORKBUDDY_CONFIG_DIR` 下的 MCP 配置;⛔ 非 `.mcp.json`)。备份 = `mcp.json.bak-20260918-brevo`。
**注册了 8 个分模块端点(共 64 工具)**(基址 `https://mcp.brevo.com/v1/<module>/mcp`):
| server 名 | 模块 | 工具数 |
|---|---|---|
| brevo-campaigns | email_campaign_management | 12 |
| brevo-transac | transac_templates(**事务邮件发送**,核心) | 18 |
| brevo-templates | templates | 7 |
| brevo-senders | senders | 5 |
| brevo-domains | domains | 5 |
| brevo-contacts | contacts | 6 |
| brevo-lists | lists | 7 |
| brevo-analytics | campaign_analytics | 4 |
⛔ **未采用聚合端点** `/v1/brevo/mcp` —— 它一次性暴露 **282 个工具**,会随每轮全量重发烧上下文。⛔ 未纳入 CRM / loyalty / whatsapp / sms / coupons 等非邮箱域(需用时再加模块)。
**验证三层全过**:① JSON 合法(11 servers)② 8 个端点 `initialize` + `tools/list` 全 200 ③ **真实 `tools/call`**:`get_senders` 回真实数据(sender `aisharenet / [email protected]`,active)、`get_domains` 回 `count:0`(该账号未配域名);另用普通 REST `api.brevo.com/v3/account` 单独验证 Key 有效(账号 `[email protected]`,组织 `aisharenet`,SMTP relay `smtp-relay.brevo.com:587`,登录名 `[email protected]`)。
### 🔴 本轮五个坑
1. 🔴 **文档没写全鉴权头 ⇒ 别把 401 当"Key 无效"**:Bearer 401、`X-Api-Key` 401,**`api-key` 200**。判别法 = 另打厂商普通 REST API 把"Key 坏"与"头写错"分开。
2. 🔴 **Cloudflare 1010 拦 Python**:`urllib` 被 `Error 1010 browser_signature_banned` 拒(TLS 指纹)⇒ **换 curl 即通**。探测一律用 curl。
3. 🔴 **聚合端点 = 上下文炸弹**(282 vs 64)。
4. ⚠️ **本机 git 全局代理指向未监听端口**(`127.0.0.1:10800`)⇒ 探测要 `--noproxy '*'`;mcp.brevo.com 直连可达,无需代理。
5. ⚠️ **服务端无状态、不返回 `mcp-session-id`** ⇒ `tools/list` 不带 session 也能调;⛔ 别因缺该 header 判失败。
### 待用户动作
🔴 **改了 mcp.json ≠ 生效** —— 需在**连接器管理页右上角「自定义连接器」**对 8 个新 server 逐个点**「信任」**才会激活。
⚠️ 连接器市场**无 Brevo 连接器**(已查:仅 QQ邮箱/飞书/钉钉/企业微信等)⇒ 厂商 MCP 是唯一路径。
### 边界自证
⛔ 未动既有 MCP 条目(baota-mcp ×2 / github-custom 原样保留)· ⛔ 未动生产 · ⛔ 未 commit/push · ✅ 临时目录 `_tmp_brevo/` 已删 · ✅ 密钥扫描未落入工作区(仅存在于 `mcp.json` +其备份,与既有 baota/github 令牌同为明文存放,属既有约定)· ✅ 技能 `workbuddy-mcp-install` 已建(用户级)。
---
## 覆盖网络线 · 序㊵ 执行棒(P2 · S5:直连候选交换 + 打洞探测)· 2026-09-18 12:33–13:2x
**判定 = 线内可停 · 🔴 D8(云安全组)待用户拍板**。依据 = 工作区根 `交接单_覆盖网络直连与P2P_20260918.md`(归档号 **132**;**§8 前前缀 `8925c2460810c6dc5cdbc075b6cdaed2`**,回填 §8 后**逐字不变** ✅)。
**交付(代码)** = 新增 `src/net/relay/direct/{candidate,punch,index}.ts`(候选交换 / `dgram` 打洞 / 开关·默认值·提示)+ `src/web/routes/overlay-nodes.ts`(P1 出口项=管理面 API)+ `scripts/overlay-direct-probe.cjs`(只读观测面)+ `test/overlay-direct.test.mjs`(10 用例)+ 探针 `OBS-26/27/28`;修改 `src/net/relay/network.ts`(**只加** `isAllowedDialer` = 白名单准入的**唯一可复用出口**,⛔ `server.ts` 零字节改动)/`join.ts`/`src/net/relay/index.ts`/`src/web/server.ts`/`scripts/overlay-node-join.cjs`/`scripts/overlay-probe.cjs`。
**读数** = 探针 **28 PASS / 0 SKIP / 0 FAIL**(rc=0;基线 25P ⇒ **+3 = `OBS-26/27/28`**)|`npm.cmd test` **201/200/0/1**(逐字同基线)|`--scene all --table` **12P/0S/0F**(幕4-C **23422 ms** / 30000)。**部署 4 处**(47 `/opt/dshs/lib` + `/opt/dsh-relay/lib`;106 `/opt/dshs-cluster/lib` + `/opt/dsh-relay/lib`,**32 md5 全同、不一致 0**)+ 3 脚本到 47 `/opt/dshs/scripts/`;重启 47 `dshs` + `dshs-worker`;回滚点 `/opt/dsh/backups/seq40-0918-1255/`。
**D1–D6 先红后绿**:先红 = 旧产物 `Error: Cannot find module '/opt/dshs/lib/net/relay/direct/index.js'`(入口即缺);后绿 = `OBS-26/27/28` 三行 PASS。管理层端点 `/api/admin/overlay-nodes[/direct]` **404 → 401**。
🔴 **D8 = 唯一红(未实施)**:云安全组 UDP 入站仍未放行(**双向实测均发 20 收 0**;`nft input policy = accept` ⇒ 挡在云侧、非 nft)。拟录 **乙-1** 两条 = 47↔106 各放行**对端节点 IP `/32`** × UDP `21100/21115`,⛔ **零个 `0.0.0.0/0`**。未实施理由 = 属 §1 边界外第 ④ 类(需用户给控制台访问方式 / 审批)⇒ ⛔ 未索取、未硬编码任何云 AK/SK。
🔴 **本棒自造过一次生产影响(已修复)**:首次 `--scene all` 被 `timeout 300` **截断** ⇒ 末尾 `restore()` 未执行 ⇒ **106 `dshs-relay` 停机约 4 min**(13:05:58 → 13:10 发现后 `systemctl start` 复原;该窗口门户 / 实例面全程 200、无业务中断证据)。**教训:`--scene all` 内含 120 s + 90 s 观察窗口 ⇒ ⛔ 不许套小于 ~8 min 的外层超时**。
🔴 **本棒修掉两条自造缺陷**(均在**本棒新建的观测脚本**内,不涉既有生产代码):① `--ss` 是**布尔开关**却按"带值选项"解析(`opt()` 只认字符串)⇒ 该腿**恒为 null(死代码)**、且正对照硬编码 null ⇒ **潜在假绿**("关闭态 0 行"在测量坏了时也是 0)⇒ 修 = 按"出现过"判定 + **新增正对照**(真绑一个直连口、要求 `ss` 看得见);② `punch` 从 `direct/index.js`(**选择性桶导出**,不转发 punch 常量)取 `DEFAULT_PUNCH_DEADLINE_MS` / `DEFAULT_DIRECT_COOLDOWN_MS` ⇒ `undefined` ⇒ `Number()` = `NaN` ⇒ **冷却构造期断言直接抛**(原文「收到 null」是假象 —— `JSON.stringify(NaN) === 'null'`)⇒ 修 = 改从 `punch.js` 取 + **启动期硬断言**(缺常量 ⇒ 具名 `exit 2`,⛔ 不再静默取缺省)。
⚠️ **D7 块 id 复算属 S6** ⇒ 本棒如实登记 **未验证**(⛔ 不编数;变更面自证 = `src/net/relay/content/**` 零改动)。
**边界自证** = ⛔ 未 commit / 未 push(HEAD 仍 **`09ce76f`**;`git status --short` **17** 处与开工逐数相同)|⛔ 未改 `server.ts` DIAL/白名单语义 · ⛔ 未改 nft(**72 行**逐字一致)/ nginx / bwrap|🔴 `RELAY_FAILOVER_COOLDOWN_MS=0` 命中 **0**|🔴 密钥本体不经网络 / relay|**值格一律未动** ⇒ 零个新增 / 改动生产 env(`DSHS_OVERLAY_DIRECT` 两机命中 **0** = 走缺省即开)。
**参数表 §10 指纹** `d408d640246a980f702fe7b0a2895219` → **`6b37bfd506758d882d9f803678f85d23`**(现算并同步;⚠️ 写 §11.8 **不改**该指纹 —— §11 在口径之外,已实测复核)。
**收尾四件套** = ✅ 交接单 §8 全节|✅ 参数表 §11.8|✅ 入口 §0(新增一行)+ §2(下一棒)|✅ 本日志|✅ `--release-exec` 释锁。**证据落点** = `_tmp_seq40/`(`probe.realmachine*.txt` / `drill2.txt` / `npmtest.txt` / `selfcheck47*.out` / `local.md5` / `udp_recv.js` / `udp_send.js` / `table.copy.md` / `probe.green2.txt` / `probe.red2.txt`),⛔ 未清理("先红后绿"的可复现证据)。
➡️ **下一棒(唯一一个)** = **序㊶ 执行棒(P2 · S6:`peer` 档接线 + D7 块 id 复算)**,automation **`32c2cde1-e5c4-427a-9b9b-e8adab1c8725`**(一次性 · **2026-09-18 13:34** = 收口 +~7 min;`nextRunAt` = 1789709640000)。
---
## 序㊶ 执行棒(P2 · S6:`peer` 档接线 + D7 块 id 复算)· 2026-09-18 13:35–14:0x
**判定 = ✅ D7 绿(先红后绿两腿齐)· ✅ 零回归三件套全绿 · 🔴 D8 云安全组仍未放行(既存唯一红,本棒只读核对)· ➡️ 已登记唯一下一棒 = 序㊷(P2 · S7 规模回归)。**
**开工门** = 锁 `--claim-exec "序㊶ 执行棒(P2·S6)"` 抢到(13:35)|**§8 前前缀开工前现算 = `8925c2460810c6dc5cdbc075b6cdaed2`** ✅ 逐字一致(回填 §8.8 后**仍逐字不变**)。范围 = 只做 §6 的 S6(⛔ 未做 S7)。
**交付(2 源文件 + 1 观测面)** = `src/net/relay/content/runtime.ts`(`fetchFromPeers()` + 双通道 `PEER_CHANNEL_ORDER=['direct','wss']` + 🔴 **D7 复算闸门**(`blockIdOf(gotBytes)!==id` ⇒ **丢弃且不算命中**、`idMismatches` 计数)+ `peerWireSnapshot()` + `noteDirectCandidate()`/`withdrawDirectCandidate()` + 注入式 `peerChannels`)|`src/net/relay/content/source.ts`(**仅注释**:复算闸门**刻意落在装配层**、⛔ 不放进"五档通用链"以免分层退化)|`scripts/overlay-direct-probe.cjs` **新增 `peer-wire` 子命令**(**13 条具名判据** + 块 id 不变性读数)。
**D7 读数(原文)** = **主判据** `idsDigest = ec70e7166ae24b1367c7c251ead8ad2979f66463ae3e6380eeda84fabb985c3a`(**四处同值**:本机接线前/本机接线后/**47 生产接线前**/**47 生产接线后**;`plain putPlan[0]=3374319c90813e8f7911654132f34a0c`、`cipher putPlan[0]=8dd3ea58093df7130ba07722cd28bf81`);**先红 ①** 扰动 `blockIdOf` ⇒ digest → `fbb80585c86a4c492f6d71a7af9212121f35c2c43f2cc7018d08149e2cff5ac3`,还原后 md5 逐字回 `a69b3379c719562c46d3e63832bdf25d`、`diff` 空;**先红 ②** 拆闸门 ⇒ `peer-wire` 判据 ⑦ **转 FAIL**(原文 `{"tier":"peer","bytesMatch":false,"hits":{"wss":1},"idChecks":1,"idMismatches":0,"peerHitsTotal":1}` = **篡改块被记成"命中成功"的假绿本形**),还原后 ✅ + `idMismatches=1`、`tier=null`。真机 `content.peerWire` 新键在线(`wired={direct:true,wss:false}`、`idChecks=0`、`last=null`)。
**E1 回归** = 接线腿 `amp=1.0000`(A `originFetches=6 / 5,255,225 B`;B `servedFromPeer=18`、`hits=[6,6,6]`、`idChecks=[6,6,6]`、`idMismatches=[0,0,0]`)/对照腿 `amp=4.0000`(`originFetches=24 / 21,020,900 B`)⇒ **与序㉔/㉛ 的 4.00× → 1.00× 逐项对齐**;`idGate={idChecks:18,idMismatches:0}`。
**三件套** = `npm.cmd test` **201/200/0/1**|`--scene all --table` **12P/0S/0F**(rc=0,6m27s,幕4-C 18990 ms/30000;**未套外层超时** ⇒ `restore()` 正常、两台 relay 均 active)|`overlay-probe --table` **28P/0S/0F**(部署后+演练后两次同值)|`test/overlay-content.test.mjs` **44/44**。
**部署** = 4 处(47 `/opt/dshs/lib` + `/opt/dsh-relay/lib`;106 `/opt/dshs-cluster/lib` + `/opt/dsh-relay/lib`),`runtime.js` md5 四处全同 `c9a8b01a78524d31cd77fde32895839a`、`source.js` `43cb1b7ab5387ff9745e8dab7726b68b`(接线前 `runtime.js` = `7fe548c42b71b9e4785f139561fe8925`);重启 47 `dshs-relay`+`dshs`、106 `dshs-relay`;回滚点 = 两机 `/opt/dsh/backups/seq41-20260918-135146/`。**改动面 = 6 文件**(`content/runtime.{js,d.ts,js.map}` + `content/source.{js,d.ts,js.map}`)。
**D8(只读核对,红)** = 双向并发 UDP:47 发 29 收 0 / 106 发 28 收 0(`reason=deadline`);两机 `nft` **72 行**未动 ⇒ 挡在**云侧**;⛔ 未改任何云侧规则、⛔ **无任何 `0.0.0.0/0`**、⛔ 未索取/未硬编码任何云 AK/SK。
**🔴 三条经验(可复用)**:**① D7 这类"接线不改语义"的判据,红腿必须做两条** —— 只做"扰动实现"会漏掉"闸门被拆"这一路(后者才是"零回源"变假绿的真实入口);**② 夹具对照腿必须用全新冷 store** —— 复用已填充的 store ⇒ **假对照**(本棒实测踩过,`amp` 得到天文数字);**③ 跨模块键口径要对齐** —— 直连候选入册键必须与 `peer.ts#candidates()` 同口径(**逻辑名 `network/hostId`**),用载荷 `hostId` ⇒ 判据假红。
**⚠️ 三条如实留档(已写进单 §8.8-⑦)** = **①** 真机双向直连**不可达属已知** ⇒ `peer` 档正确形态 = **回落 wss 且块 id 不变**(⛔ 未把"直连建立了"写成成功);**②** S6 的**数据面刻意未建成**(直连通道以 **`data-plane-pending`** 具名降级、`fetch()` 抛错,⛔ 不静默回"没有");**③** 106 `/opt/dshs-cluster/lib` **无 `registry.js`** ⇒ `overlay-direct-probe.cjs` 在 106 **跑不起来**(D8 核对改用独立最小脚本 `d8_punch_106.cjs`;探针留存在 106 的副本**不可跑**,属独立后续项)+ 106 `bwrap` = **0.11.0**(47 = 0.4.0)。
**收尾四件套** = ✅ 交接单 **§8.8**(⛔ 未改 §1–§7、⛔ 未改 §8.1–§8.7 小节点名;**§8 前前缀写入后现算仍 = `8925c2460810c6dc5cdbc075b6cdaed2`**)|✅ 参数表 **§11.9**(**§10 指纹现算未变 = `6b37bfd506758d882d9f803678f85d23`** —— §11 在 §10 口径之外;⛔ 零键零值变更)|✅ 入口 §0(新增一行)+ §2(下一棒)|✅ 本日志|✅ `--release-exec` 释锁。**证据落点** = `_tmp_seq41/`(`ids.mjs` / `ids_before.txt` / `ids_after.txt` / `e1.mjs` / `lib_before/` / `d8_47.json` / `d8_106.json` / `d8_punch_106.cjs`),⛔ 未清理("先红后绿"的可复现证据)。
**边界自证** = ⛔ 未 commit / 未 push(HEAD 仍 `09ce76f`;`git status --short` **19** 处 = 开工 17 + 本棒 2)· ⛔ 未改 `server.ts`(DIAL/白名单语义零字节)· ⛔ 未改块 id 计算路径(扰动**临时**且**已逐字还原**)· ⛔ 未改 nft(72 行)/ nginx / bwrap · 🔴 `RELAY_FAILOVER_COOLDOWN_MS=0` 命中 **0** · 🔴 密钥本体不经网络 / relay(两机 `600`)· ⛔ 零个新增/改动生产 env(`DSHS_OVERLAY_DIRECT` 两机命中 **0** ⇒ 缺省即开)。
➡️ **下一棒(唯一一个)= 序㊷ 执行棒(P2 · S7:规模回归 —— 候选数 / 直连成功率 / 切换耗时 / 回源字节 四项复测)**,automation **`690f44e9-b6f7-4517-ae2b-ca0b05b8dfe9`**(一次性 · **2026-09-18 14:14** = 收口 +~6 min;`nextRunAt` = **1789712040000**)。
---
## 14:14–14:5x · 序㊷ 执行棒(覆盖网络线 · **P2/S7 规模回归** 四项复测)—— ✅ 四项全绿 · ✅ 三件套全绿 · 🔴 D8 仍为唯一红
**一句话** = 四项规模读数各自拿到原始读数并与基线逐项对齐 —— 候选数不退化、**回源字节"按组线性增长"被机器断言**、切换耗时 **20.9 s / 限 30 s**、而**直连成功率只有模拟器是 `1.0000`,真机仍是 `0`**。
**① 候选数** = `dshs@47` 与 `dshs-worker@106` 均 **`count=3 hosts=3`**(≥ `CAND_MIN=2`,**逐字同序㉙ 基线**;⚠️ **`hosts=3` ≠ 独立路径数** —— 前两条候选摘名不同**同落 47** ⇒ 机器级只有 **2**)|直连候选准入 `accepted=3 / rejected=9(每条具名)/ silentRejections=0 / bucketsPerNetwork=true / matcher=network.ts#isAllowedDialer`|🔬 **规模夹具**(`_tmp_seq42/s7_candidate_scale.mjs`,2 张网 × {4,16,64} 节点)⇒ `accepted 10/34/130`(**线性**)、`rejected 32/128/512`(**8 类原因各 perNet 条、全部具名**)、`silentRejections=0`、按网分桶 `true`(同名 `hostId` 跨网互不可见 ⇒ 跨网窥探得 `cross-network`)。
**② 直连成功率**(🔴 **"判据成立"与"冗余建成"已彻底分开**)= **本系统(离线 NAT 模拟器,同一份 `runPunchAttempt`)正腿 `24/24 = 1.0000` 双向、p50 建连 `155 ms`**;负腿 `one-way(a)=12` / `one-way(b)=12` 全 **0**、`both=12` 全 **`deadline`**(⛔ **有收包一律不算成功**)|**真机** `47→106.54.21.172:21100` **发 21 收 0**、`106→47.77.182.89:21100` **发 28 收 0**(`reason=deadline`)⇒ **不可达(D8 未放行)** ⇒ ⛔ **未把"直连建立了"写成成功**;真机形态 = **回落 wss 且块 id 不变**。📌 **对照 §3.2**:`HOLE_PUNCH_RATE_LOCAL` = **分层可行性 2/2** —— 它是"**能不能打洞**",⛔ **不是"本系统成功率"**(两者必须分开报)。
**③ 切换耗时** = `幕4-C` **20871 ms / 30000**(`PASS`)|`幕1-A` **20605 ms**|`幕4-A` **18815 ms**(n=3,**均在 deadline 内侧**;对照序⑨ 修后基线 **20.9–23.9 s** **同量级**)|收口后 `jitter.p95AbsDeltaMs=6 ms / overThreshold=false`(⚠️ 演练窗口内曾读到 `920 ms` —— 同源 = **心跳 RTT,含应用层与验签** ⇒ ⛔ 按 §3.5 口径不得当链路 RTT 用)。🔴 **阈值权威落点 = 参数表 §4 `RELAY_FAILOVER_DEADLINE_MS`** —— ⛔ **不是 prompt 写的 §3.2**(§3.2 是打洞率) ⇒ **按权威落点对照 + 如实留档**(⛔ 未为迁就指针改 §3.2 / §4 任一格)。
**④ 回源字节**(夹具 = 同一份 **5,255,225 B / 6 块**,**逐次计 `origin` fetcher 字节**)= `1组×4节点` **1.0000**(5,255,225 B)|`1组×16节点` **1.0000**|`4组×4节点` **4.0000**(21,020,900 B)|`8组×4节点` **8.0000**(42,041,800 B)|对照腿(peer 关)= **4 / 16 / 16 / 32 ×** ⇒ 🔴 **每组合计恒 `1.0000`**(= 规模不变量:**回源字节 ∝ 组数、⛔ 不随组内节点数增长**)。✅ 与序㉔ `1.0000 ⇄ 4.0000`、序㊛ `4 组 = 21,021,572 B = 4.0001×` **逐项对齐**。**运行时接线腿**(8 组×4 节点,真 `ContentRuntime`)= `wss-回落腿` `hits.wss=144 / downReasons={direct:no-address:144}`|`direct-优先腿` **`hits.direct=144 / hits.wss=0`** ⇒ **优先直连且 ⛔ 不碰 wss**;两腿均 `idChecks=144 / idMismatches=0`(**每次命中都过 D7 复算闸门**)。📌 **`§10-2` 内容面节省 = `1 − 组数/节点数`** ⇒ **组内 4 人 75.00% / 组内 16 人 93.75%**(真机直连节省仍 = **0**,因 D8)。
**零回归三件套** = `npm.cmd test` **201/200/0/1**(`duration_ms=53333`;逐字同基线)|`--scene all --table` **12 PASS / 0 SKIP / 0 FAIL**(rc=0;**6m20s**;**未套外层超时** ⇒ 末尾 `restore()` 正常执行、两台 `dshs-relay` 均 `active`)|`overlay-probe.cjs --table` **28 PASS / 0 SKIP / 0 FAIL**(**开工前 + 收口后两次同值**)。
**收尾四件套** = ✅ 交接单 **§8.9**(新增;⛔ 未改 §1–§7、⛔ 未改 §8.1–§8.8 小节点名;**§8 前前缀写入后现算仍 = `8925c2460810c6dc5cdbc075b6cdaed2`** ✅)|✅ 参数表 **§11.10**(**§10 指纹现算未变 = `6b37bfd506758d882d9f803678f85d23`** —— §11 在 §10 口径之外;⛔ 本表**零键值改动**)|✅ 入口 §0 新增一行 + §2 更新下一棒|✅ 今日日志(本节)|✅ `--release-exec` 释锁。
**边界自证** = ⛔ 未 commit / 未 push(HEAD 仍 **`09ce76f`**;`git status --short` **19** 处 = 与序㊶ 收口**逐数相同** ⇒ **零新增条目**)· ⛔ **本棒零仓内改动**(三项夹具 + 读数全在工作区 `_tmp_seq42/`,⛔ 仓外)· ⛔ 未改 `server.ts` / `content/**` 块 id 口径(**本棒未做任何扰动实验**)· ⛔ 未改 `nft`(**72 行**)/ `nginx` / `bwrap`(47=**0.4.0**、106=0.11.0)· 🔴 `RELAY_FAILOVER_COOLDOWN_MS=0` 命中 **0** · 🔴 密钥本体不经网络 / relay(`content-group-key.json` 两机仍 `600`)· ⛔ **零个新增 / 改动生产 env**(`DSHS_OVERLAY_DIRECT` 两机命中 **0** ⇒ 走缺省即开)· ⛔ **零新增监听口**(收口后 `ss -lunp` 在 `21100–21115` 段 **0 行**;`OBS-11 多出 0 缺失 0`)。
**🔴 三条如实留档(已写进单 §8.9-⑥)** = **① 探针 / 演练必须从「工作区根」跑**(按 cwd 找参数表;从代码仓根跑 ⇒ `❌ 参数表装载失败 … 找到 0 个参数表` rc=2;⛔ 该失败**不发生任何生产动作**)|**② 演练后的复原程序要"停够久"** —— **"停 2 s 即启"不触发回落**(worker 在 `RELAY_GRACEFUL_BURST_MS=15000` 的快速重试窗内**重连回同一条** url;该窗内 `attempts` 恒 0 ⇒ 切流条件不成立),**改"停 26 s 后启"成功**(`used=2`、端点 2 条全在线)⇒ ⛔ 未重启 worker、⛔ 未换实例|**③ 一处过程测错并当场修正** = 首次 `selfcheck --ss` **与 punch 腿并发** ⇒ punch 先绑住 `21100` ⇒ 正对照 `EADDRINUSE` ⇒ `ssDuringOnCase` 读成 **`null`**、`ssBefore` 读成 **1**(**"UDP 口被占"的假红**);**单跑**后 = **`ssBefore 0 / offCase 0 / onCase 1`**(与序㊵ **逐字同值**)⇒ **测量错,⛔ 非产品缺陷**。
**🔴 一条口径纪律(本棒再次执行)** = **"判据成立" ≠ "冗余建成"** —— 候选数 `count=3` ✅ 但**机器级独立路径只有 2 台**;打洞**模拟器 1.0000** ✅ 但**真机 0**。两者**分开报**,⛔ 不许用前者的绿掩盖后者的红。
➡️ **下一棒(唯一一个)= 序㊸ 执行棒(观测面缺口收口:① `overlay-direct-probe.cjs` 在 106 上可跑〔⛔ 不靠再铺 `registry.js`,改惰性/可选加载 + 具名降级〕② 参数表定位与 cwd 解耦〔⚠️ 保留"恰好 1 个才合法"防错〕③ 106 上不可跑残留副本处置)**,automation **`8dbee90b-7d49-4c97-a25e-fdc302f9ca32`**(一次性 · **2026-09-18 14:47** = 收口 +~6 min;`nextRunAt` = **1789714020000**)。⚠️ **同一时刻只挂这一个棒**;📌 **在册待你拍板项 = D8**(云安全组乙-1,需控制台访问方式 / 审批)—— 已在册,⛔ 不因它阻塞排棒。⚠️ **D8 仍待用户拍板**(已在册、非本轮新发现):要不要现在开通云安全组 **乙-1**(需用户给控制台访问方式 / 审批;⛔ 不索取、不硬编码任何云 AK/SK)。
---
## 2026-09-18 15:2x · 覆盖网络线 **序㊸ 执行棒(观测面缺口收口)收官** —— 判定 =「**线内可停 · 🔴 D8 待拍板**」,⛔ 未登记下一棒
**做了什么(3 个脚本,⛔ 零 TS / 零 src / 零 build)**
- `scripts/overlay-direct-probe.cjs`:顶层对 `../lib/net/relay/registry.js`(**控制面注册表**模块)与同样 import 它的 `../lib/net/relay/join.js` 的**硬 `require` 改成惰性 / 可选加载** ⇒ 只有 `join 回读`(判据 D3)这条腿依赖它们,缺则**具名降级** `{available:false, reason:'module-missing', missing:[{spec,code,message}]}`;读数加机器可读 `degraded` / `degradedLegs`;文本行 `join 回读:⛔ 不可用(module-missing)—— 缺 …|⚠️ 这是「没装」不是「没采到」`。⛔ **不静默返"没有"**(缺块**不填** `direct`/`readback`);只吞 `MODULE_NOT_FOUND` / `ERR_MODULE_NOT_FOUND` 且**原文 message 原样带出**,其它异常照抛。
- `scripts/overlay-probe.cjs` + `scripts/overlay-failover-drill.cjs`:**参数表定位与 `cwd` 解耦** ⇒ 候选目录链 `--table`(显式文件,最高优先)> `--dir` > `DSHS_OVERLAY_TABLE_DIR` > **机器本地注册文件**(`DSHS_OVERLAY_TABLE_REGISTRY`,缺省 **`~/.dshs/overlay-table-dir`**,一行一个目录)> `cwd` > 脚本目录及上两级。🔴 **"恰好 1 个才合法"保留且更严**:任一候选目录 ≥2 ⇒ 立即报错;**跨目录合计 ≥2 ⇒ 也报错**;一份都没有 ⇒ 报错并列出**所有搜过的目录**(⛔ 不放宽成"找不到就用缺省")。
- ⚠️ **范围新增 1 处(已声明)**:`overlay-probe.cjs` 的 **`OBS-26` 子判据 +3 行** —— 生产方**显式**给 `joinConf.available === false` 时,该子判据取 **SKIP + 留痕**(⛔ 不判红、detail 点名缺哪个模块)。理由 = 上面改了读数形状,不改这里会让一份**合法的 106 读数假红**;**零回归佐证** = 老形状(无 `available` 键)走原路径、判定与文本逐字不变。
**关键读数(⛔ 无编数)**
- **① 先红后绿**:106 上改前副本(md5 `8d1ea60a01fc09fb5f56e492afb76fc9`)⇒ `status` / `selfcheck --json` **两条都**原文 `Error: Cannot find module '../lib/net/relay/registry.js'`;修后**同一规范路径** ⇒ `status` rc=0(`直连(打洞):开|来源 default|缺省 true`)+ `selfcheck --json` rc=0(`{"degraded":true,"degradedLegs":["joinConf"],"joinConfAvailable":false,"joinConfReason":"module-missing","candAcc":3,"candRej":9,"silent":0,"bi":true,"ok":2,"cooldown":300000,"zero":"throws","deps":true}`)+ `punch --peer 47.77.182.89:21100` rc=0(`窗内零收包(deadline 3000 ms,实耗 3000 ms,发 21 包)⇒ 判死 + 进冷却 300000 ms`)。**本地等效夹具**:临时改名本机 `lib/net/relay/{registry,join}.js` ⇒ 同一降级形状、其余腿全在;**还原后 md5 逐字回位**(`951a2526789b897aa58e3abbb5dea332` / `6946e1d802494ccb7f522ef7b937614a`)。
- **② 两腿对照**:旧逻辑副本(与 `git show HEAD:scripts/overlay-probe.cjs` 的 `resolveTablePath` **逐字一致**)从代码仓根 ⇒ rc=**2** `❌ 参数表装载失败:在 D:\github\dsh_shenxian 下按 /^参数表_覆盖网络_.+\.md$/ 找到 0 个参数表(要求恰好 1 个)`;修后**从代码仓根、不给 `--table`** ⇒ 探针 **28P/0S/0F** + 演练 **12P/0S/0F**(原文 `# 参数表=E:\ProgramData\AIProject\ai1net-dsh-server\参数表_覆盖网络_20260917.md`)。**防错腿**:同目录 2 份 ⇒ rc=2;跨目录 2 份 ⇒ rc=2(**探针与演练各测一次**)。
- **③ 处置 = 使其可跑**(⛔ 未删):探针 scp 三处 **md5 全同 `4734686b5270059697ccbcf966c34ade`**(仓 / 47 `/opt/dshs/scripts` / 106 `/opt/dshs-cluster/scripts`);回滚点 = 两机 `/opt/dsh/backups/seq43-20260918-150549/`。
- **④ 三件套**:`npm.cmd test` **201/200/0/1**(53.3 s)|`--scene all` **12P/0S/0F**(6m23s;`幕4-C` 17240 ms / 30000)|探针 **28P/0S/0F**(两次同值)。
- **恢复形态**:47 三单元 `active`、relay 200、门户 200;106 两单元 `active`;演练后 47 视角 `endpoints=[]` ⇒ **停 106 `dshs-relay` 26 s 再启 + 等 ~20 s** ⇒ `w-106` 回挂、端点 2 条全在线、`OBS-01 used=2`。
- **边界**:⛔ 未 commit / 未 push(HEAD `09ce76f`;`git status --short` **19** 处 = 与序㊷ 逐数相同)|⛔ 未改 `server.ts` / `content/**` / `nft`(72 行)/ nginx / bwrap(0.4.0–0.11.0)|🔴 `COOLDOWN_MS=0` 命中 **0**|密钥两机仍 `600`|⛔ 零新增 env / 监听口(`21100–21115` = 0 行)|🔴 **D8 零动作**(⛔ 无 `0.0.0.0/0`、⛔ 未索取 AK/SK)。
**两条须知(会传给后续会话)**
1. 🔑 **参数表定位链新增"机器本地注册文件"** `~/.dshs/overlay-table-dir`(本机已写一行 = 工作区根)⇒ **换机器 / 换工作区路径需补这一行**(或改用 `--table` / `--dir` / `DSHS_OVERLAY_TABLE_DIR` / `DSHS_OVERLAY_TABLE_REGISTRY`)。⛔ **仓内零机器路径**(该文件不入库、不进 `git status`)。
2. ⚠️ **参数表 §6 `OBS-26` 行本棒 ⛔ 未改**(**为保 §10 指纹 `6b37bfd506758d882d9f803678f85d23` 不变** —— §6 在 §10 口径之内)⇒ "具名降级 ⇒ SKIP + 留痕"登记在 **§11.11**;下次键值变更时顺带并入。
**产物 / 落点**:交接单 `交接单_覆盖网络直连与P2P_20260918.md` **§8.10**(§8 前前缀 `8925c2460810c6dc5cdbc075b6cdaed2` **写后现算逐字不变** ✅)|参数表 **§11.11**(§10 指纹现算 = `6b37bfd506758d882d9f803678f85d23` **不变**)|夹具与读数全在工作区 `_tmp_seq43/`(**临时产物,待用户确认后清理**)。
➡️ **本线可停**:三项全绿、无遗留 ⇒ ⛔ **本棒未登记下一棒**;**唯一在册大项 = D8(云安全组,需你拍板:要不要现在开通 乙-1,需你给控制台访问方式 / 审批)**。
## 2026-09-18 15:3x–16:0x · 覆盖网络线 **序㊹ 执行棒(D8 云安全组乙-1)** —— ⏸ **未收官 · 阻塞上报**(用户已批准,执行侧无凭据)+ 本机角色判定结清
**起因(用户原话,15:4x)**:「**1 开通,需要我操作还是你可以自己操作,所有的测试是否包含本机,当做结点或终端**」= 对 序㊸ 收官报告末节「待你拍板第 1 项 = D8」的**批准 + 追加两问**。
**本棒性质**:`--claim-exec "序㊹ 执行棒(D8 云安全组乙-1)"` 抢锁成功(15:37)⇒ **只读取证后停下**(⛔ 零 scp / 零 build / 零重启任何单元)。
### 一、D8 执行侧阻塞(原文级证据 ⇒ 只能用户侧控制台落地)
| 位置 | 证据 | 结论 |
|---|---|---|
| 本机(WorkBuddy 开发机) | **无** cloud CLI、**无** `~/.aliyun` / `~/.tccli` / `~/.aws` | 无法调云 API |
| 47(阿里云) | `aliyun` CLI **存在**(`/usr/local/bin/aliyun`)但 **`~/.aliyun/config.json` 不存在**;`aliyun configure list` = `default * \| (空) \| Invalid \| (空) \| en` | **凭据未配置** |
| 47 metadata | `http://100.100.100.200/latest/meta-data/ram/security-credentials/` ⇒ **404 - Not Found** | **无 RAM 角色**(拿不到 STS) |
| 106(腾讯云) | **无** `tccli`;`http://metadata.tencentyun.com/latest/meta-data/cam/security-credentials/` ⇒ **404** | **无 CAM 角色** |
⇒ 🔴 依本线纪律 **⛔ 不索取、⛔ 不硬编码任何云 AK/SK** ⇒ **只能用户侧操作**(或由用户**主动**给一次性凭据 —— 本棒 ⛔ 不索要)。
### 二、待落地规则(乙-1 · 原文级 · ⛔ 执行侧不得改形)
- **47 阿里云安全组 · 入方向 · 协议 `UDP` · 端口 `21100/21115` · 授权对象 `106.54.21.172/32`**
- **106 腾讯云安全组 · 入方向 · 协议 `UDP` · 端口 `21100/21115` · 授权对象 `47.77.182.89/32`**
- 🔴 **硬门**:**⛔ 零个 `0.0.0.0/0`**(含拆分 / 等价写法);⛔ 不放行 TCP 段;⛔ 不改 `nft` / `nginx` / `bwrap`。
### 三、前置取证(只读 · 证明 D8 是"唯一缺口")
| # | 项 | 实测 |
|---|---|---|
| a | 固定公网 IP | 47 = **`47.77.182.89`**(`i-rj99af19cibck1ge93tq`)/ 106 = **`106.54.21.172`**(`ins-3q6k1p8t`) |
| b | 网卡形态 | 47 `eth0 172.18.16.212/18`;106 `eth0 10.0.0.8/22` ⇒ 两侧公网 IP **均为云网关 NAT 映射**(⛔ 非直挂网卡) |
| c | UDP 区间 | **`[21100, 21116)`**(`punch.ts#PUNCH_PORT_BASE=21100` + `SPAN=16`)。⚠️ 两机 `ss -lunp` 该段**无监听** = **正常**(打洞 socket 只在实打时才开),⛔ **不是"没装"** |
| d | 直连总开关 | `DSHS_OVERLAY_DIRECT` 两机 `/etc/dshs*.env` **命中 0** ⇒ 走**缺省 = 开**,⛔ 无需改 drop-in |
| e | 47 本地防火墙 | `nft` **72 行**;`hook input … policy accept` ⇒ **不拦入站 UDP**。⚠️ 那条 `udp … reject` 是**沙箱 uid `100000-199999` 的出站**限制(`daddr` 含 `47.77.182.89`),⛔ **与本腿无关** |
| f | 106 本地防火墙 | `nft` **0 行**;`iptables -P INPUT ACCEPT` + `YJ-FIREWALL-INPUT` 仅 4 条 `/32` REJECT(**不含 47**)+ `ipset test YJ-GLOBAL-INBLOCK 47.77.182.89` ⇒ **NOT in set** ⇒ **不拦入站 UDP** |
| g | 47 节点注册表 | `/var/lib/dshs/overlay/nodes.json` = `{"version":1,"nodes":{}}`(**空**);`directory.json` relays = `alotbuy.com` / `relay-direct.alotbuy.com` / `106.54.21.172`(3 条) |
### 四、本机(开发机)在测试中的角色判定 —— **回答用户第二问**
- **判定**:本机 = **测试发起端 / 观测台 + 开发机**。⛔ **不是**覆盖网节点,⛔ **也不是**覆盖网终端(本线节点形态只有 `worker` 一种)。
- **五条可分证据**:
1. 四条测试命令**均从本机发起**(`npm.cmd test` / `overlay-probe.cjs` / `overlay-failover-drill.cjs` / `overlay-direct-probe.cjs`)⇒ 本机是**发起端**、不是**被测对象**。
2. 本机 **零** `dshs*` / `dsh-*` 进程、**零** `20080` / `19100` / `20000` / `21000–21001` / `21100–21115` 监听 ⇒ 结构上**不在** `/status.endpoints[]` / `sessions[]` 里。
3. 47 权威注册表 `nodes.json` = `{}`(空)⇒ 本机**未注册**为节点。
4. 🔴 本机 `curl -4 ifconfig.me` **回落到 IPv6**(`240e:332:cd0:a710:…`)⇒ **无固定公网 IPv4** ⇒ ⛔ 给不出稳定 `/32` ⇒ **D8 式对端放行对本机结构上不成立**。
5. 本机 `~/.dshs/` 仅两项:`overlay-table-dir`(**序㊸ 自建**的参数表定位注册,⛔ 非节点注册)+ `secret.key`(64 B · 09-13 15:40 · 系 `src/config.ts#ensureSecretKey` 的**数据根密钥**,⛔ **非**节点密钥;**密钥本体 ⛔ 未读取**)。
- **若要纳入**:只能作「**只拨出**」从节点(本线铁律:**worker 永远只拨出**),需装运行时 + profile + 注册为 Worker;⚠️ 因无固定公网 IPv4 ⇒ **天然只能用 relay / 443 兜底腿**,**直连腿结构上不可用**。
### 五、边界自证
⛔ 只读取证(零 scp / 零 build / 零重启)|⛔ 未 commit / 未 push(HEAD 仍 **`09ce76f`**)|⛔ 未改 `nft`(47 仍 72 行)/ `nginx` / `bwrap` / `server.ts` / `content/**`|🔴 **零个 `0.0.0.0/0`**、⛔ 未索取 / 未硬编码任何云 AK/SK|🔴 密钥本体 ⛔ 未读取、两机仍 `600`。
**产物 / 落点**:交接单 `交接单_覆盖网络直连与P2P_20260918.md` **新增 §8.11**(⏸ 未收官 · 阻塞上报;**§8 前前缀写后现算仍 `8925c2460810c6dc5cdbc075b6cdaed2`** ✅,666 行、CR=0)|接续入口 **§0 新增 15:5x 行 + §2 首行改为 ⏸ 未收官**(432 行、CR=0)|⚠️ **参数表本棒零改动**(§10 指纹仍 `6b37bfd506758d882d9f803678f85d23`)。
➡️ **下一棒 ⛔ 未登记**(阻塞项需人操作,⛔ 无人值守重试无意义);**落地后第一动作 = 双向实测复测**(基线 **47→106 发 21 收 0** / **106→47 发 28 收 0**)+ 探针 `OBS-27/28` 复跑 + 参数表 §11 补记。锁已 `--release-exec` 释放。
### 16:0x 追加(用户追问「需要什么哪台服务器什么凭据,人工操作需要做什么」)—— 交付「控制台定位 + 人工步骤」
- 🔴 **新取证(会传给后续会话)**:**47 在阿里云「美国(硅谷)`us-west-1` · `us-west-1a`」**(实例 `i-rj99af19cibck1ge93tq`,私网 `172.18.16.212`,VPC `vpc-rj900gd84ra3taamu6tbr`)|**106 在腾讯云「上海 `ap-shanghai` · `ap-shanghai-2`」**(实例 `ins-3q6k1p8t`,私网 `10.0.0.8`)⇒ **两机跨洋**(打洞建连时延天然偏高,解读 S5/D5 耗时读数时要带上这一条)。
- 🔴 **两机的安全组 ID 都取不到**:阿里云 metadata `security-group-ids` ⇒ **404**;腾讯云 metadata `security-groups` ⇒ **404** ⇒ 只能从**控制台实例详情页**点进去。
- 🔴 **106 实例 ID 前缀 `ins-`(CVM)⛔ 不是 `lhins-`(轻量)** ⇒ 走 **CVM 安全组**口径,⛔ 不是轻量「防火墙」口径。
- 🔴 **已核连接器市场**:**不存在**阿里云 / 腾讯云 CVM·ECS 安全组相关的 Connector ⇒ **没有"装个连接器就自动获得授权"这条路**(腾讯云侧只有 CloudBase / DLC / ES / TCHouse-C 等产品连接器,均不碰安全组)。
- **人工步骤已落档**(交接单 **§8.11-④-补**):阿里云(47)= ECS 控制台 → 地域切 **美国(硅谷)** → 实例 → 安全组 → 访问规则 → **入方向** → 手动添加(允许 / `自定义 UDP` / `21100/21115` / `106.54.21.172/32`);腾讯云(106)= CVM 控制台 → 地域 **上海** → 实例 → 安全组 → **入站规则** → 添加(`UDP:21100-21115` / 来源 `47.77.182.89/32` / 允许)。两侧**保存即生效,⛔ 无需重启实例**。
- **代做时的最小权限口径(备查,⛔ 本棒不索取)**:阿里云 RAM 子账号仅 `ecs:DescribeSecurityGroups` + `ecs:AuthorizeSecurityGroup`(地域限 `us-west-1`);腾讯云 CAM 子账号仅 `cvm:DescribeSecurityGroupPolicies` + `cvm:ModifySecurityGroupPolicies`(地域限 `ap-shanghai`)。
- **边界自证**:仍 **零写入生产**(⛔ 未点任何云控制台 / 未改 nft / 未改任何规则);⛔ 未 commit / 未 push(HEAD 仍 `09ce76f`)。
### 16:1x 本轮收官盘点(用户:「这个测试先记下来后续在处理,本轮开发所有任务都完成了吗」)
**① D8 已按用户指示「挂起登记」** ⇒ 落点 = 接续入口 **§4**(标题由「待拍板」改为「**待你处理**」,首条即 D8;含精确操作 / 定位信息 / 为什么不能代做 / 落地后第一动作 / 🔴 ⛔ 不要为它登记接续棒)。已在册 = **四处**(入口 §4 + 单 132 §8.11-④-补 + `MEMORY.md` 状态层 + 自动化记忆)。
**② 盘点结论(如实)**:**覆盖网络线 序①–㊸ 全部完成并验收**;**唯一未完成 = 序㊹(D8)**,现转为挂起。
**③ 🔴 发现三类「账面未平」事项(均非"没做完",而是"没记账 / 没提交")—— 按纪律先报告、⛔ 本轮未动手**:
1. 🔴 **19 处未提交改动**(`git status --short`;远端 `master` = `09ce76f3af…` **与本地 HEAD 同 sha ⇒ 未分叉**,即本地也无未推送 commit):7 条 `M`(`scripts/overlay-{probe,failover-drill}.cjs`、`src/net/relay/content/{runtime,source}.ts`、`src/net/relay/{index,network}.ts`、`src/web/server.ts`)+ 12 条 `??`(**P2 单的核心新源码全部仍 untracked**:`src/net/relay/direct/`、`src/net/relay/{join,registry}.ts`、`src/web/routes/overlay-nodes.ts`、`test/overlay-{direct,join}.test.mjs`、`scripts/overlay-{direct-probe,node-admit,node-join}.cjs`,以及 `_tmp_seq24/`、`_tmp_seq40/`、`_中间产物_待清理/`)。⚠️ **这些代码已 scp 到 47/106 生产并在跑**,但源码未进 git ⇒ **工作区丢失 = 生产代码无源码来源**。
2. ⚠️ **`交接单/README.md §一`(自称"唯一来源 = 本表")已过期**:`覆盖网络-序24/25/26` 三行仍写「⏳ 待执行」(实际均已收官并另处回报);且 **129 / 130 / 131 / 132 四份单完全未登记**。
3. ⚠️ **三份覆盖网络单文件的 §8 回报从未回填**(`覆盖网络-序24/25/26*.md` 里 `### 8.x 序 ㉔/㉕/㉖ 执行棒回报(YYYY-MM-DD HH:MM–HH:MM)` 仍是**模板占位**,文件头状态仍写「待执行」)。
**线外(供参考)**:集群化线 **D6(生产拓扑最终落点)** 仍在册待拍板。
**④ 边界自证**:本回合**只改 1 个文件**(接续入口 §4)+ 本日志;⛔ 未 commit / 未 push;⛔ 未动代码 / 未动服务器;HEAD 仍 `09ce76f`。
---
## 16:4x–16:5x · 用户令「其余部分全部通过测试就,提交到双仓库」—— 已提交+主仓已推,CNB 卡凭据
**① 用户指令原话**:「其余部分全部通过测试就,提交到双仓库」。
**② 提交前零回归三件套复跑(全绿,实测留在 `_tmp_seq44` 类的证据已清)**:
| 项 | 结果 |
|---|---|
| `npm.cmd test` | **201 tests / pass 200 / fail 0 / skipped 1**(Node v22.22.2 / npm 10.9.7 / 53.6 s) |
| `overlay-probe.cjs --table` | **OBS-01…OBS-28 全 PASS**(0 SKIP / 0 FAIL) |
| `overlay-failover-drill.cjs --scene all --table` | **12 PASS / 0 SKIP / 0 FAIL**(6 m 47 s) |
**③ 提交**:`git add` **18 项(16 个交付物路径)**,🔴 **排除** `_tmp_seq24/`、`_tmp_seq40/`、`_中间产物_待清理/` 三个临时目录。
新 HEAD = **`c452013129c810944a7b4996076b9304976ce04e`**
(message:`feat(overlay): 覆盖网络线 序㉔–㊸ 补提交 —— 源码与已部署产物对齐`)。
行尾核查:`core.autocrlf=true`,但暂存新文件 blob **CR=0** ⇒ 不污染仓库。
**④ 双推结果**:
- ✅ **主仓** `[email protected]:maogeigei/dsh_shenxian.git` —— `09ce76f..c452013 master -> master` 成功;`git ls-remote` 复核远端裸 sha = `c452013…` **一致**。推前已确认远端裸 sha 是本地新 HEAD 的**父** ⇒ FF,无分叉。
- 🔴 **CNB 失败**:`remote: Repository Not Found. / 仓库不存在。`
**⑤ CNB 失败根因(本轮新增取证)**:
1. `~/.cnb/token` 的 `expires_at = 1789712902` = **2026-09-18 14:28:22**,测试时刻 16:45 ⇒ **登录态已过期**;`~/.cnb/git-token`(长期令牌)**不存在**。
2. **对照实验**:对**真实仓**与**不存在的仓** `zzz-not-exist-xyz-0918` 跑 `ls-remote --heads`,报错**逐字相同**(连 `token: cn***…` 回显都有)⇒ 证明「仓库不存在」是**鉴权失败的泛化报错**,⛔ **不是 URL 写错**(URL 今晨 08:1x 双推成功过)。
3. **无自刷新通道**(新增结论):CNB 连接器 `cnb-api` 在 `connector-states.json` 里 **bound=true / enabled=true**,但其 CLI 通道要求**全局安装 `@cnbcool/cnb-cli` + `cnb login` 设备流**(见 marketplace `cli.json`);本机 `which cnb` = 无、`binaries/node/cli-connector-packages` **不存在** ⇒ **CLI 未装**;本会话也未暴露任何 `mcp__cnb__*` 工具(连接器状态栏仅 `agent-mail` connected)。⇒ **本地无法自助刷新**,必须用户侧动作。
4. ⛔ **未改动 `~/.cnb/token`**(无 `.bak`、mtime 仍 06:28)—— 遵守「不碰用户凭据文件」。
**⑥ 收尾**:工作区仅剩 3 个**有意不提交**的临时目录(`_tmp_seq24/`、`_tmp_seq40/`、`_中间产物_待清理/`);
**全局执行锁已 `--release-exec` 释放**(释放后复核 = 无全局锁)。
本回合**未动代码 / 未动服务器 / 未改线上**。
---
## 16:5x · 跨线事项:授权归档找回(用户令「从 dsh-ai1net-github 空间中找一下授权文件双模式的,也同步到仓库中」)
- **用户指令原话**:「从dsh-ai1net-github 空间中找一下 授权文件双模式的也同步到仓库中」。
- ✅ **找到了**:09-13 记为「已不存在 · 恢复路径全部失效」的授权归档,实测在
**`E:\ProgramData\_已移出项目_授权归档\_授权归档_发布时再放回\`**(误判原因 = 当时搜索深度不足)。
含:`LICENSE`(5931 B,**自拟「双模式」**:第一层上游 MIT + 第二层 §二 免费/§三 商业)· `LICENSE-UPSTREAM-MIT.txt`(1107 B)·
`THIRD-PARTY-NOTICES.md`(4430 B)· `README.md.orig-带授权章节` · `package.json.orig-带SEE-LICENSE` ·
`代码仓/` 与 `服务器工作树/`(各 LICENSE + README.md + package.json,**源码仓 09-13 删前原版**)。
- 🔴 **但「放回」≠「照原样拷回」**:导出仓 `LICENSE` 已是 **AGPL-3.0 官方全文**(B 方案)⇒ 放回自拟「个人免费/商用收费」版 = **AGPL §10 追加限制**(09-17 定案明确要避开);两份声明文件按现行口径**无需单独放回**(上游 DSH 为 MIT 且不分发其代码,第三方声明由 README 承载);源码仓那三处删除是**用户明令**,未经重申不得自行恢复。
- **本回合落点**:只改**导出空间的台账** 3 处失效引用(§F 正文 + §十 A 表项 2 + §十六 快照行)→ 补真实路径 + 「放回判定」;另更新本工作区 `MEMORY.md` 授权行(补路径与「非照原样拷回」)。⛔ **三个仓库零改动、未 commit / push**。⛔ **没有自行选一个「最像」的方案写进仓库** —— 三处各踩一条硬线,须用户逐项点名。
- 详见 `E:\ProgramData\AIProject\dsh-ai1net-github\.workbuddy\memory\2026-09-18.md` 的 16:5x 节。
**⑤ 补记(17:4x 实测)**:CNB 连接器恢复 connected 后 **补推成功** —— 源码仓 `09ce76f..c452013 master -> master` ✅(`ls-remote` 复核两端裸 sha 一致)⇒ **源码仓双仓库已同步**。⛔ 全程未改动 `~/.cnb/token` 等任何凭据文件。
---
## 17:3x–17:5x · 用户选「B 模式」——**双模式授权落地为「两条轨各自成文件」**(已双推)
- **用户指令原话**:「b 模式,那个案子讲的是什么再说说」(B = 把归档里的「双模式」授权文件同步进仓库;并要求复述支撑判定的判例)。
- 🔴 **关键判断(自决修正,非照字面执行)**:归档那份自拟 `LICENSE` 的 §三写「商业使用必须事先取得书面授权」——
放在**已发布 AGPL 项目**里 = **AGPL §10 追加限制** + **OSD 第 6 条** ⇒ ①丢开源名号 ②**罗盒案明确否定** ③**推翻用户 09-17 的双轨定案**。
⇒ 落地为**正向等价**:**轨 1(AGPL)零改动,轨 2(商业轨)从"README 一段散文"升格为独立文件**。
- ✅ **落地(导出仓 `dsh-users-platform`,提交 `25f930f` · 4 files / +66 / −2 · 双推 GitHub + CNB 均成功)**:
新建 `COMMERCIAL-LICENSE.md` + `.zh-CN.md`(各 32 行、CR=0、EN 汉字数 4);`_build_export.py` 的 **OVERLAY 28→30**、**REQUIRED_EXPORT 58→60**;四份 README 授权节尾各 +1 句链接;`_check_parity.mjs` 的 `PAIRS` +1 对。
🔴 三条措辞铁律:① 全文写成「**平行的许可选择**」⛔ 不写成"对 AGPL 的限制";② 文件名**刻意不以 `LICENSE` 开头**;③ 正文**不含项目名**(改名窗口期两版通用)。
- **验收**:`_check_links.mjs` 26 份 / **0 问题**|`_check_parity.mjs` COMMERCIAL **5/5**、README 19/19 · 75/75 · 11/11、`CJKinEN 4`|新名 grep **0**|AGPL `LICENSE` md5 `4ae09d45eac4aa08d013b5f2e01c67f6` **未动**|overlay ↔ 仓 md5 SAME。
- **锁定论据(用户追问的「那个案子」)**:**罗盒 v. 玩友** —— 广州知识产权法院 **(2019)粤73知民初207号**,入选**最高法 2021 年中国法院十大知识产权案件**;同构案情,四项认定(GPL = 附解除条件的许可合同;**收会员费不违反 GPL**;**不提供源码 ⇒ 授权自动终止 ⇒ 侵权**,判停运 + 赔 **50 万**;🔴 **「在开源项目里加商业使用限制条款」明确不支持**)。同类:网经 v. 亿邦 50 万|南京中院 2022 年 300 万。
- 🔴 **锁**:本会话 `--claim-exec` 抢到 → 收官已 `--release-exec` 并复核(无全局锁)。
- ⏸ **仍未动**:源码仓 `dsh_shenxian` 授权三处(09-13 用户明令删除,**需重申**)|归档中 `LICENSE-UPSTREAM-MIT.txt` 与 `THIRD-PARTY-NOTICES.md` 判为**冗余不放回**。
---
## 17:5x · 技能 `dsh-opensource-release` 升 1.7.8 并**两副本对齐**(清结一笔 09-17 起挂的欠同步)
- **背景**:MEMORY §15 记的欠同步 —— 用户级副本 **1.7.7** vs 文档库副本 **1.6.0**(差 7 个小版本)。
- ✅ **本轮 `.workbuddy/skills/` 侧改动(4 处)**:① §0 事实表 —— `OVERLAY` **28 → 30**、`REQUIRED_EXPORT` **58 → 60**、`LEAK_PROBES` **72 → 75**、远端 `main` `c39758d → 25f930f`;② §5 补 09-18 落地(轨 2 独立成文件 + **三条措辞铁律** + 🔴 归档自拟 `LICENSE` **不得放回**);③ 修正 `_授权归档_发布时再放回/` 的失效路径引用(改为真实绝对路径 + 注明 09-13 曾误判"已不存在");④ §8 **新增坑 20**(新增中英文件对必须登记 `_check_parity.mjs` 的 `PAIRS`,否则**静默跳过**对等检查 —— 同类静默跳过已第 3 次出现)+ **坑 21**(`COMMERCIAL-LICENSE.*` 落位规矩)。
- ✅ **两副本同步**:`cp` 到 `D:\github\dsh_shenxian\dsh-server-docs\skills\dsh-opensource-release\SKILL.md` ⇒ **md5 双端一致 = `2f4bb3e2714da3b1716ee4469fc18d3a`**(85,224 B / **CR=0** 两边同)。
- ✅ **`INDEX.md` 两处过期登记同步修**(第 19 行能力索引 + 第 182 行变更记录):原文仍写「五条硬规则 R-O1–R-O5 / 验证六件套 / 8 条实测坑 / 产物在本机 `_开源导出_20260913/`」⇒ 改记 R-O1–R-O14 / 验证七件套 / 21 条坑 / v1.7.8 / 真实工作根。🔴 **走字节级单行替换**(`INDEX.md` 是**混合行尾**文件)⇒ 改后 **CR 5 → 5、LF 255 → 255 逐字节不变**,仅目标文本变化。
- 🔒 **锁**:本回合 `--claim-exec "技能同步 dsh-opensource-release 1.7.8(两副本对齐)"` 抢到 → 收官 `--release-exec`。
- ⛔ **未 commit / push**(用户未要求)⇒ 文档库这两处改动现为**未提交状态**。
---
## 20:0x–20:1x · 用户令「开发仓库都要上传 双授权文件」—— ✅ 开发仓补齐并双推(`45b4999`)
- **用户指令原话**:「开发仓库都要上传 双授权文件」。
- 🔴 **这是对 09-13「把仓库中的这类授权信息也都删除」的正式重申(反向)** ⇒ 之前挂在 MEMORY 里的「源码仓授权三处需重申才动」**已结清**。
- ✅ **落地(`D:\github\dsh_shenxian`,2 个提交)**:
- **`73645ac`** —— 新增 `LICENSE`(AGPL-3.0 官方全文 **34,523 B**,md5 `4ae09d45eac4aa08d013b5f2e01c67f6` 与导出层同一份)+ `COMMERCIAL-LICENSE.md` / `.zh-CN.md`(与 `_overlay/` **同 md5**:`9a69f164…` / `abe34bab…`)+ README 新增「## 许可证」节(三轨对照表 + 源码披露义务 + 商业轨取得方式)。
- **`45b4999`** —— `.gitattributes` 给三份授权文本加 **`-text`**(本仓 `core.autocrlf=true`,不加则下次 checkout 会把 `LICENSE` 改成 CRLF、**不再逐字节一致**);`git check-attr` 已核 `text: unset` 生效。
- ⛔ **未改 `package.json`** —— 其 `license` 字段由导出层规则 **插入**(`_build_export.py:266` `'"version": "0.1.0"' → '"version": "1.2.0",\n "license": "AGPL-3.0-only"'`)⇒ 源仓若也写,导出产物的 package.json 会出现**重复 `license` 键**(回归)。
- **验收**:三份文件工作区与仓库 blob **CR 均为 0**(纯 LF)|新名/中文新名 grep **0**|推前 `merge-base --is-ancestor` 判 **FF**|**双推 work + CNB 均成功**(`c452013..45b4999`,两端 `ls-remote` 裸 sha = 本地 HEAD `45b4999`)。
- 🔴 **README.md 是全 CRLF 文件**(CR=160/LF=160)⇒ 追加走**二进制 `\r\n`**,改后 **CR 160→173 = LF 173**(全 CRLF 保持)。
- **仍留在工作树未提交**(另一话题,等指令):`dsh-server-docs/INDEX.md` + `dsh-server-docs/skills/dsh-opensource-release/SKILL.md`(技能 1.7.8 登记同步)|3 个有意不提交的临时目录。
**20:2x 术语纠错(用户追问「你知道我说的双授权是什么吗」后核实)**:🔴 **「双授权」≠「双模式」** —— 前者 = **AGPL-3.0 官方全文 + 商业授权**(台账 §C **路线 B**,09-14 定案;README 自称 dual-licensed)=**现行口径,两仓已落**;后者 = **归档那份 5,931 B 自拟条款**(个人免费/商用收费 + 上游 MIT 分层),09-14 已被 B 取代、**不得放回**。我此前把两者当同一件,拿 5,931 B 当靶子论证了一轮 —— 结论没错、**论证路径错**。⇒ 教训:**用户提的术语先到本工作区台账/决策证据里查既有定义**,别用字面近义替换。详见导出空间当日日志 20:2x 节。
---
## 20:1x · 用户问「集群化 D6 待拍板:是什么」—— 口径已核实(⛔ 零改动)
- **D6 = 生产拓扑最终落点**。三选一:**47 扩容 / 106 扩容 / 同云新购**(出处:`交接单/archive/交接单-已完成/T08-集群化落地-兼容单例模式.md` §4 决策点表;BRIEF.md §3 与 INDEX `04-119` 均指向它)。**不阻塞** S0–S5(已在 106 测试环境完成)。
- **两条已定约束把选项压得很窄**:① **D4** = 跨云实测 **150–183 ms** ⇒ **生产拓扑须同云**,跨云只当故障注入测试床;② **备案坑** —— 阿里云公网 SLB 对未备案域名回 **403 Non-compliance ICP Filing**(`04-119 §2.4` / §822 / §863)⇒ **入口留在腾讯云 nginx,不要在阿里云侧直接对公网提供域名入口**。
- 🔴 **现状提醒**:47 = **阿里云硅谷 us-west-1**、106 = **腾讯云上海 ap-shanghai** ⇒ 现行「47 Manager + 106 Worker」**本身就是跨云** ⇒ D6 的实质就是「这个跨云形态是否为最终态;若不是,买在哪朵云」。
- ✅ **D6 已在上方 20:1x 一并答复用户**(含三个候选的优缺点与我的倾向:**B(106/腾讯云扩容)** 与 **C(同云新购)** 才是真取舍,**A(47/阿里云扩容)** 受备案约束基本出局);⛔ 未登记接续棒(属**花钱**决策,等你拍板后才排下一棒)。
---
## 20:2x–20:4x · 用户「把这个双规的授权相关文件都同步到 双开发仓库中」—— 两开发仓已补齐(四仓对齐闭环)
- **执行**:抢全局执行锁(`双开发仓同步双轨授权文件`,20:32)→ 两仓各克隆临时副本操作 → 完成 → 锁已 **20:4x 释放**(`✓ 已释放全局执行锁`,复核 `.exec-lock` 不存在);临时副本 `_tmp_docrepo` / `_tmp_ai1net` **已 rm -rf 并复核不存在**。
- ✅ **文档仓 `dsh_shenxian_doc`**(`[email protected]:maogeigei/dsh_shenxian_doc.git`,分支 `main`):原**无**任何授权文件 ⇒ 新增 `LICENSE` + `COMMERCIAL-LICENSE.{md,zh-CN.md}`(三份均从 `_overlay/` 直拷、md5 **SAME**、CR=0)⇒ 提交 **`74e2654`** ⇒ 推前 `merge-base --is-ancestor` 判 **FF** ⇒ 推送成功 `9b57578..74e2654`。**远端 main 裸 sha = `74e2654a7c52cdfb6f306bf978951ce2686886da` = 本地 HEAD** ✅。⚠️ 该仓**无本地 `user.*`**、全局也空 ⇒ 首次 commit 报 `Author identity unknown`,设 `maogeigei` / `[email protected]`(与代码仓一致)后成功。
- ✅ **新仓 `dsh_ai1net`**(`[email protected]:maogeigei/dsh_ai1net.git`,分支 `main`):原仓**只有 1 个文件** `LICENSE`(**CRLF,35,184 B** —— 归一化后 md5 同为 `4ae09d45…`,即同一份 AGPL 但被 `autocrlf` 改写成 CRLF)⇒ **替换为纯 LF 34,523 B**(CR 661→0)+ 新增两个 COMMERCIAL 文件 + **新建 `.gitattributes`**(三条 `-text`)⇒ 提交 **`8dd8071`** ⇒ **远端裸 sha = `8dd80712674c1e0c8ec08513c0f30f7dbf39ac11` = 本地 HEAD** ✅。
- 🔴 **新事实(后续推该仓必须知道)**:`dsh_ai1net` 仓**只认专用键 `~/.ssh/id_ed25519_ai1net`** —— 默认推送报 `fatal: Could not read from remote repository.`;换 `id_ed25519_github2` 报 `ERROR: Permission to maogeigei/dsh_ai1net.git denied to deploy key`(该键在此仓只有 deploy key 读权限)。**解法**:`GIT_SSH_COMMAND="ssh -i ~/.ssh/id_ed25519_ai1net -o IdentitiesOnly=yes" git push`。⚠️ `github.com` 段的默认候选序 = `id_ed25519_github2` → `id_ed25519_github` → `id_ed25519_ai1net`(前两个对上 `dsh_ai1net` 都会失败)。
- ✅ **四仓对齐闭环**(同一份三文件,md5 `4ae09d45eac4aa08d013b5f2e01c67f6` / `9a69f164d14ec8807ca2c7b76a6491f3` / `abe34babb0e0a0e14ee5b49ddfa26c6f`):
- 开发/代码仓 `D:\github\dsh_shenxian` → **`45b4999`**(09-18 早先完成,双推 work + CNB)
- 开源导出仓 `E:\ProgramData\AIProject\dsh-ai1net-github\dsh-users-platform` → **`25f930f`**(GitHub + CNB)
- **文档仓 `dsh_shenxian_doc` → `74e2654`**(本轮新增)
- **新仓 `dsh_ai1net` → `8dd8071`**(本轮新增)
- 🔒 **行尾保护分层(已实测)**:代码仓 `core.autocrlf=true` ⇒ 靠**新增的 `.gitattributes` 三条 `-text`**;文档仓靠既有 `.gitattributes` = `* -text` / `* -crlf`(全仓免转换);新仓靠**本轮新建的 `.gitattributes`**。三仓授权文本工作区 **CR 均 = 0**。
- ⛔ **未改 `package.json`**(延续铁律):代码仓 `version` 必须保持 `"0.1.0"`,`license` 由导出层规则插入(`_build_export.py:266`),源仓再写 ⇒ 导出产物**重复 `license` 键**。
- **仍留在工作树未提交**(等指令,本轮未顺手动):`dsh-server-docs/INDEX.md` + `dsh-server-docs/skills/dsh-opensource-release/SKILL.md`(技能 1.7.8 登记同步)|3 个有意不提交的临时目录|本地 `_中间产物_待清理/` 等(非本线产物)。
### 附:`MEMORY.md` 超限瘦身(系统提示强制项,本轮一并做完)
- 🔴 **触发**:会话注入时判定本工作区 `MEMORY.md` **超出注入上限被截断**(原 **14,935 B / 8,719 字符**;上限 ≈ **7,800 字符**)⇒ 系统在上下文里标 `ACTION REQUIRED`。**超出部分 = 等于不存在**,会静默丢规则 ⇒ 属事故级。
- **瘦身动作**(严格按本文件自身判据「不看它会否导致违规/事故」执行):**方法论 → 技能指针**(§三 整节压到 4 条,详版指向 `会话接续规范 §3.2.1/§6` + 技能 `dsh-auto-handoff-chain`)|**一次性事实 / 已过期值 → 现查**(删「提交基线 = `c452013`」写死值,改为「⛔ 不写死 ⇒ 现查 `state.py`」;这正是 09-18 被打穿的同一个坑)|**历史枚举 → 删**(覆盖网络「已落」清单里 relay 切流/presence/块级寻址/组密钥/jitter 逐项列举合并缩写;表格「形态」行与「判据」行**合并为一行**)|**重复指针 → 删**(`DEPLOY-本部署.md` / `覆盖网络_补遗与参考方案` 两条在 §2 指针表已有同项)。
- **瘦身结果**:**13,232 B / 7,760 字符** —— **进入上限(余量 40)**,`CR=0` / `LF=47`(纯 LF 保持)。⚠️ **余量只剩 40 字符** ⇒ 已在文件头写明「**只减不增,新增前先删等量**」。
- ⛔ **未删任何事故性内容**:R10 属主/EACCES、bwrap 版本差异、hook buffer 写入、四条取数坑、`--scene all` 超时、ssh 引号、单机自用≠不需要互联、异构设计、方案不含合规、接手前先取证、技能加载闸门、本机执行环境 3 条 —— **全数保留**;新增的「双授权术语消歧」「四仓 sha」「`dsh_ai1net` 专用 SSH 键」**一并纳入**。
- ⚠️ **另一处同类欠账(未动,仅记录)**:导出空间 `E:\ProgramData\AIProject\dsh-ai1net-github\.workbuddy\memory\MEMORY.md` 现为 **22,278 B / 13,009 字符** ⇒ **同样远超 7,800**,若该空间也按此上限注入,则其超限部分**已被截断**。⇒ **下次进该空间时先做同样的瘦身**(本轮用户未要求,⛔ 未顺手改)。
---
## 20:46 · 🔴 **D4 重定(用户澄清)——「生产须同云」作废,跨云即目标生产形态**
**用户原话**:「所以 D6 的实质是:现行「47 Manager(阿里云·硅谷)+ 106 Worker(腾讯云·上海)」本身就是跨云:**我要求做覆盖网络测的就是复杂网络情况下的网络连接,在一个云一个机房里我开发这些东西做什么**」
### 一、我错在哪
- 09-16 报 D6 时我写「**D4 跨云实测 150–183 ms ⇒ 生产须同云**,跨云只当故障注入测试床」,并据此把「同云新购」当成真候选之一。**这是把"用户要解决的场景"当成了"要消除的偏差"**。
- 文档里的原始出处(现已定位):
- `交接单/archive/交接单-已完成/T08-集群化落地-兼容单例模式.md` **行 67**(D4 表项)/**行 69**(D6)/**行 130**(S6 末尾「生产拓扑须同云」)
- `04-调整方案/119-集群化改造方案-Manager-Worker.md` **§17.5 行 964–965**(「这两台机器不能组成一个集群」⇒「必须选一个云内扩容」)
- `交接单/README.md` **行 24**(T08 行括注「跨云只当测试床」)
### 二、重定后的口径(**现行**)
- 🔴 **跨云 = 目标生产形态本身**,⛔ **不是**待消除的偏差。理由:覆盖网络的全部机制(relay 切流 · 打洞 · presence · jitter 选路 · 块级寻址)**只有在复杂网络下才有意义**;同云 <1 ms 会让它们**全部退化为无用**(= 用户原话「在一个云一个机房里我开发这些东西做什么」)。
- ⚠️ **但要保留一条区分(⛔ 别一起删)**:**跨云 ≠ 性能/容量基线环境** —— 性能与容量结论仍应在同云同 VPC 取数(否则被 150 ms 误导)。这是方法论,与"能不能跨云运行"是两件事(`119 §1054` 原文就是这个意思,只需补限定)。
- 🔴 **§17.5「这两台机器不能组成一个集群」已被实践推翻**:47=Manager、106=Worker **已经组成集群并跑起来了**(09-15 17:4x 生产整体切换;覆盖网络线序①–㊹ 全部是在这条**跨云 47↔106 链路**上完成的)。该结论是早期只看 150 ms 数字的推断。
- **D6 重排**:⛔ **「同云新购」排除**(与重定冲突 = 自己拆掉目标场景)⇒ 候选 = **47 侧扩容(阿里云)/ 106 侧扩容(腾讯云)/ 双侧**;⚠️ 不变约束 = **入口须留腾讯云 nginx**(阿里云 SLB 对未备案域名回 `403 Non-compliance ICP Filing`)。
- 🔑 **给后续会话的判据**:凡见到「跨云 = 测试床 / 生产须同云」的表述,一律按本条重定理解 —— **跨云是目的,性能基线才是要同云取数的东西**。
### 三、本轮未完成项(⛔ 因锁被占而跳过,非遗忘)
- **抢锁失败**:`handoff-guard.sh --claim-exec "D4/D6 跨云口径重定"` ⇒ ✗ 占用者 = **「日志可观测面-实现-20260918」(09-18 20:47 起)**。按 **R9** ⛔ 不接管、⛔ 不删锁 ⇒ **文档改写整体跳过**。
- ⏳ **待改清单(3 个文件,共 5 处)** —— 上述「一、」里点名的行号,逐处把「生产须同云 / 跨云只当测试床」改为「跨云=目标生产形态;性能基线另在同云取数」,并把 D6 候选里的「同云新购」标注为已排除。改前重跑取号/对账,改后 `README`/`INDEX` 登记跟随(同属 `交接单/README.md` 行 24)。
- ✅ 本轮**已完成**:口径落记忆(本文件 + `MEMORY.md`);⛔ **未动任何受锁保护的文件**。
---
## 20:5x · memory 优化(用户:「知道 memory 的优化策略吗,知道的话就优化 memory」)
- **策略四条(已确认并按此执行)**:① **分层判据** —— 不看它会不会导致「违规/事故」?会 ⇒ 写实体;只"更慢更绕" ⇒ 只给指针。② **注入上限 ≈7,800 字符**(超出即截断 ⇒ **排序即重要性**)。③ 🔴 **落点分流** —— 常驻规则 → 根 `CODEBUDDY.md`(⛔ 不重复)|会变状态 → `MEMORY.md`|方法论 → 技能 / PLAYBOOK|一次性事实 → `PLAYBOOK` / `04/<NN>`。④ **体积只减不增**、按重要性插入对应小节(⛔ 不追加末尾)。
- ✅ **工作区 `MEMORY.md`:8,719(超限被截断)→ 7,738 字符**(余量 62)。本轮二次优化走第 ③ 条**去重**:查得 `CODEBUDDY.md §7` 已承载「bash PATH 被 shim 重置」「Node 22 跑 `npm test`」「`git ls-remote` 核验推送」三条,而 `MEMORY.md` 的 §二 / §一 里又各自展开了一遍 ⇒ 三处**降级为带 `§` 号的指针**(信息不丢,省约 100 字符)。
- ✅ **导出空间 `MEMORY.md`:13,009(超限 5,209 = 约 40% 内容失效)→ 7,217 字符**(余量 583)。手法 = 第 ③ 条落点分流:把 **§3「铁律」17 条的机制细节 + §6 本机环境坑 + §6b `_overlay` 未来版陷阱** 全部**下沉**到新建的 `PLAYBOOK-导出脚本与铁律.md`(5,168 字符,不受 7,800 约束),`MEMORY.md` 只留**最高危 5 条实体 + 索引**。
- 🔑 **判据(可复用)**:`MEMORY.md` 的定位 = **索引 + 事故级实体**;**「改脚本前必读」的机制细节 ≠ 「每轮必读」** ⇒ 它该进 PLAYBOOK / 技能。⛔ 只删字会**丢信息**,**下沉才是正解**。
- ⚠️ 两个文件均 `CR=0`(纯 LF)保持。
---
## 21:1x · 用户问「§10-3 定稿口径的影响 / 有无绝对安全方案 / 阻碍」—— 分析结论(⛔ 零改动)
**问**:「中继看不到明文成立、内容不可推断不成立」会造成什么影响?是否有方案确保数据绝对安全、性能不受影响?有什么阻碍?
**🔑 核心结论 = 三者互斥**:**绝对安全 · 性能不受影响 · 内容可跨节点共享去重 —— 数学上不可能同时满足,必须砍一个**。理由:当前 `1.0×` 回源(而非 `N×`)**完全来自**「相同明文 ⇒ 相同密文 ⇒ 相同块 id」这一**可判定性**,而这个可判定性**就是**相等性泄露的来源 ⇒ 去掉泄露必然去掉可判定性 ⇒ 必然去掉共享 ⇒ 性能退化到 `N×`。这是收敛加密 / **MLE(Message-Locked Encryption)的定理级结论**,⛔ 不是实现缺陷。
**三层影响(按严重度)**:① **相等性 / 重复率可观测** —— 中继**无密钥**也能看"哪些块是同一块" ⇒ 可推"两用户共享同一份内容"、内容增减、低熵语义(实测 `10,000 块 / 100 值域 ⇒ 仅 100 种密文`)。② **持组密钥者可枚举** —— `85,878 条/秒`,域 `2³²` ⇒ 全量 `14.7 h` ⇒ **同组任一节点被攻破 = 该组低熵内容实质暴露**。③ 🔴 **但"读明文"底线守住** —— 中继自造字典命中 `0/1000`、持 1 对明文-密文也预测不出别的(`0/999`)⇒ 定性 = **元数据 / 相等性泄露级**,⛔ **不是"数据泄露级"**。
**三选项**:**A 保持现状 + 明确披露**(性能最优、零改动;泄露永久存在)|**B 按熵分层**(低熵块加随机盐 ⇒ 真正破坏相等性;高熵块保持确定性共享;⚠️ 低熵块彻底失去共享 ⇒ 该部分 `1×→N×`;块 id 须换口径 ⇒ 触 §9-5)|**C per-node 子密钥**(唯一真正的"绝对安全",连相等性都不泄露;代价 = 回源与存储 `×N`、中继节省 `93.75%→0` ⇒ **等于放弃序㉔/㉜/㉝ 的全部成果**)。**倾向 = A + B 组合(按内容分级)**,但 B 的成本须先量「低熵块在首屏包里占多少字节」。
**阻碍(5 条)**:① **理论层** —— MLE 对低熵「可去重且不泄露相等性」**不可能**;**OPRF 去重**只是把"判定相同"的能力从中继**收窄**到 OPRF 服务端,**相等性本身仍可见** ⇒ **收窄 ≠ 消除**。② **块 id 口径必须换**(`β′` = 密文哈希正是共享的前提)。③ **超出在册文件集** ⇒ 直接触发组密钥单 **§9-5 回头条件**。④ `E1` 判据体系须重写 + 需新建「低熵块识别」判据 —— **而"什么算低熵"本身无可靠判据**(开放问题,做错 ⇒ 要么无效要么白付代价)。⑤ **存储已在告急**(轮换实测已让 `storeBytes` 翻倍 `5,255,393 → 10,510,786`)⇒ 再叠 `×N` 容量账要重算。
**成本硬数据(已有,⛔ 非估算)**:加密 CPU 开销 = `p50 22.636 → 42.782 ms`(**`1.890×`**,含 切块/加密/put/get/解密**全链**;⚠️ 口径界 = 单核、页缓存全热、两臂零回源 ⇒ **"加密层自身成本",⛔ 不是线上 `E1` 读数**)—— 🔑 **与是否加盐无关**|共享的收益 = `1 组×16 节点 = 1.0000×`(对照 `16×`)、中继带宽节省 `75%(4 人)/ 93.75%(16 人)`。
⚠️ **若立项 ⇒ 属方案类文档**(须落 `04-调整方案/<NN>` + 交接单),⛔ 本轮**未动任何受锁保护文件**(锁仍被「日志可观测面-实现-20260918」占用)。
---
## 21:5x · 用户令「**全网查相关文献和 GITHUB 找最佳解决方案**」(§10-3 加密去重)—— 检索收官 · 结论 = **文献印证「三不兼得」,⛔ 未推翻**
**检索范围**:学术 2 轮(收敛加密攻击 / DupLESS·SA-MLE)+ 工程 3 轮(RFC 9497 OPRF 实现 / MLE 去重代码 / 备份工具对照)。⛔ 零代码、零服务器改动(锁仍被占)。
### 一、文献侧 = **印证**上节结论(三者互斥),⛔ 不是推翻
| 结论 | 出处 | 关键原文 / 数据 |
|---|---|---|
| **收敛加密对可预测内容必被暴力攻破** | Bellare, Keelveedhi, Ristenpart, **Eurocrypt 2013**(MLE 原论文) | *brute-force attacks exist for **all** message-locked encryption schemes*;MLE **仅**对 unpredictable messages 安全 |
| 暴力速度量级 | DupLESS 官方页 | **up to 12,000 attempts/sec**(单核、短文件)+ **完全可并行** ⇒ 与本项目实测 `85,878 条/秒` 同量级(我们更快是因为实现更粗 / 值域更小) |
| 🔴 **相等性泄露是"必要代价"、不可消除** | **DupLESS 官方页原文** | *Effectively, semantic security, **except that ciphertexts leak equality of the underlying plaintexts. The latter is necessary for deduplication.*** |
| KS 被攻破 ⇒ **回落收敛加密**级 | 同上 | `compromise resilience` = "**不更差**",⛔ 不是"更强" |
| 还有一类我们**未覆盖**的攻击 | **Hack Tahoe-LAFS!**(Perttula & Warner, 2008) | 除 confirmation-of-a-file 外另有 **learn-the-remaining-information (LRI)** |
| 加盐 / 域分离能抗上述攻击,**但去重域随之收窄** | xn--2-umb.com「Convergent Encryption」 | `HA` 改 **keyed hash** ⇒ 去重仅限同 `KA` 域内;Tahoe-LAFS 方案 = 共享秘密 `K = H(S‖M)` ⇒ **秘密泄露即全崩** |
**⇒ 判定**:DupLESS / SA-MLE 抗的是「**离线暴力恢复明文**」,⛔ **不消除「相等性泄露」**(它**必须**保留相等性才能去重)。故上节「**绝对安全 · 性能不受影响 · 共享去重 —— 三者互斥**」**成立且已被文献确证**;能争取的只是**把攻击成本从离线抬到在线限速**。
### 二、工程侧:**现成实现已存在** ⇒ ⛔ 无需自研密码学
- 🔴 **RFC 9497(2023)已标准化 OPRF / VOPRF / POPRF** ⇒ DupLESS 用的 OPRF 不再是"论文原语"而是**正式标准**。可用实现:
· `facebook/voprf` —— **Rust**,**MIT / Apache-2.0 双许可**,76★,`v0.6.0-pre.0`,MSRV 1.83,2025-11 仍在更新(**生产级,首选**)
· `bytemare/voprf` —— **Go**,MIT,**另含 TOPRF(阈值 OPRF)+ DKG**(对应下面「去单点」)
· `oprf` —— **Python**(`pip install oprf`,Ristretto255,可纯 Python 跑 ⇒ **适合本机原型验证**)
- **DupLESS 官方代码可下载**(UCSD 页 → *Download Code*);原型 KS 官称"20 行 Python + Apache on EC2"。
- **性能对照(DupLESS 实测,1 MB put)**:总时间开销 **≈17%**、带宽开销 **<1%**;分解 = KS 交互 `371 ms(9.1%)`/TLS `243 ms`/加密 `49 ms`/上传 `3676 ms(89.7%)`,合计 `4099 ms`|存储开销 `3L+120 bytes`|🔑 **仅 store 需 KS 交互**,KS 不可用 ⇒ **fail-safe 回落常规加密**。
⚠️ **口径界**:2013 年 Dropbox/EC2 单机实验,⛔ 不可外推到本平台;但「**加密/KS 开销占比远小于 I/O**」这一结构与本项目实测(加密全链 `1.890×` 但绝对值仅 `~20 ms`)**方向一致**。
- 可选后续读:`BL-MLE`(块级双重加密)|`UMLE`(块二叉树,更新 `O(logM)`)|`FuzzyMLE`(相似性哈希 ⇒ 相似但不相同也能重删)|`RekeyDedup`(**MLE 密钥不可撤销 ⇒ 引入 rekeying**,正对应我们"块 id 须换口径")|`MetaDedup`(CUHK,降元数据开销)。
### 三、对本项目的**结构性发现**(本轮新增 · ⛔ 尚无任何文档记录)
1. 🔑 **DupLESS 的 KS 可直接落成我们的 Manager(47)** —— 本项目已有「**权威状态单点 Manager**」「归属/租约只有 Manager 能写」的既有判据 ⇒ **架构天然吻合,不需要新增组件**。⚠️ 但须先定 **KS 的域边界**(全局一个 / 每 network 一个)—— 它**直接决定去重域**。
2. 🔑 **文献已给出 DupLESS「KS 单点故障 / 效率瓶颈」的解法** ⇒ `eprint 2013/807` **Distributed Key Generation for Secure Encrypted Deduplication**:给出 DupLESS 在 ROM 下的**严格安全证明**,并用**分布式密钥生成**消除 KS 单点,使 **P2P 系统**同样可达该安全级。⚠️ **与本项目 P2P/覆盖网络形态高度契合** ⇒ 若走 B 路线**应直接上阈值/分布式 OPRF(TOPRF),⛔ 不做单点 KS**(`bytemare/voprf` 已含 TOPRF + DKG)。
3. ✅ **一个"代价≈0"的真发现**:把块 id 的 `HA` 从裸哈希改成**带每个-network 密钥的 keyed hash(域分离)** ⇒ 可抗 COF / LRI / 离线枚举;而**跨 network 本来就不该去重**(不同租户 / 不同覆盖网络)⇒ **对本项目代价≈0**。⚠️ 但它**只切断跨域攻击**,**同一 network 内部持钥者枚举(`85,878 条/秒`)依然存在** ⇒ 是**低成本加分项,⛔ 不是 §10-3 的解**。
4. ⚠️ **第三条路(Borg / Restic 做法)值得单列**:两者均为「**加密前分块 + 明文哈希索引 + 随机 nonce 加密**」⇒ **明确放弃"相同明文 ⇒ 相同密文"**,换取不泄露相等性;原文一句 *client-side deduplication and strong encryption are **mathematically incompatible unless you separate the two***。⇒ 对本项目 = **`1.0×` 回源直接失效**(即上节选项 C)。
🔴 **关键推论**:`1.0×` 回源**不是"白拿的性能"**,**它的价格就是 §10-3 这条泄露**。⇒ 「既绝对安全、性能又不受影响」的方案,**在文献层面不存在**。
### 四、结论与边界
- **可答复用户**:文献 + GitHub **均未给出**「绝对安全 / 性能不受影响 / 保持共享去重」三者兼得的方案;可行性被**定理级**排除。可得最优 = **把离线枚举抬成在线限速查询**(DupLESS / OPRF 路线,RFC 9497 已有成熟实现),且**仍保留相等性泄露**。
- ⛔ **本轮零改动** ⇒ 上述第 3 条「域分离」与第 4 条「Borg 路线」**均为候选、未立项**;第 1/2 条为**架构判据补充**,未动任何受锁文件。
- 📄 本轮检索已整理为独立报告:`调研_MLE加密去重最优方案_20260918.md`(**工作区根**,⛔ 非文档库 —— 待锁释放后再定是否并入 `04-调整方案/`)。
---
## 21:2x · 用户问「B 方案改造复杂吗 / 能提升多少安全性 / 举例」(⛔ 零改动 · 只读取证 2 次)
**取证**:`src/net/relay/content/chunker.ts#blockIdOf` = `sha256(字节) 前 32 hex`(**只由字节决定**)|`src/net/relay/content/crypto.ts` = 组密钥实现|`package.json` **依赖极简**(fastify / better-sqlite3 / pg)⇒ 🔴 **零第三方密码学库,全走 `node:crypto`**;✅ 但 **`@fastify/rate-limit ^10` 已在依赖里 ⇒ KS 限速中间件现成**。
### 一、改造复杂度 = **中等偏低**(6 项 · 唯一真难点是 OPRF 原语)
| # | 改动 | 量级 | 说明 |
|---|---|---|---|
| 1 | Manager 侧新增 KS 端点 | **小** | 复用 fastify + 现成 `@fastify/rate-limit`;一条路由 + 一把密钥 |
| 2 | 客户端密钥派生改造 | **中** | `content/crypto.ts`:`k = HMAC(key,…)` ⇒ `mleKey = OPRF_KS(H(明文))` |
| 3 | 🔴 **OPRF 原语实现** | **难(唯一)** | 无现成依赖 ⇒ 引 `@noble/curves`(纯 JS 零 native)**须走 dsh 依赖披露**(`package.json#disclosure`);或降级为**非盲化 HMAC 派生**(零依赖,但 **Manager 会知道块指纹** ⇒ 信任模型变化) |
| 4 | fail-safe 降级 | 小 | KS 不可达 ⇒ 回落常规收敛加密 |
| 5 | `E1` 判据扩展 | 小 | 加 KS 交互 / 降级项 |
| 6 | ✅ **块 id / 存储 / 去重率** | **零** | 🔑 **OPRF 对同输入同输出 ⇒ 确定性保持 ⇒ `blockIdOf` 不用改、`storeBytes` 不翻倍** |
🔑 **关键**:B 比"按熵分层加盐"**更简单** —— 因为它**不动块 id 口径、不动去重率**(加盐方案两样都要动)。
### 二、安全性提升(分场景量化 · 本项目实测 `85,878 条/秒` vs DupLESS 限速 `≈3.3 条/秒`)
| 攻击者 | 现状 | B 后 | 提升 |
|---|---|---|---|
| **中继(无密钥)** | 可判相等性(`10,000 块/100 值域 ⇒ 100 种密文`) | 🔴 **完全一样** | **1×(零改善)** |
| 外部攻击者(无 KS 认证) | 离线 `85,878/秒` ⇒ 域 `2³²` **13.9 h** | 无法离线派生 + 查不了 KS | **不可行** |
| 组内持钥者(能认证) | `85,878/秒` ⇒ 域 `2³²` **13.9 h** | `3.3/秒` ⇒ 域 `2³²` **41.3 年** | **≈ 26,000×** |
| 组内持钥者 · **极低熵**(域 100) | `1.16 ms` | `30.3 s` | 26,000× —— 🔴 **但两边都是"秒破"** |
| Manager 被攻破 | —— | 回落收敛加密(= 现状) | **不更差** |
🔴 **核心洞察**:**B 的收益 ≈ 2.6 万倍,但只对「高熵且可枚举」有效**;对**低熵块**(本项目最担心)**绝对值两边都是"可破"** ⇒ **B 治不了 §10-3 的主症状**,只治"离职成员带密钥枚举 ID/口令"这类。
### 三、四例(用户要求举例)
1. **B 有效** —— 配置块 = 32 位设备 ID(域 `2³²`):现状组内成员一台笔记本 **14 小时**枚举全组;B 后须逐条问 KS ⇒ **41 年** ⇒ 从"一个下午"变"不可行"。
2. **B 无效** —— 首屏包里的布尔状态块(域 2):**中继不需要密钥就能数出统计**,B 后**一样数得出**;持钥者 `0.02 ms` ⇒ `0.6 s`,**两边都是立刻破**。
3. **B 无效** —— 跨 network 相关性:中继仍能从"同一块 id 同时出现在 A / B 两个 network"推出"两租户共享同一份内容";**B 后照样推得出**(确定性保持 = 去重保持)。
4. **B 的兜底价值** —— Manager 的 OPRF 秘密若泄露 ⇒ 回落收敛加密级 ⇒ **回到今天的水平,不会更糟**(这是敢上 B 的理由)。
**⇒ 一句话**:B = 把"离线暴破"变成"在线限速查询" ⇒ 对高熵可枚举资产 **2.6 万倍**(14 h → 41 年);对**低熵块与中继侧泄露近乎无用**。改造**中等偏低**,最大成本在 **OPRF 原语 + 依赖披露**,最大**利好**是**确定性保持 ⇒ 不动块 id、不动存储**。
---
## 21:2x · 用户追问「低熵(域 100)有什么互补方案 / B 是不是当前最合适的方案」(⛔ 零改动 · 零工具取证)
### 一、先厘清威胁模型(**本轮补正 · 决定解法方向**)
收敛加密下 **持组密钥 ≠ 能解密** —— 块密钥含明文成分(`iv = HMAC(key,"iv"‖明文)` ⇒ `k = HMAC(key,"k"‖iv)`)⇒ 持钥者只能"**猜明文 + 验证**"(`85,878/秒`)。
🔴 **推论**:低熵块的唯一有效解 = **把"明文成分"从块密钥里彻底拿掉(非确定性加密)**,⛔ **不是去限速** —— 限速挡不住"域只有 100"。
### 二、低熵互补方案族(5 类 · 按性价比)
| 方案 | 治低熵 | 治中继相等性 | 代价 |
|---|---|---|---|
| **A 现状 + 披露** | ❌ | ❌ | 0 |
| **B OPRF / SA-MLE** | ⚠️ 仅到 30 秒(**仍可破**) | ❌ **零改善** | 中(OPRF + 披露 + 在线依赖) |
| **C 域分离(per-network keyed hash)** | ❌ | ⚠️ 只治跨 network | **≈ 0** |
| **D 低熵块非确定性 / 合并加密** | ✅ **唯一有效** | ⚠️ 该块失去去重 ⇒ 中继也失去抓手 | **小**(低熵块体积小、种类 ≤ 100) |
| **E 低熵字段移出块存储**(放实例 home / 元数据层) | ✅ | ✅ 该字段根本不进网络 | 小-中 |
🔴 **D 的两种实现(推荐 D-1)**:
- **D-1「稀释域」合并加密** —— 把低熵小字段与高熵内容(用户 ID / 时间戳 / 会话号)拼在一起再加密 ⇒ 域 `100 ⇒ 100×2³²` ⇒ 不可枚举。🔑 **代价 ≈ 0,不动密码学、不动块 id 口径**,只牺牲这个合并块的去重(低熵块去重本无价值)。
- **D-2 随机密钥**(每块随机 key + key 随内容封装)—— 彻底非确定性;代价 = 该块不去重,且需**新增密钥封装配送**。
### 三、判定:**B 不是"最合适"的,它是"最贵且收益最窄"的那个**
- **性价比排序:C + D > E > B > A**(C/D 治的正好是主症状,代价小得多)。
- **B 的独特价值只在「大域暴破」这一格**(设备 ID / 口令类),而这**恰是本项目优先级最低的风险**(域大本来就难猜)。
- ✅ 公平说:B 有一个别人没有的性质 —— **不破坏去重**。而 D 牺牲低熵块去重(价值低)、C 牺牲跨 network 去重(本不该去重)⇒ **C/D 的"代价"都踩在不值得保留的地方**。
- ⚠️ **真正有效的低熵组合 = B(限速)+ D(非确定性)**;**B 单独不够**(30 秒仍可破)。
### 四、结论与待测
- **自决判断(技术项)**:**最合适 = C + D**;**B 降级为可选加强**,仅在"确实存在大域可枚举资产"时才值。
- **前置待测**:低熵块的**种类数 / 体积 / 占首屏包比例**(决定 D 的边界;⛔ 本轮仍未测)。
- ⛔ 本轮**零代码、零服务器改动**;⛔ 未动任何受锁文件(锁仍被「日志可观测面-实现-20260918」占用)。
---
## 21:3x · 用户令「按照你的建议优化,可以建立接续会话处理」—— 已登记**一棒**规划棒(⛔ 零改动)
**用户拍板(2026-09-18 21:28)**:采纳 **C + D** 方向;**B(OPRF / SA-MLE)降级为可选加强、⛔ 不立项**。
**已登记(唯一一棒 · 一次性)**:
- 名称 = `覆盖网络线 §10-3 低熵块治理 · 规划棒`
- 🔑 **id = `b188a495-033b-4da4-87d4-f23ced466ccb`**(来自工具返回值,⛔ 非占位)
- `scheduledAt = 2026-09-18T21:35`(`nextRunAt = 1789738500000` ⇒ 🧾 已用 `date -d @…` 复核 = **2026-09-18 21:35:00** ✅)
- `cwds = E:\ProgramData\AIProject\ai1net-dsh-server`
- 判据:**收口时刻(21:29)+ 6 分钟** ⇒ 符合「首个接续 5–8 分钟」铁律;⛔ **同时只挂这一个**,下一棒由当棒收官时再排。
**该棒任务(写入 prompt 的要点)**:跑 `state.py` ⇒ **先抢全局执行锁(抢不到只报告、⛔ 不删别人锁)** ⇒ 为 §10-3 低熵块治理立**方案类**文档(`04-调整方案/<NN>` 复跑取号 + `.lock-<NN>` 原子占号 + `交接单/`)⇒ **第一个动作为补测「低熵块种类数 / 体积 / 占首屏包比例」**(从未测过,决定 D 的边界)。边界:⛔ 不改生产 / ⛔ 不重启在线服务 / ⛔ 不引依赖 / ⛔ 不动 `package.json` / >10 文件先出清单。
**⛔ 本轮零代码、零服务器改动**;⛔ 未动任何受锁文件(锁仍被「日志可观测面-实现-20260918」占用)。
---
## 21:35–21:5x · 覆盖网络线 **序㊺ 规划棒(§10-3 低熵块治理 · 立方案 + 派执行棒)** —— ✅ 收官 · ⛔ 零代码 / ⛔ 零服务器改动
**用户拍板(2026-09-18 21:28)**:采纳 **C + D**;**B(OPRF / SA-MLE)降级为可选加强、⛔ 本轮不立项**。
**产出(4 处落盘)**
1. 方案 = `04-调整方案/133-覆盖网络-低熵块治理方案-C域分离与D非确定性.md`(**原子占号 133**;⚠️ **130 空号未补占** —— 记忆里 130 已言明归「单 B」,131/132 已被锁占)
2. 交接单 = `交接单/覆盖网络-序45-低熵块治理-测熵与实现.md`(8 段模板;**§8 前前缀 `be548afc3340583b2b63ca254bcf550f`**;全文件 md5 `0169b900a6ea50608c0d50a3a0514e68` · 210 行)
3. `交接单/README.md §一` 新增一行 + `INDEX.md §二` 新增 `04-133` 行(⚠️ 机器生成的**状态摘要**用 `docs-index-stats.py --write` 刷新 ⇒ 档案 **115 → 117** 份,差额 = 129(他棒遗留未计数)+ 133)
4. `docs-manifest.json` 已刷新
**🔑 本轮新增的源码级结构发现(⛔ 尚无任何文档记录 · 待实测确认 · 决定方案形状)**
| 事实 | 出处 | 推论 |
|---|---|---|
| 切分是**定长 1 MiB**(`offset += blockSize`,块大小是**常量非配置**) | `chunker.ts:60` + `:137` | 首屏包 10.8 MB ≈ **11 块、每块混高熵内容** ⇒ **首屏包内几乎不可能有纯低熵块** |
| 块 id = 裸 `sha256(字节)` 前 32 hex(`blockIdOf`);内容 id 同理(`contentIdOf`) | `chunker.ts:108-115` | ⇒ **C 必须同时改两个函数**,⛔ 只改一个会留"内容指纹仍裸哈希"缺口 |
| 块密钥含明文成分:`iv = HMAC(key,"iv"‖plain)` ⇒ `k = HMAC(key,"k"‖iv)` | `crypto.ts:405-413` | ⇒ 🔑 **持组密钥 ≠ 能解密**(本题前提);低熵可枚举 = "猜明文+复算",⛔ 不是"直接解密" |
| 块存储**纯内存 + 可选 dir**,装配处**没给 dir** | `server.ts:909-911`、`runtime.ts:347-350` | ⇒ 生产盘上**无块样本可捞** ⇒ 测熵只能对**真实内容**离线做 |
⇒ **两条方向相反的结论**:**①** D-1「稀释域」对**首屏包**几乎免费也无收益(低熵已被同块 1 MiB 高熵内容稀释);**②** 🔴 **D 的真实战场 = 「整体小于 1 MiB 的独立内容」**(只切 1 块;本身低熵 ⇒ 纯低熵块)。
⇒ 🔴 **因此测熵只测首屏包会得出"没有低熵块"的假绿** ⇒ **必须加子窗口熵腿**(滑窗找块内低熵片段)—— 这是本方案 `M1-d` 的由来。
**`M1`(= 本轮定义的"第一个动作")四项指标**:种类数/重复率 | 低熵块数 + 体积 + **H 直方图** | 占首屏包比例 | **子窗口熵尺寸分布**。
⚠️ **为什么必须先测**:D 的边界(什么算低熵 / 稀释单元多大 / 要不要合并)全是它的函数;且**历史所有"低熵"读数都是合成字节** —— `_tmp_seq32/p01-perf.mjs:42` 用 `makeBytes()`、`_tmp_seq32/p03-lowentropy.txt` 用 8 B 合成块 ⇒ **从未对真实内容测过**。
**🔴 发现一条既有漂移(⚠️ 只报不改)**:参数表 §10 头值记 `d408d640246a980f702fe7b0a2895219`(序㊴ 12:2x 记录),**现算 = `6b37bfd506758d882d9f803678f85d23`**(830 行 · mtime 09-18 15:18)⇒ **记录值未随 §10 之前的内容更新**。⚠️ 与序㊹ 自报值**一致**(两次独立计算同值)⇒ 不是算错、是漏更新。⇒ 已登记为交接单 §10 未验证项 1(执行棒取基线**一律用现算**)。
**边界自证** = ⛔ 零代码(未改 `src/**` / `scripts/**` / `test/**`)· ⛔ 零服务器触碰(未 ssh / 未 scp / 未 build / 未重启任何单元)· ⛔ **不 commit / 不 push**(CODEBUDDY §4:未明确要求不提交 ⇒ 本棒未提交;未提交改动 = 本棒 4 处)· ⛔ 未动 `参数表`(指纹现算仍 `6b37bfd5…`)· ⛔ 未动 `package.json` / `DEFAULT_BLOCK_SIZE`。
⚠️ **两条如实留档**:① `docs-sync-check.sh` 报 **`无法读取服务器目录 bt-server:/opt/dsh/docs(ssh 失败或目录不存在)`** ⇒ **未能双端对账**(规划棒不 scp、未推送 ⇒ 不阻塞;⚠️ 执行棒推送前必须复跑成功)② `docs-audit.py` **退出码 0**(无 P0)。
**➡️ 下一棒(唯一一棒 · 已登记)** = 序㊺ 执行棒,automation **`f94f2973-4ead-471a-a020-df6dc0b144a4`**(一次性 · **2026-09-18 21:56** · `nextRunAt` = **1789739760000** ⇒ `date -d @…` 复核 = 21:56:00 ✅ · 收口 +~6 min,落在 5~8 分钟窗口内)。已同步推进入口 `接续入口_覆盖网络线_20260916.md` **§0 + §2**(含真 id)。
📌 **在册待你拍板项仍 = D8**(云安全组乙-1;⛔ 不进排棒队列、⛔ 不阻塞排棒)。
---
## 2026-09-18 21:56–22:2x · 覆盖网络线 **序㊺ 执行棒**(低熵块治理 · 测熵先行)收官
**判定 = 「`M1` 首次实测完成 · 🔴 **D 的作用面为空 ⇒ D 本轮不实现**」**。一句话 = 「低熵块」第一次被**真实内容**量了 —— 首屏包 **0 个低熵块**、低熵物质只以 **≤ 80 KiB 块内片段**存在(**1.0225%**)、**60 份真实独立小内容 0 份低熵** ⇒ **D(非确定性)当前没有对象可治**,而 **C(域分离)仍值得做**。
**① `M1` 四项读数(真实内容 · ⛔ 零合成字节)**:S1 = `GET https://admin.alotbuy.com/` 壳页 → **按页面顺序**取**全部 60 条** `/plugins/??…&rev=…` 拼接(**23,629,336 B**;🔴 **口径纠正**:历史 `11,363,655 B` = **最大单条 combo**(本棒实测 **11,794,471 B**),⛔ 非全部之和)⇒ **`M1-a`** 块 23 / 唯一 23 / **重复率 0.00%**|**`M1-b`** 低熵块 **0 块 / 0 B**,逐块 H ∈ **[5.1807, 5.8444]**,直方图 `{5.0-5.5: 8, 5.5-6.0: 15}`|**`M1-c`** **0.000000%**|**`M1-d`** 4 KiB 窗 5,768 ⇒ **低熵段 15 段 / 241,664 B = 1.0225%**(max 段 81,920 B)|**S2** 60 份中单块 **56**、**低熵 0 份**。
**② 判定与处置**:**D 的目标已由「定长 1 MiB 切分」结构性达成**(低熵物质只以 ≤ 80 KiB 块内片段存在 ⇒ 对块级去重 / 枚举**不可见**;且**无低熵内容单元**)⇒ `D-1` 无对象可稀释、`D-2` 前提不成立(`04-133` 决策点 4 **两分支都不命中**)⇒ **D 本轮不实现**(⚠️ **非降级** —— 实测已证目标达成;🔑 **回头条件 = 出现「低熵内容单元」**)。⇒ **步骤 4–5 的前提被证伪**(`OBS-29` 原负腿「稀释源换确定性派生量 ⇒ 必红」**失去对象**)⇒ 若继续落 C 而沿用原判据面 = 交付**没有判据**的代码 = 本线明令禁止的"静默放行" ⇒ **C + 判据重裁一并交序㊻**。
**③ 产物** = **`scripts/overlay-entropy.cjs`**(只读探针 · 零第三方依赖 · **切分调用仓库那份 `chunkify`** = `lib/net/relay/content/chunker.js` ⇒ ⛔ **无双源**;`OVERLAY_CHUNKER` 可覆盖)+ 参数表 **`§11.12 补记`**(行 829 起;**⛔ 未新增键 / ⛔ 未改值 / ⛔ 未改阈值** ⇒ **§10 指纹现算仍 `6b37bfd506758d882d9f803678f85d23`**,写入前后两次同值 ✅)+ 本单 **§12 回填** + `交接单/README.md` + `INDEX.md`(`docs-manifest.json` 已刷新)。
**④ 边界自证**:⛔ **未改 `src/**` 一行**(C 未开工)· ⛔ 未动 `DEFAULT_BLOCK_SIZE` / `package.json` · ⛔ 未改生产实例 / ⛔ 未重启在线服务 · ⛔ **不 commit / 不 push**(HEAD **`45b4999`**;`git status --short` **13 处**,**本棒唯一新增 = `scripts/overlay-entropy.cjs`**)· 🔴 **临时物已清**(47 `/tmp/s45`;PG 临时会话 `user_agent='seq45-entropy'` ⇒ `remaining=0`)· `docs-audit.py` **rc=0**(无 P0)。
**⑤ 三条如实留档**:**①** 🔴 **仓里 `mksess.cjs` 已失效** —— 它写 SQLite `/var/lib/dshs/dshs.db`,而 47 控制面**权威库 = PG**(`dshs.service.d/cluster.conf` 的 `DSHS_DB_URL=postgres://dshs@127.0.0.1:15432/dshs`)⇒ 插进去的会话 Manager **查不到** ⇒ 一插就 **401**;**正确做法(本棒实测可用)** = 往 **PG `sessions` 表**插(cookie 名 `sid`;`token_hash` = sha256hex(token))+ 用完即删(**待收口小项**);**②** `/plugins/??…` **有鉴权**(无会话 = **401 / 24 B**)且**盘上无"首屏包单文件产物"**(combo 由宿主**运行时**拼装)⇒ 测熵取数只能 HTTP + 临时会话;**③** ⚠️ **"低熵"阈值 `H ≤ 4.0` 判别力有限**(JS 文本字节熵天然落在 **5.0–6.0** ⇒ 单用恒 0)⇒ 后续任何低熵结论**必须同时给 `M1-d` 子窗口腿**。⚠️ `docs-sync-check.sh` **rc=2**(与规划棒逐字同:`无法读取服务器目录 bt-server:/opt/dsh/docs`)⇒ **既有条件**、本棒未 scp / 未推送 ⇒ 不阻塞,🔴 **执行棒推送前必须复跑成功**。
**➡️ 下一棒(唯一一棒 · 已登记)** = **序㊻ 执行棒(C 域分离 + 判据重裁)**,automation **`aab9e357-8101-4011-b829-bf9f4a459bf7`**(一次性 · **2026-09-18 22:33** · `nextRunAt` = **1789741980000** ⇒ 收口(22:2x)+~7 min,落在 5~8 分钟窗口内)。已同步推进入口 **§0 + §2**(含真 id)。
📌 **在册待你拍板项仍 = D8**(云安全组乙-1;⛔ 不进排棒队列、⛔ 不阻塞排棒)。🔴 **新增在册项 = `mksess.cjs` 失效**(见 ⑤①,**待收口小项**,⛔ 不阻塞)。
---
## 序㊻ 执行棒 · C(块 id per-network 域分离)+ `OBS-29` 判据重裁(2026-09-18 22:33–23:4x)
**判定 = `C` 全绿 · 零回归三件套全绿 · ⏳ 唯一未办 = 真机腿(须部署)**
**① 交付(8 文件 · 代码仓 `D:/github/dsh_shenxian`)** = `src/net/relay/content/{chunker,crypto,store,runtime}.ts` + `src/net/relay/{main,index}.ts` + `test/overlay-content.test.mjs` + `scripts/overlay-probe.cjs`。块 id 口径换代:`blockIdOf` / `contentIdOf` 由**裸 `sha256`** ⇒ **`HMAC-SHA256(netKey, bytes)` 取前 32 hex**;per-network 密钥**复用 `crypto.ts` 既有组密钥链**派生(`deriveBlockIdKey()` + `BLOCK_ID_DOMAIN_TAG = 'dshs-overlay-block-id/v1'`)⇒ 🔑 **零新密钥文件 / 零新 env**;🔴 **缺省或空 `netKey` ⇒ 回落裸 `sha256`**(= **回滚路径** + 保住既有单测语义);`encode` 与 `netKey` 由新增的 `writeTransforms()` / `readTransforms()` **同生同灭**(⛔ 不许半开)。观测面 `snapshot()` 新增 `blockIdKeyed?` / `blockIdKeyId?`(**未启用时整体缺席**,⛔ 不写 `false`、⛔ 不写空串)。
**② 判据重裁(本棒存在的主因)** = 序㊺ 实测判 `D` 无作用面 ⇒ 原 `OBS-29` 那条"稀释源换成确定性派生量 ⇒ 必红"的负腿**失去对象** ⇒ **重裁为 `C` 的判据**:**正腿 3**(`P1` 跨网必不同且**两侧都 ≠ 裸哈希** / `P2` 同网必相同=**去重不得丢** / `P3` 装配面无漏改)+ **具名负腿 2**(`flat-key-collapses` / `empty-key-falls-back-to-bare-hash`)。🔴 **两条负腿都真跑过**(先红后绿,⛔ 非纸面声明):**红腿 A**(派生**丢掉 `network` 维度**)⇒ 单测 48/54、**6 红**(`G1`/`G1-b`/`G3`/`G5`/`G7`/`G8`)、探针 `腿数 4/5 ❌ 缺 P1`;**红腿 B**(`writeTransforms()` **漏传 `netKey`** = 漏掉一个调用点)⇒ 单测 45/54、**9 红**、原文 `content-store: 块校验失败(丢弃)expected=9aef9126… actual=e1311d71…`。两条均已**复原**(md5 回位、`REDLEG` 计数 **0**)。
**③ 读数** = `npm.cmd test` **200 pass / 0 fail / 1 skipped**|`overlay-content.test.mjs` **55/55**(新增**组 G** 共 10 例,含 `G10` 的 `E1` 重取)|`npm.cmd run check:layering` **无新增违规**|探针 **29 PASS / 0 FAIL / 0 SKIP**(基线 28 ⇒ **+1 = `OBS-29`**)|`--scene all` **12 PASS / 0 SKIP / 0 FAIL**(rc=0;**8m25s**)。**`E1` 基线已重取**(**定义不变** = 回源字节 ≈ **1 份 × 组数**;**旧读数作废**):同网 4 轮 × 4 块**全 local 命中**、`origin = 0`、`putRejected = 0`、`puts = dedupIds`;跨网**各 1 份**(起点 `local = 0`)。
**④ 本棒发现的四条既有缺陷(⛔ 均只报不动手 ⇒ 另立小项)**:
**(a)** 🔴 **演练脚本 `currentChannelHint()`** 用 `journalctl -u dshs --since -6h` 捞**每 2 s 一条**的 `[overlay-dir] 取址`(6 h ≈ **1 万行 / ≈1.3 MB**)⇒ **单次 ssh 实测 ~47 s > `SSH_TIMEOUT_MS` 20 s** ⇒ 整轮 `spawnSync ssh ETIMEDOUT` **中止**。⚠️ 该函数**源码自述「仅供人读,不参与判定」** ⇒ **装饰性线索不该是主流程的硬依赖**(处置方向:加长超时 / 缩小 `--since` / 失败降级为空串)。
**(b)** 🔴 **演练 `lastManagerAuthOn()` 的 6 h 上限** ⇒ Manager 隧道**稳定超过 6 h 就不再重注册** ⇒ 两台 relay 都读 **-1** ⇒ `killTarget = null` ⇒ `幕1-A` 降级 **SKIP** + **`幕1-B` / `幕1-C` 结构上位于 `else` 分支(脚本 863–866 行)⇒ 一行都不打印**(**12 腿里 3 腿未判**)。**已取证 + 已先红后绿**:开工前两侧最后一条 AUTH = `1789720529`(47) / `1789720501`(106),开工 = `1789744400`(**23:13:20**)⇒ 间隔 **≈6.63 h > 6 h**;首跑 **9P/1S/0F** ⇒ 按脚本自述办法**重启一次 47 `dshs`** 刷新 AUTH(`1789745060` = **23:24:20**)⇒ 复跑 **12P/0S/0F**。⇒ 🔴 **是判据口径缺陷,⛔ 不是产品回归、⛔ 与本棒 C 改动无关**(演练脚本本棒**零改动**)。
**(c)** ⚠️ **`mksess.cjs` 已失效**(写 SQLite,而 47 控制面**权威库 = PG** ⇒ 一插就 401)。
**(d)** 🔴 **`docs-sync-check.sh` rc=2 的**真因 = ssh 别名 `bt-server` 端口陈旧**(`~/.ssh/config` 里 `Port 32022` ⇒ `Connection refused`;脚本第 88 行**不带 `-p 22`**)。⚠️ **不是"目录不存在"** —— `ssh -p 22 bt-server 'ls -d /opt/dsh/docs'` ⇒ **存在、EXIT=0**。
**⑤ 边界自证** = ⛔ 未 commit / 未 push(HEAD **`45b4999`**;`git status --short` **21** = 序㊺ 遗留 **13** + 本棒 **8**)|⛔ **未 scp / 未部署**|⛔ 未动任何配置值 / nft·nginx·bwrap|⛔ 未改 `DEFAULT_BLOCK_SIZE` / `package.json` / 零新依赖。⚠️ **对生产的唯一动作 = 重启 47 `dshs` 一次**(**R8**;为满足演练的 6 h AUTH 窗口;无不可逆项)。**演练后生产态核对** = 47 `dshs`/`dshs-relay`/`dshs-worker` **全 active**;106 `dshs-relay`/`dshs-worker` **active**(`dshs` inactive = **正常**);门户 **200**。
**⑥ 产物落点** = 交接单 `dsh-server-docs/交接单/覆盖网络-序45-低熵块治理-测熵与实现.md` **§13 全节**(`§13.1` 如实留档 + `§13.2` 零回归三件套;⛔ **§8 前前缀 `be548afc3340583b2b63ca254bcf550f` 逐字不变**,已复算)+ 参数表 `参数表_覆盖网络_20260917.md` **§11.13 补记** + **`OBS-29` 行**(§6 判据表 · 行 430)+ §10 指纹 `d408d640…` → **`ac6bbbbb8c92bd57ff0dc4cd8f4983ba`**(**现算并已同步**;⚠️ §11.13 在 §10 口径之外 ⇒ 追加后指纹不变,已复算)。`docs-audit.py` **rc 0**(无 P0)|`docs-manifest.py` **rc 0**(已重生成 `docs-manifest.json`)。
**➡️ 下一棒(唯一一个 · 已登记)** = **序㊼ 部署棒(`C` 落地两机 + `OBS-29` 真机腿)**,automation **`92f8a1b9-5e38-4a7b-bd1b-7d7695e73d09`**(一次性 · **2026-09-18 23:43** = 本棒收口 +~6 min)。🔴 **要点 = 两机必须同一窗口内一起换**(`C` 改了块 id 口径 ⇒ **一台新一台旧 ⇒ 跨机取块全部判校验失败**)。📌 **在册待你拍板项仍 = `D8`**(云安全组乙-1)。
---
## 🆕 用户新需求登记 · 覆盖网络「密钥轮换 + 多路分发」(2026-09-18 23:42 提出 / 23:46 定「记录下来后续实现」)
**用户原话** = 「能否增加一个随机时间更换加密密钥的方式增加破解难度,甚至可以通过不同节点或打洞设备更新加密密钥,还可以多设备同时分发多个只有一个是真实的」→ 「可以记录下来后续实现」。
**状态 = ⏸ 待实现(⛔ 零实现)**。**落点 = 工作区根 `需求登记_覆盖网络_密钥轮换与多路分发_20260918.md`**(新建独立文件;⚠️ 因 序㊼ 部署棒持有全局执行锁,**未**写入口 §4 / 交接单 ⇒ 待其放锁后折进 §4)。
**评估要点(详细版见该文件)**:
1. 🔴 **先纠正前提**:用户以为"C + D 已采纳",实际 **C 已落地(序㊻)/D 未实现**(序㊺ 实测判 D 作用面为空:23 块 0 低熵、60 份小内容 0 低熵)。
2. 🔴 **本需求三条全是"抗密钥失窃(乙)",都不解决已实测证伪的"相等性泄露/低熵枚举(甲)"**;不能替代,只能叠加。
3. **第 1 层(随机换钥)建议采纳,形态收窄为「随机间隔(6–24 h)而非随机时刻」**;🔴 关键代价 = **轮换 ⇒ 块 id 全换 ⇒ 去重归零、首轮回源回到 1 份 × 组数**(序㉛ 实测 24/24)⇒ 轮换每秒都在花带宽。🔑 **洞察:轮换 = 按时间窗做的 D**(D 逐次随机化 ⇒ 去重全死;轮换保留 epoch 内去重)。
4. **第 2 层拆两半**:**凭据**多路分发(不含密钥本体)**可立即做、零暴露面**;**密钥本体**经网络须 **R5 权限影响评估**(且它不提升对已失陷节点的抗性,只提抗关联/可用性/时延)。
5. **第 3 层(诱饵密钥)建议暂不采纳**:不解决「甲」+ 有自毁条件(真伪一落可见面即失效;否则 N× 试解)。
6. **实现前须用户拍板的一条** = **轮换周期口径(安全窗口 vs 回源放大)**,候选 A 不轮换 / B 随机 6–24 h / C 随机 1–6 h,优缺点已列在该文件 §4 —— ⛔ 留到规划棒开工前再问,本轮不问。
---
## 序㊼ 部署棒(2026-09-18 23:43 – 2026-09-19 00:1x)· `C` 落地两机 + `OBS-29` 真机腿
**做了什么**:把代码仓 `D:/github/dsh_shenxian` 的 `build` 产物 `lib`(**270 文件**)**全量**部署到两机 4 处 —— 47 `/opt/dshs/lib` + `/opt/dsh-relay/lib`、106 `/opt/dshs-cluster/lib` + `/opt/dsh-relay/lib`;**两机同一窗口内**换包 + 重启(47 `dshs-relay`/`dshs`/`dshs-worker`;106 `dshs-relay`/`dshs-worker`)。
**四条关键判定**:
1. 🔴 **部署口径 = 全量,不是"只传改动文件"** —— 实测两机 4 处 `lib` 是**历次增量 scp 的叠加**(47 `net/relay/index.js` mtime **12:55:27** vs 同目录 `content/chunker.js` **06:52:16**),与本机产物同名 md5 不同 **38–41 个**。⇒ 今后部署一律**全量替换** + **两机同窗口**。
2. ✅ **`OBS-29` 真机腿 PASS**:两机 `/status.content` 的 `blockIdKeyed=true` + `blockIdKeyId` **逐字相同**(`d6e62322e5166938`);第二来源 = 两机 relay 启动**判别器**日志原文 `[content] 块 id 口径 = HMAC-SHA256(域密钥) blockIdKeyId=…`。⇒ §6 该判据的"⏳ 真机腿"**结清**。
3. 🔴 **`OBS-09` 由 PASS 退化 SKIP = 既有缺陷被"重启 106 `dshs-worker`"触发**(⛔ 非本单引入、⛔ 非业务中断):实例 `286172` 健在且**未被 teardown**(`OBS-22 scanned=1 adopted=1 stopped=0` + `[rehydrate] probe OK dsh-100002-7d1c8cbf.scope :21001`),但 47 端点表**缺 `w-106:21001`**;**机理** = `relay-tunnel.ts#cancel()` 只撤销**当前进程** `forwarded` 集里的端口 ⇒ 遗留条目无人撤销、新进程**不重注册**已认领实例的端口;**恢复须"实例重新拉起"** ⇒ 本棒**不修、不凑绿**(另立小项 → 下一棒)。
4. **备份/回滚点** = 两机 `/opt/dsh/backups/seq47-20260918-2347/`(47 = 262/263 文件,106 = 260/262);**核对 = 1080 对 md5 全同、不一致 0**(270 × 4)。
**读数**:探针 **28 PASS / 0 FAIL / 1 SKIP**(⚠️ 基线 29P/0S/0F)|`--scene all` **12 PASS / 0 SKIP / 0 FAIL**(rc=0;**6m28s**)✅ 同基线|`npm test` ⛔ 未跑(本棒**零代码改动**)。
**边界**:⛔ 未 commit / 未 push(HEAD **`45b4999`**;`git status` 仍 21 处)|⛔ 未改 `DEFAULT_BLOCK_SIZE` / `package.json` / 零新依赖|⛔ 未改 nft·nginx·bwrap|⛔ 值格未动(§10 现算 **`ac6bbbbb8c92bd57ff0dc4cd8f4983ba`** = 与 §13 记录同值)|🔴 密钥本体不经网络 / relay。
**产物** = 交接单 `覆盖网络-序45-低熵块治理-测熵与实现.md` **§14** + 参数表 **§11.14** + 本入口 §0 / §2。
**下一棒** = **序㊽**(`OBS-09` 孤儿端点条目修复:让 worker 重启后能**重注册已认领实例的端口**)。