# DSH 平台 — 记忆(**状态层**) > **判据**:不看它会不会导致「违规」或「事故」?会 ⇒ 写实体;只"更慢更绕" ⇒ 只留指针。 > **落点**:动作前必须生效的规则 → 根 `CODEBUDDY.md`(**不重复**)|会变的状态 → 本文件|方法论 → 技能|一次性事实·命令原文 → `PLAYBOOK` / `04/`|当天流水 → `memory/YYYY-MM-DD.md`|跨项目偏好 → `~/.workbuddy/MEMORY.md`。 > 📄 **详版 = `.workbuddy/memory/PLAYBOOK-实例与插件坑.md`**(**报障第一件事就开它**,§1–12)。 > ⚠️ **注入上限(超了会被截断)** ⇒ 新增**按序插入对应小节,⛔ 别追加到末尾**;**体积只减不增**。 ## 一、状态速查(会变的都在这) | 项 | 状态 | |---|---| | **在途单** | **交接单 v2 待执行** = 覆盖网络线(工作区根 `交接单_覆盖网络落地执行_20260916.md`,**须另开会话**);T01–T08 已归档 | | **生产形态** | 47 = **Manager**(cluster;控制面库 = 47 的 PG13 `127.0.0.1:15432`,单元 `dshs-pg`)+本地 Worker `w-47`(19100);**106 = Worker `w-106`**(19000)。存量锚 `w-47`、新用户按容量落 `w-106`(**粘性优先**)。⚠️ **权威库 = 47 的 PG**(`/var/lib/dshs/dshs.db` 仅回滚用)。⛔ 47 兼作 Worker 是**有意设计**。**回滚** = 删 drop-in `dshs.service.d/cluster.conf` → `daemon-reload` → `restart dshs`(cluster 配置在**这个 drop-in 里**,⛔ 不在 `/etc/dshs.env`) | | **106 节点** | 实例能力已补齐(`users/` 权限**必须 711**,700 ⇒ 实例必崩)。⏳ **provisioner 未铺**;⚠️ 旧控制面 `dsh-users-platform.service`(3080) 待清 | | **覆盖网络线** | 入口 = `接续入口_覆盖网络线_20260916.md`。**P1–P3 已完成验收**(证据 = 交接单 §8):P1 47 对齐 `640813e`|P2 `dsh_hosts.via`(v8;w-47=`local`/w-106=`manager-ssh`)|P3 端口区间隔离(w-47 20000+/w-106 21000+)⇒ 修掉「跨 worker 同号 ⇒ 静默拨到别人实例」真 bug。🔴 **P4(SSH 版中继 / 32023)已判「不做」**——SSH 反向隧道**不是"不安全"**(专用非特权账号 + `PermitOpen` + 限速是业界标准做法),但**不该长期做数据面**;它想要的(与宝塔 sshd 解耦、甩掉 root 凭据)**frp 里同样拿到** ⇒ 两步合一。🔴 **传输方向已定**(用户问"开放端口能否解决 / SSH 是否临时方案"):**worker 永远只拨出、不开任何入站端口**(106 入站口 = **0 个**)⇒ **摆脱 SSH 的正解 = 自研 relay + 单端口出向长连接**(**不需新开公网口**,与 S5「443/TCP 兜底」合并做);⛔ 别再把"在 106 开 agent 端口"当方案(暴露面 O(1)→O(N),且 agent 只有共享 token 认证)。**终态 relay 实现「未定」**:**frp 已撤销**(用户质疑「项目久 ≠ 最好」是对的)—— 它有 **5 项默认姿态缺陷**(认证绕过 CVE-2026-40910 / 默认 `admin:admin` / 默认绑公网 / 静态 token 明文 / 不强制 TLS),按 6 条判据(**认证模型·默认姿态·零入站·爆炸半径·可观测·生态**)**前三条最差一档**;候选 = **OpenZiti / Nebula(证书身份)> rathole(Noise_NK + 每服务 token)> frp**。⇒ **本轮不引入第三方 relay**,先做 **R0 = 六条判据打分表 + 测链路画像(RTT/jitter/带宽)**;**性能不作为选型轴**(第一瓶颈是 **presence** 不是带宽)。⚠️ 换第三方 = 用**不掌控的攻击面**替换**已收窄的 sshd 面** ⇒ **不是无条件升级**。🔑 `Reachability.via` + `Rendezvous` 已抽好 ⇒ **换实现 = env 级切换**。详见 `覆盖网络_传输方案取舍_开放端口与自研relay_20260916.md`。🔴 **勘误**:**`proxy.ts:109` 是上游 Host 头**(改它 = `/api` 全 403;连接目标 `:136-137`)|**「回环别名」实测证伪**;**S4 异地中继仍缺一跳**(6 条全文在交接单 §0.2 / 方案 §9)。⚠️ `dsh_instances.status` **非实时** ⇒ 看 worker agent `/status/:userId`。⚠️ 47=`/opt/dshs/lib/`、106=`/opt/dshs-cluster/lib/`;**47 部署 = 本机 build 后 scp `lib/`**(⛔ 非 git pull);47 的 PG **只能 TCP**;`npm test` 必须 Node 22。🟢 本机作「客户端类型」测试节点(Win soft 模式 = **目标模式**) | | **代码分层** | ✅ P0 已立:代码仓 `docs/architecture.md`(`①入口→②领域→③能力→④基础` + R1–R4)。🔴 旧结论「3 处反向依赖」**已勘误为 0 处** ⇒ 剩 P1 `src/domain/*` / P2 模块头注释 | | 集群化 | 单一来源 = 项目根 `集群化改造方案_Manager-Worker_20260914.md`;D1–D5 已定,⏳ 仅 D6(不阻塞) | | 实例内存(档案 96) | 与插件开关**解耦**:`MemoryHigh=MIN=448`+`MemoryMax=MAX=1024`;`/api/dsh/status` 带 `quota{...}`。⚠️ `MEM_TABLE` **仅预估、不驱动配额** | | 归档编号 | 至 **112**(103–112 = 覆盖网络线);**63 空号**,勿补占 | | 熔断 / MCN / univer | 熔断 78:冷却 6h + 双通道告警已上线,**L1 实测未做**|MCN 0.3.10 已铺发,`mcn-task` 重复建组未解|univer = **内存探针,有意保留 ⇒ 勿下架** | | **提交基线** | HEAD **`640813e`**(已 push);✅ 工作区 0 改动。🔑 **本机 `refs/remotes/**` 写不进** ⇒ `git fetch` **静默失败** ⇒ 分叉判定**必须** `git ls-remote origin refs/heads/master` 取**裸 sha** + `git merge-base --is-ancestor`;推前 `--dry-run`、推后重验。🗣 **「本机」= 跑 WorkBuddy 的开发机** | ## 二、本机铁律(⛔ 违反即事故) - 🔴 **R10 实例起不来先查属主/EACCES,别先怀疑 OOM** —— root 跑 `dsh --profile` ⇒ 属主变 root ⇒ 崩溃循环/404。修 `find -user root -exec chown : {} +` → 重启。⚠️ 用户 home 是 watch 域 ⇒ 读不了的文件同样致命 ⇒ 平台侧备份一律 `/opt/dsh/backups/`。 - 🔴 **改 bwrap 参数前先看目标机 bubblewrap 版本**(**47=0.4.0 / 106=0.11.0**):`--perms` 属 0.5+ ⇒ 47 上 `Unknown option` ⇒ **沙箱起不来 = 所有实例全挂**;要权限**用 `--tmpfs`**。⚠️ `smoke-isolation` **不能**作 account 模式验收。 - 🔴 **集群化数据分层判据**:① 归属/租约 ⇒ **只有 Manager 能写**(双写 = 脑裂)② 跟着用户走 ⇒ 放**实例 home** ③ 本机运维用 ⇒ **Worker 本地库**。 - 🔴 **hook 脚本必须走 `sys.stdout.buffer.write(bytes)`** —— 文本层 `sys.stdout.write` + 文案含 `⛔` ⇒ 宿主 stdout 非 UTF-8 ⇒ `UnicodeEncodeError` ⇒ **stdout 空 ⇒ 静默放行**。判据 = **"写日志 + emit"双动作且 emit 走 buffer**。⛔ 别再写"hook 拦不住"。📂 自证日志 = `D:\github\dsh_shenxian\.workbuddy\lock-hook.log`。⇒ PB §10。 - 🔴 **两条命令坑**:① `pkill -f` 要匹配"实际 argv" ⇒ ✅ `ss -lntpH 'sport = :PORT' | grep -oP 'pid=\K[0-9]+'`。② **`fetch` 静默丢 `Host` 头**(undici)⇒ 多租户验证落"无租户"路由、**假 404/200** ⇒ ✅ `curl -H "Host: …"`。 - 🔴 **两条取数坑**:① **本机 `md5sum` 输出 `hash *path`(二进制)、远端是 `hash path`(文本)** ⇒ 直接 `diff` 得"**所有文件都不同**"的**假象**(59/59 全红其实只差 7 个)⇒ 先归一化路径字段。② **`ssh '…'` 单引号里不能出现单引号**(SQL 字面量)⇒ `'local'` 被本地 shell 吃掉 ⇒ ✅ 用 `cat > f <<"EOF"` 落文件,或本地 `echo` 管道给远端 `psql -f -`。 - 🔴 **「单机自用」≠「不需要互联」**(用户原话:「**单机用也要互联,这正是建立覆盖网络的目的**」):**租户维度收窄与网络维度收窄正交** ⇒ ⛔ 别再用"单机自用所以没流量要互联"当前提。 - 🔴 **覆盖网络按异构设计**:① 中继按 **45% 设计、55% 留余量**(⛔ 别用同构的 15%)② **必须补 443/TCP 兜底** ③ 有公网 IP 的节点自动升格为中继候选,但**权威状态单点 Manager** ④ VPN/代理类**检测即降级中继**、不重试 ⑤ 节点自报网络画像。 - 🔴 **方案只做技术实现,合规不进方案**(用户原话:「**方案只考虑技术实现,跨境数据合规是谁用谁自己考虑**」)⇒ ⛔ 不再把合规/数据主权/备案当前置条件或上抛项。 - 🔴 **接手前人结论,先做一次最小取证**(同日踩两次):① `proxy.ts:109` 实为**上游 Host 头**;② "3 处反向依赖" 实为 **0 处**。⛔ 别拿上一份文档的结论当既成事实。 - 🔔 **技能加载闸门**:`scripts/skill-load-guard.py` —— 命中「决策方法/按你的规划/别问我/自行决策」⇒ 注入"必须先加载 `dsh-decision-method`"(急停 `DSH_SKILL_GUARD_OFF=1`)。⚠️ **需完全重启**。 - ⚠️ **本机执行环境**:**Python stdin/stdout 必走 `buffer` 显式 UTF-8**|改文件**必用 `newline=""`(读也要)**|**ESM**|bash **PATH 被 shim 重置** ⇒ 先 `export PATH=/usr/bin:…:/c/Windows/System32:/c/Windows:$PATH`|**Python exe 不认 `/e/…`** ⇒ 传参 `E:/…`|bash 内联 `\` 被 MSYS 改写 ⇒ 含 Windows 路径的 Python **落 .py 再跑**|`tar czf E:/…` 加 `--force-local`|`ls` 被 MSYS 搞坏 ⇒ 先 `cd` 再相对路径。⇒ PB §10。 - ⚠️ **行尾只信字节级**(`grep -c $'\r$'` 会误报)⇒ Python `b.count(b'\r')`。BRIEF/README/`skills/**`/`memory/**` = 纯 LF;**`INDEX.md` = 混合** ⇒ 字节级单行插入;⛔ 别对它跑 `git checkout --`。 - ⚠️ **引用用户口径只写原话**|**含反引号的内容一律用 Write/Edit 写**|**进程替换 `diff <(a) <(b)` 不可用**|**`Edit` 是替换不是追加**。 - ⚠️ **打包与改名**:`npm pack` 前核断言锚点、先删 `lib/*.bak` 与 `*.tgz`、每包必须有 `.npmignore`;改名三件套 = SQLite `.db`+`-wal`+`-shm` 一起改 / 压缩包内 grep-sed 够不到 ⇒ 解压→改→重压 / 改完真环境跑一次。 - 浏览器只准 `agent-browser` / `browser-harness`(CDP 9223);⛔ **Playwright 全面禁止**。 ## 三、成本与自动化(09-16 实测) - 🔴 **成本公式**:**积分 ≈ 单价 × 一轮内工具调用次数**。单价随水位涨(<10 万 ≈**0.10**、15 万 ≈**0.41** 分/次,受控实测 **4 倍**)⇒ **成本全由"次数"累出来**。**真杠杆 = ① 一轮工具次数(主导)→ ② 水位 → ③ 固定注入 35,192 tok(缓存命中 99.5%,很轻)**。⛔ **「切会话能归零」错** —— 自动化**全新会话首轮**烧 **8.93 / 10.05 / 13.82** 分。 - 🔴 **自动化会话为什么贵**:**不是"自动化"这个身份**(手动会话也可更贵)。根因 = ① **一条 prompt 塞 N 件事** ② **无人能打断**(实证:`b08b1c35` 用户只说"先确认待办",**第 28 次调用已在写代码**)③ 规则本身制造调用。**修法**:prompt 必写「本轮只做一件事,做完即停,⛔ 不许顺手做别的」+「取证最多 3 个命令,要动代码先停下报告」。⇒ `会话接续规范_20260916.md §3.2.1/§6`。 - ⚠️ **自动化 = 开新会话的唯一通道**;**钩子无创建/切换会话能力** ⇒ 自动接续**只能半自动**:钩子注入收口指令 → **模型调 `automation_update`** 登记 +2min 一次性自动化。⛔ 钩子不得绕过 `automation_update` 写库。🔴 **09-16 补**:① 开机**第 0 步 = 跑 `state.py`**;② prompt **⛔ 不许抄任务细节**;③ 三级注入 = "**登记一次性 automation**";④ **多线并行**:口令**必带线名**、`[入口]` 已**列全各线**、登记前先看 `automation_runtime_state.running`、抢不到锁 = 正常信号只报告。⑤ 🔴 **登记 automation 后必须在给用户的回复里用陈述句告知**("已登记自动接续,约 N 分钟后自动开新会话,不用你操作;接续点 = X")—— 新建会话是**用户可感知的状态变更**,**只登记不告知 = 缺陷**(实证:会话 `408636f2` 登记了自动化但末条回复一字未提,用户两小时后自己发现才追问)。 ## 四、仓库 / 推送 / 授权 - 📦 **09-15 文档库已并入代码仓**(R4 选 a)⇒ **`D:\github\dsh_shenxian\dsh-server-docs\`**(git = `D:/github/dsh_shenxian/.git`)。锁脚本 = `…\dsh-server-docs\scripts\handoff-guard.sh`。镜像 `/opt/dsh/docs` 不变。⚠️ hooks 路径改动要**完全重启**才生效。 - 代码仓 `D:\github\dsh_shenxian` → `…dsh_shenxian.git`(master)|**推送只能从本机**;服务器改码走 `git bundle`。文档库 `autocrlf=false` + `.gitattributes(* -text)` **必须保持**。 - ⚠️ **技能是「三处」**:本机 `.workbuddy/skills//` ← 文档库 `skills//` ← 镜像 `/opt/dsh/docs/skills/`。**验收 = 两副本 md5 一致 + 镜像一致 + `README.md`/`INDEX.md` 登记跟着改**;同步前先抢锁。 - ⚠️ **改任何文件前先抢全局执行锁**(`--claim-exec`);**释放了再想继续写就得重抢**。 - 📄 **「去查」索引 = PLAYBOOK §11**(权限档位 / 插件启停 / UI·univer / 个人技能 / docx / `business-plugins` 升级 / admin 跨用户 / 加新能力 / dsh 版本 / 开源导出)。 - 🔒 **授权类**:文件已全部移除(不得再列为待办或上抛)。上游 MIT、**不得对上游代码附加限制**;**对外发布前先放回那三份**(`_授权归档_发布时再放回\`)。