Files
dsh_shenxian/dsh-server-docs/04-调整方案/40-共享技能层挂载修复.md
T

70 lines
3.8 KiB
Markdown
Raw Normal View History

# 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 范畴)。