前言¶
DSH 的理念是「一切皆插件」,profile 用来划分不同的插件环境。其中 safemode 的定位是“干净环境”:排障、做对照,或者只想要核心能力时,用它启动最合适。
但 safemode profile 本质上就是 $DSH_HOME/profiles/safemode/ 下的一组普通文件。一次 dsh plugin --profile safemode add ...、一次手动编辑 package.json,甚至某个脚本的一次误写,都会让这个环境不再干净,而且你不一定会立刻发现。事后逐个文件核对、手动清理,费时也不可靠。
dsh-safemode-profile 把这件事自动化了:启动时无条件强制还原,运行期持续守护,让 dsh --profile safemode 在任何时刻都只含白名单核心 bundle。
这是什么¶
dsh-safemode-profile 是一个 DSH 插件,作者 jinsiyu,MIT 许可。它只有一个职责:把 safemode profile 锁定在白名单模板上——启动时强制还原,运行期发现漂移自动还原。
前置要求:已安装 DSH(@deepseek-ai/dsh)与 Node.js(≥ 18)。
核心功能¶
启动时强制还原¶
DSH 每次启动加载本插件时,会把 $DSH_HOME/profiles/safemode/ 无条件写回白名单模板,涉及三个文件:
| 文件 | 强制内容 |
|---|---|
package.json |
dsh.profile.bundles 为白名单(默认 @deepseek-ai/dsh-base + @deepseek-ai/dsh-web-app),dependencies 清空 |
cordis.patch.yml |
空的用户 patch 层 |
pnpm-workspace.yaml |
pnpm workspace 设置 |
与模板一致时不动(避免自触发),不一致一律重写。
运行期双通道守护¶
插件存活期间,用两条通道监控 profile 是否被改动:
1、fs.watch 实时监听:三个文件任何变更,防抖 300ms 后立即还原;
2、30 秒轮询兜底:detectDrift 全量比对,覆盖 watch 不可靠(如挂载场景)和文件被外部整体替换的情况。
发现漂移——bundle 被加/删、patch 层被写入、dependencies 被加了东西,就自动还原,并记录 warn 日志。
无构建脚本,一次装好¶
包内没有 postinstall,也没有任何构建脚本,pnpm 不会拦截它。dsh plugin add 一次成功(退出码 0),不需要配置 approve-builds / allowBuilds。还原与守护的全部工作由插件行在 DSH 启动加载时完成。
白名单可定制¶
白名单是唯一的定制入口,通过环境变量 DSH_SAFEMODE_BUNDLES 设置。守护与还原读取的是同一个白名单,改一处即全局生效。
安装与启用¶
先确认前置条件:已安装 DSH(@deepseek-ai/dsh)和 Node.js(≥ 18)。下面三种方式任选其一,命令以官方文档给出的形式为准:
方式 A(推荐,npm 安装):
dsh plugin --profile web add dsh-safemode-profile
方式 B(GitHub 直装,显式指定 main 分支):
dsh plugin --profile web add github:jinsiyu/dsh-safemode-profile#main
方式 C(本地打包 tgz):
npm pack # 生成 dsh-safemode-profile-<版本>.tgz
dsh plugin --profile web add .\dsh-safemode-profile-<版本>.tgz
装完重启 DSH,插件行生效后,safemode 即进入“强制还原 + 常驻守护”状态。
典型用法¶
零第三方插件启动¶
dsh --profile safemode
safemode 也带 webServer(默认 3080),如果和 web profile 同时运行,建议用 --port 错开:
dsh --profile safemode --port 3081
自定义白名单¶
比如只想留纯 CLI(无 GUI):
$env:DSH_SAFEMODE_BUNDLES = "@deepseek-ai/dsh-base"
dsh 启动时继承该环境变量即生效。注意不要手动改 safemode 的 profile 文件,任何改动都会被守护逻辑还原,定制一律走这个环境变量。
手动强制还原一次¶
不想重启 DSH,或者排查问题时,可以立即触发一次还原:
node scripts/ensure-safemode.mjs
文件系统加固(可选)¶
在插件守护之外,还可以把三个受管文件设为只读,从文件系统层面阻止 dsh plugin --profile safemode add <包>:pnpm 写这些文件会失败,安装直接报错,插件进不去;DSH 启动不受影响。共三级:
1、基础只读(推荐日常使用):Windows 用 attrib +R,POSIX 用 chmod 444:
attrib +R "$env:USERPROFILE\.dsh\profiles\safemode\package.json" `
"$env:USERPROFILE\.dsh\profiles\safemode\cordis.patch.yml" `
"$env:USERPROFILE\.dsh\profiles\safemode\pnpm-workspace.yaml"
chmod 444 ~/.dsh/profiles/safemode/package.json \
~/.dsh/profiles/safemode/cordis.patch.yml \
~/.dsh/profiles/safemode/pnpm-workspace.yaml
2、Windows ACL(icacls deny):可精确到“写”与“删”,但仍挡不住管理员,Administrators / SYSTEM 默认完全控制,可通过取得所有权绕开;
3、Linux immutable(chattr +i):最强的一档,root 也动不了,但需要 sudo,且文件系统要支持(ext4/xfs 支持)。
加固的几个注意点:
- 只锁这三个受管文件,不要锁整个目录。Windows 下尤其不要用
attrib +R <目录> /S递归加锁——DSH 每次启动都会重写cordis.yml,目录被锁会导致 safemode 启动失败(实测 exit 1)。 - 先让插件创建/还原 profile,内容与白名单模板一致后再锁。锁定后若出现漂移,守护还原会因只读而失败并记 warn——这正是锁定的预期行为:锁住即不该有漂移。
chattr +i会连守护的自愈重建一起挡住,升级插件、修改白名单前都要先chattr -i。日常场景用基础只读就够了。
适用场景与注意¶
适合的场景:需要一个可预期的干净 DSH 环境做排障或对照;机器上有自动化脚本或其他人会碰 DSH 配置,希望 safemode 不被意外(或故意)改动。
使用前需要清楚的几件事:
1、端口冲突:safemode 也带 webServer,默认 3080。与 web profile 同时跑会因 EADDRINUSE 启动失败,用 --port 错开(如 --port 3081)。
2、会话与凭据不隔离:sessions、settings.yaml、.env 是 home 级共享,safemode 隔离的只有插件。
3、home patch 串层:~/.dsh/cordis.patch.yml 对每个 profile 都生效,别往里面挂插件,守护逻辑只盯 safemode 自己的目录。
4、cordis.yml 不要手改:DSH 每次启动自动重写它。safemode 的 patch 层会被本插件还原为空,定制走 DSH_SAFEMODE_BUNDLES。
最后照例提醒:插件以当前 dsh 进程的权限运行,安装前建议先读一遍源码(仓库见文末链接)与许可证(MIT),确认符合你的安全要求再装。
小结¶
dsh-safemode-profile 只做一件事:让 dsh --profile safemode 永远干净。启动强制还原、运行期双通道守护,配合可选的文件系统只读加固,safemode 不会再悄悄“变脏”。
- 插件目录页:https://www.skillhub.cn/plugins/jinsiyu/dsh-safemode-profile
- GitHub 仓库:https://github.com/jinsiyu/dsh-safemode-profile
其中目录页是社区维护的独立站点,与 DeepSeek / 幻方无官方从属关系,仅作检索入口。