Files
dsh_shenxian/dsh-server-docs/04-调整方案/40-共享技能层挂载修复.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

71 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 40-共享技能层挂载修复(bundled-skills 进实例命名空间)(2026-09-11 落地)
## 背景与动机
档案 38a 的 P0:共享技能层「投了也发现不了」。
- `config.bundledSkillDir` = `<dataRoot>/bundled-skills`(`/var/lib/dshs/bundled-skills`),
`orchestrator.ts` 已把它注入 env `DSH_BUNDLED_SKILL_DIR`(`ALLOWED_ENV` 也放行)。
- 但 `@deepseek-ai/dsh-skill-filesystem` 是**在实例内读盘**:
`lib/index.js:84` `config.bundledSkillDir ?? process.env.DSH_BUNDLED_SKILL_DIR` → `resolve()`
→ `lib/index.js:181-183` `roots.push({ path: this.bundledSkillDir, source: "bundled" })`
→ `discoverRoot(root, …)`。
- 而平台 bwrap **只注入 env、从未挂载该目录** → 实例内 `ls` = No such file or directory
→ **技能投进去也永远发现不了**(档案 10/11 的共享技能层实际失效)。
## 用户决策
> 「**bundled-skills 的调整确保用户访问权限不扩大就行**」
→ 修挂载,但必须证明可见面不变。已按此口径实测(见「验证记录」)。
## 实现
`src/supervisor/orchestrator.ts` · `spawnAsUser`,在 `'--bind', root, root` **之后**追加:
```ts
...(this.config.bundledSkillDir !== ''
? (['--ro-bind-try', this.config.bundledSkillDir, this.config.bundledSkillDir] as string[])
: []),
```
- **放在 `--bind root root` 之后**:bwrap 后写覆盖前写(与 profile 三文件同理)。
- 用 `--ro-bind-try` 而非 `--ro-bind`:目录若被误删,实例仍能起来(不崩)。
- 仅当 `config.bundledSkillDir !== ''` 时追加,保留"未配置即不暴露"的语义。
## 验证记录
以**平台原样 bwrap 参数 + 本行**、以 guest 的 uid(100002)在生产机实跑。
| 检查 | 结果 |
|---|---|
| `mountinfo` | `…/bundled-skills → 同名 ro,nosuid,nodev`(**精确叶子路径 + 只读**) |
| 实例内 `ls $DSH_BUNDLED_SKILL_DIR` | `mcn-probe`(探针技能可见) |
| 实例内 `readFileSync($DSH_BUNDLED_SKILL_DIR + '/mcn-probe/SKILL.md')` | **OK,读到 `name: mcn-probe`** ← 即 `skill-filesystem` 的读取条件成立 |
| 实例内 `realpathSync($DSH_BUNDLED_SKILL_DIR)` | `/var/lib/dshs/bundled-skills` |
| 挂载点可写 | `touch` = **Read-only file system** |
| **`ls …/users/`** | **1 项(只列用户自己)** ← 可见面未扩大 |
| 读 `dshs.db` | **NO** |
| `/etc` 可见项 | **13**(未被本改动影响) |
| 真实启动实例 | 正常打印 `dsh web: http://127.0.0.1:41998/?token=…` |
探针技能验证后已删除,宿主 `bundled-skills/` 恢复为空。
## 事故 / 踩坑记录
- **光注入 env 没用**:`DSH_BUNDLED_SKILL_DIR` 一直在 `ALLOWED_ENV` + `baseEnv` 里,
看上去"配置齐全",但 skill-filesystem 是**实例内读盘**,env 只决定"去哪儿找",
目录不挂载就是找不到。**判据:插件在实例内 `resolve()` + 读盘的东西,必须真的出现在
命名空间里**(通用化为本 skill 的铁律)。
- ⛔ **只能挂最内层路径**。写成挂父目录(`--ro-bind <dataRoot> <dataRoot>`)=
一次性把全部用户 home + DB + 凭据放进每个实例。改完必须逐字复核 args 并重跑可见面实测。
## 回滚 / 注意
- **回滚**:`cp src/supervisor/orchestrator.ts.bak-<ts> src/supervisor/orchestrator.ts && npm run build && systemctl restart dshs`
- **副作用**:`bundled-skills/` 内容**对每个登录用户可读**(设计意图)→ 该目录
**禁止放内部文档 / 私有提示词 / 凭据**;投放清单须按"全员可见"审一遍。
- **无 per-user 关闭能力**:dsh 技能层无 disable/deny 机制,rank 600 的共享层内容**用户关不掉**
→ 只适合"全员基线技能";要可启停必须走 rank 400 用户层(`$DSH_HOME/skills`,档案 41 方案)。
- **现状**:机制已通,但**尚未投放任何技能**(投放属档案 41 范畴)。