docs: 契约补一条转发层依赖的坑(socat 需要 PartOf + Wants 两个方向)

DSH 的 LAN 转发在 2026-09-02 隐形挂了 9 小时:为验证离线补投重启 dsh,
dsh-lan.service 的 socat 被 Requires 带停后再没起来。

Requires 不含重启语义、PartOf 不含启动语义、单元自己的
WantedBy=multi-user.target 只在开机时生效 —— 三者缺一,restart 或
stop+start 之后就会出现「平台进程活着、loopback 通、外网全不通」,
而这个现象很难联想到转发层。

下一个平台如果也用 socat 暴露 loopback 端口会踩同一个坑,因此记进
第九节「部署环境的坑」。另附 SuccessExitStatus=143:被 SIGTERM 停掉是
正常路径,不加会在 systemctl status 里留一条红色 failed 掩盖真故障。

本机的 dsh.service / dsh-lan.service 不入库(dsh 是被接入方,不是本项目的
一部分),只把可复用的教训记进契约。
This commit is contained in:
2026-09-03 08:22:41 +08:00
parent 289f37f7fb
commit 22ddb1b89c

View File

@ -1003,6 +1003,31 @@ turn 崩溃。base bundle 已经注册了 agent-loop / llm / tools
| `ctx.logger.info` 看不到 | DSH 的 logger 不进 journalctl | 用 `console.error` | | `ctx.logger.info` 看不到 | DSH 的 logger 不进 journalctl | 用 `console.error` |
| `dsh --patch` 不生效 | `--patch` 只对 `dsh --profile <name>` 有效 | 配置写进 profile 的 `cordis.patch.yml` | | `dsh --patch` 不生效 | `--patch` 只对 `dsh --profile <name>` 有效 | 配置写进 profile 的 `cordis.patch.yml` |
| 第二个实例起不来 | task-board 的 ledger 文件锁 | 注入现有实例,不另起 | | 第二个实例起不来 | task-board 的 ledger 文件锁 | 注入现有实例,不另起 |
| 平台活着但外网访问不了 | 转发层socat / nginx没跟着平台重启 | 见下 |
**转发层的依赖要写两个方向。** 平台监听 loopback、靠一个 socat 单元暴露到 LAN
或公网时,只写 `Requires=` 是不够的:
| 指令 | 管什么 | 少了会怎样 |
|---|---|---|
| `Requires=平台` | 平台**停止**时转发也停 | —— |
| `PartOf=平台` | 平台**重启**时转发也重启 | `restart` 后转发永久消失 |
| 平台侧 `Wants=转发` | 平台**启动**时拉起转发 | 手工 `start` 后转发不起来 |
`Requires` 不含重启语义,`PartOf` 不含启动语义,单元自己的
`WantedBy=multi-user.target` 只在开机时生效 —— 三者缺一,
`systemctl restart <平台>``stop` + `start` 之后就会出现
**「平台进程活着、loopback 通、外网全不通」** 这个很难联想到转发层的现象。
2026-09-02 真实发生过一次,隐形 9 小时:为验证插件的离线补投重启了 dsh
socat 被 `Requires` 带停后再没起来。加 `PartOf` 后又发现 `start` 起不来,
才补上平台侧的 `Wants`
另外给转发单元加 `SuccessExitStatus=143`:被 SIGTERM 停掉是正常路径,
不加会在 `systemctl status` 里留一条红色 `failed`,掩盖真正的故障。
> 用 `Wants` 而不是 `Requires` 引用转发层:转发起不来不该阻止平台本身启动,
> loopback 访问仍然可用(插件走的正是 loopback
### 9.9 登记密钥的字段名是 `key_token` ### 9.9 登记密钥的字段名是 `key_token`