回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复: - 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录) - tmp/(32.4 M,按接续棒命名的过程临时区) - .workbuddy/tmp/(39.5 M) - 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物) - tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留 入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与 接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、 .workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。 排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、 打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
1283 lines
184 KiB
Markdown
1283 lines
184 KiB
Markdown
# 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\AI技能\aliyun-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\AI技能\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\AI技能\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\AI技能\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\AI技能\aliyun-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 重启后能**重注册已认领实例的端口**)。
|