dsh-capsule
OS-isolated capability capsules for third-party DeepSeek Harness plugins
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:2-c-q/dsh-capsule说明文档
阅读完整 README ↗dsh-capsule
一切皆插件,但环境权限不应自动属于插件。
dsh-capsule v0.1 在全新的 Linux Bubblewrap 进程中运行兼容的 DeepSeek Harness 第三方 guest。可信 host 验证一个 integrity 固定的 JavaScript artifact,在隔离的 describe cell 中发现其贡献,并代替 guest 注册真实的 DSH 工具与静态 system prompt section。每次工具调用都会启动另一个全新 cell;guest 代码不会加载进 DSH 进程,也不会在调用之间驻留。
v0.1 安全边界
v0.1 isolation provider 仅限 Linux,并且只接受通过 fail-closed execution-world attestation 的 DSH managed subprocess runtime。在任何 guest 代码运行前,一个空环境、时间与输出均有界的 probe 必须原样返回 private random challenge,报告与 Host 一致的 Node、Bubblewrap、已报告非 addon shared object、mount namespace 与 root identity,并等待托管进程树退出。内置 local subprocess provider 是通常能够通过该证明的实现,但系统不要求 class identity。被选中的 subprocess provider 仍属于 trusted computing base。
一个小型可信 launcher 会逐段拒绝 symlink,并以稳定描述符打开每个待挂载的 runtime、artifact 与 workspace source;Bubblewrap 只挂载这些描述符。它从空 root 构造新的 user、PID、mount 和 network namespace;加入选定的 Node 可执行文件、非 addon 共享库、精确的只读 guest artifact、有大小上限的私有 /tmp、/proc、最小化 /dev,以及仅被显式批准的只读 workspace 文件;清除继承环境,只设置 HOME=/tmp 和 PATH=/runtime;并丢弃 capabilities。严格 launch document 把每个 mount 标记为 runtime、guest 或 workspace-read;Node workspace permission 只根据 workspace-read 生成,而不根据 destination pathname 推断。Node Permission Model、禁用 addon 和 inspector signal、V8 heap 上限、协议限制、deadline 与 host scheduler 只提供纵深防御。
Node Permission Model 不是隔离边界。配置了 capsule 的 host 如果不在 Linux 上、找不到 Bubblewrap、artifact 或 policy 不匹配,或者无法建立任一必需控制,就会激活失败。项目不提供 cooperative provider,也不会静默降级。
协议 v0.1 只支持指向现有普通文件的精确 workspace-read 能力。每项 request 都必须在部署 grants 中具有相同相对路径;请求的 workspace 路径需要一个绝对 workspaceRoot,并且必须位于该 root 内且不经过 symlink。Bubblewrap 把文件只读挂载到对应 /workspace/ ,Node allowlist 同步该授权。请求的目录目标或任意 workspace-write、network-connect、subprocess-exec、storage request 或 grant 都会导致激活失败。空 requests 与空 grants 仍然有效,echo 示例就是如此。安全声明仅限于文档所述 Linux 边界内的运行时权限隔离,不包括拒绝服务、kernel 或 Bubblewrap 漏洞、侧信道与不安全 prompt 内容。详见威胁模型。
v0.1 不提供目录 mount,因为目录内容可能包含 Host IPC endpoint 或其他特殊文件;也不提供 raw write mount,因为 inode alias 可能让写入影响超出授权 pathname。后续目录或写入能力必须通过 Host-owned broker 或 snapshot-and-commit mechanism 实现,而不能直接暴露这两类 mount。
安装到 DeepSeek Harness
先在 Debian 或 Ubuntu Linux host 上安装 Bubblewrap:
sudo apt-get update
sudo apt-get install --yes bubblewrap
Ubuntu 24.04 默认启用 AppArmor 对非特权 user namespace 的限制。如果 Capsule 激活报告 loopback: Failed RTM_NEWADDR: Operation not permitted,应加载 Ubuntu 为 Bubblewrap 提供的定向 profile,而不是在全系统关闭该限制,然后把最后一条命令作为预检:
sudo apt-get install --yes apparmor-profiles
sudo install -m 0644 \
/usr/share/apparmor/extra-profiles/bwrap-userns-restrict \
/etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser --replace /etc/apparmor.d/bwrap-userns-restrict
bwrap --die-with-parent --new-session --unshare-all --unshare-user \
--disable-userns --cap-drop ALL --ro-bind / / /bin/true
该 profile 会作用于这台 host 上的每次 /usr/bin/bwrap 调用;在长期运行的机器上加载前,管理员应评估同机其他 Bubblewrap 或 Flatpak 用途。其他发行版需要等效、由管理员批准的策略,允许非特权 Bubblewrap 创建 user、mount、PID 与 network namespace。Capsule 本身不会修改 host 的 AppArmor 或 sysctl policy。
安装的 Bubblewrap 必须支持 --ro-bind-fd。当前 managed subprocess provider 必须通过 execution-world attestation;filesystem view 不同、解析到不同对象、Bubblewrap 缺少所需能力、证明输出 malformed 或 truncated、超时、进程树未完整退出,都会导致激活失败。Host 不会改用 pathname mount 或更弱的执行模式。
Package 接受 @deepseek-ai/dsh-subprocess、@deepseek-ai/dsh-system-prompt 与 @deepseek-ai/dsh-tools 从 0.1.0-rc.5 起且低于 0.2.0 的 peer 版本;它不要求把 @deepseek-ai/dsh-subprocess-local 作为 package peer。
把 bundle 安装到应当承载 host 的 profile:
dsh plugin --profile
add github:2-c-q/dsh-capsule
Bundle 会挂载一个不含 capsule 的惰性 dsh-capsule-host 行。在 profile 级 cordis.patch.yml 中加入如下覆盖,并把示例 root 替换为本仓库 examples/echo-capsule 目录的绝对路径:
- id: dsh-capsule-host
config:
capsules:
- root: /absolute/path/to/dsh-capsule/examples/echo-capsule
manifest: capsule.json
policy:
capsuleId: example.echo
tools:
- capsule_echo
promptSections:
- capsule.echo.guidance
grants: []
bubblewrapCommand: bwrap
maxManifestBytes: 262144
maxArtifactBytes: 16777216
maxFrameBytes: 1048576
maxStderrBytes: 65536
maxDescriptionBytes: 4096
maxPromptBytes: 65536
maxContributions: 64
maxJsonNodes: 10000
maxJsonDepth: 32
startupTimeoutMs: 5000
callTimeoutMs: 30000
shutdownTimeoutMs: 500
processGraceMs: 500
maxConcurrentCalls: 4
maxQueuedCalls: 32
maxOldSpaceSizeMb: 128
tmpfsBytes: 16777216
DSH patch 会替换目标行的整个 config,不会深度合并嵌套字段。因此,覆盖必须重述需要保留的每个字段。示例显式重复 v0.1 的全部默认值,避免在添加 capsules 时丢弃 bundle 提供的限制。启动 profile 前可用 dsh --profile --dump-config 检查组合后的行。
如需 workspace 访问,在 root 与 manifest 旁添加 workspaceRoot,并在 guest manifest 和部署 policy 中声明同一个精确的只读文件。例如,manifest 中的 request:
{"kind":"workspace-read","path":"reference/context.txt"}
需要如下 capsule mount 配置:
workspaceRoot: /absolute/path/to/workspace
policy:
capsuleId: example.reader
tools:
- capsule_read
promptSections: []
grants:
- kind: workspace-read
path: reference/context.txt
/absolute/path/to/workspace/reference/context.txt 必须已经是普通文件,路径不得经过 symlink,并且必须与 Host 私有 staging 保持分离。Guest 会在 /workspace/reference/context.txt 以只读方式看到它。父路径授权、目录、不同 permission kind 或不支持的资源 kind 都不能满足 request。Policy 中额外的 workspace-read 条目只做语法校验,本身不授予权限,在被 request 匹配前不要求文件系统目标,也不会自动挂载。
激活时,host 在不执行 guest 的前提下读取 capsule.json,解析配置 root 内的 entry,验证其 SHA-256 digest,把完全相同的字节复制到私有只读 staging 文件,并要求 manifest 中的每个 contribution 与资源 request 都落在部署 policy 上限内。Policy 中额外条目本身不授予权限,也不会自动挂载。Host 会固定可信 launcher 字节,以描述符为全新的 describe cell 打开各个 source,再通过 ctx.tools 和 ctx.systemPrompt 原子发布全部获准工具与 prompt section。工具调用继续走 DSH 原生的验证、执行、展示与 session log 路径。
构建 guest
Guest 从 dsh-capsule/guest 导入小型 SDK,并调用 runCapsuleGuest(...)。完整的工具与静态 prompt section 示例见 examples/echo-capsule/source.ts。
运行时只挂载一个 guest 文件,不提供 node_modules,因此必须把 SDK 和全部 JavaScript 依赖打进一个独立 ESM artifact:
pnpm exec esbuild examples/echo-capsule/source.ts \
--bundle \
--platform=node \
--format=esm \
--target=node22 \
--outfile=examples/echo-capsule/guest.mjs
sha256sum examples/echo-capsule/guest.mjs
把该小写 digest 写入 capsule.json。这个 echo guest 不请求 workspace 访问,因此它的资源数组与 policy grants 都为空:
{
"protocol": "dsh-capsule/0",
"id": "example.echo",
"entry": "./guest.mjs",
"integrity": {
"sha256": "870a9cc8532a2b955ea5570ee9e8b79f6552029c403de95fd814f8489d5a5e58"
},
"contributes": {
"tools": ["capsule_echo"],
"promptSections": ["capsule.echo.guidance"]
},
"requests": []
}
Guest 源码、SDK 代码或 bundle 依赖变化后,都要重新构建并更新 digest。Integrity 不匹配会导致激活失败;host 不会退回执行其他字节。本仓库示例用 pnpm run build:example 自动完成构建,并用 pnpm run verify:example 检查 digest。
协议与限制
消息采用严格的长度前缀 JSON,并限制 frame 字节数;descriptor schema 另有深度与节点数限制。时间、并发、队列、stderr、prompt 与 description 上限均由部署配置控制。describe 只激活一个全新进程,以返回不可变工具 schema 与静态 prompt 文本。每次调用激活不同的进程,重新验证同一组 descriptor,精确执行一个工具,完成显式 shutdown handshake,并等待托管进程树退出。因此 guest 进程状态是临时的。只读 workspace 文件仍归 Host 所有,并可能因可信外部活动而变化,但 guest 不能通过它持久化写入。v0.1 不支持私有持久 storage,也不支持 network 或 subprocess 能力。
Capsule guest 使用独立 API,不能透明替代任意 Cordis 插件。精确协议与生命周期见 RFC,项目范围见带日期的生态审查。
开发
pnpm install
pnpm run check
pnpm run build:example
pnpm run verify:example
pnpm run pack:check
许可证
MIT