插件目录 / Developer / dsh-container
dsh-container
已验证 · 实测可装 NIyueeE
功能简介
DSH容器镜像:通用开发基础,自动更新
可用 — 实测通过,早期项目
DSH容器镜像:通用开发基础,自动更新 实测能干净安装、正常启动。早期项目,但功能可用。
「已验证」表示我们的自动化 CI 在干净 profile 里实际执行了 dsh plugin add 并启动成功——仅此而已。功能描述与版本兼容性均为作者声明。这不是安全审计,也不代表对第三方代码的背书。
README
dsh Container Image
Containerized DeepSeek Harness (dsh), built on
a small debian:13-slim base with only the toolchains this project needs: Node.js LTS, pnpm, uv,
Rust/cargo, Caddy, podman, and GitHub CLI. It is ready to use out of the box with optional dsh
auto-update baked in. The image is published to GitHub Container Registry; both the compose.yaml
and the Quadlet .container examples pull the image directly — no local build needed.
dsh itself comes from the official repository
deepseek-ai/deepseek-harness and is installed the
way the official README describes: install Node.js, then npm-install @deepseek-ai/dsh.
Features
| Component | Description |
|---|---|
| Base image | debian:13-slim (small, overridable via the BASE_IMAGE build arg) |
| Built-in toolchain | Node.js 22 LTS, pnpm, uv, Rust/cargo, git, build-essential, Caddy, podman, gh |
| Added user-level tools | Rust/cargo (~/.rustup + ~/.cargo), uv (~/.local/bin), pnpm (~/.local/share/pnpm) — persisted with /home/dsh |
| Container dev tool | podman (apt), with rootless subuid/subgid mapping configured; nested rootless operation depends on the host runtime |
| dsh | Global npm install of @deepseek-ai/dsh, same source as the official README's npx @deepseek-ai/dsh web; pinnable via DSH_VERSION |
| Auto-update | Off by default (DSH_AUTO_UPDATE=0); opt in with DSH_AUTO_UPDATE=1 to update dsh to the latest npm release on container start (only upgrades, never downgrades a pinned version). The image itself supports Pull=newer / AutoUpdate=registry |
| dsh web supervisor | dsh web runs under a small supervisor (dsh-web) that restarts it automatically if it exits; run dsh-restart inside the container to restart dsh web without restarting the container |
| Exposure | Caddy reverse proxy (0.0.0.0:3081 → dsh's 127.0.0.1:3080) rewriting Host/Origin to loopback, gzip-compressing UI assets (≈1.3 MB → ≈360 KB), with optional basic auth (DSH_PROXY_USER / DSH_PROXY_PASSWORD) |
| Observability | OCI labels (org.opencontainers.image.*, incl. git revision), HEALTHCHECK (curl 3080 + 3081) |
| Runtime user | uid 1000 (dsh); /home/dsh is the persisted user layer and dsh has passwordless sudo |
The base image is pinned by default; the dsh top-level version and Rust toolchain can be pinned
with --build-arg. uv/pnpm are installed as user-level tools, and Caddy/podman/gh come from apt —
see build.md.
Environment variables
| Variable | Default | Description |
|---|---|---|
DSH_PROXY_USER / DSH_PROXY_PASSWORD |
(empty) | Enable basic auth on the exposed proxy (recommended): without it, anyone who can reach port 3081 can drive the agent and read/write all settings & credentials (see security.md "Security boundary"). Set both or neither — the entrypoint refuses to start if only one is set |
DSH_AUTO_UPDATE |
0 |
Set to 1 to update dsh to the latest npm release on container start; keeps the in-image version when offline or on failure |
Everything else uses built-in defaults:
- dsh data:
~/.dsh(/home/dsh/.dsh) — upstream default, noDSH_HOMEoverride - working directory:
$HOME(/home/dsh); dsh creates folders under it as needed - Rust/cargo:
~/.rustup+~/.cargo - uv:
~/.local/bin(managed Python/tool data in~/.local/share/uv) - pnpm:
~/.local/share/pnpm
The whole /home/dsh directory is the persistence boundary: mount it as one volume so user-level
state survives while /usr/local and the rest of the system layer are reset on image upgrades.
User-level tools (Rust/uv/pnpm/dsh) are baked into the image and copied into a fresh named volume on
first start; system packages installed later with sudo apt live in the container/system layer and
are not part of the persistent home volume.
The exposed port is 3081: a Caddy reverse proxy inside the container listens on0.0.0.0:3081 and forwards to dsh web on 127.0.0.1:3080, rewriting Host/Origin to loopback.
UI assets are gzip-compressed by the proxy (≈1.3 MB → ≈360 KB), which matters most for remote
access; SSE/WebSocket streams pass through unbuffered (verified against Caddy 2.6), so agent
output is not delayed by the proxy. For WAN access, terminate TLS with an external reverse proxy in
front of 3081; it must forward the WebSocket upgrade headers (proxy_set_header Upgrade $http_upgrade / proxy_set_header Connection "upgrade" with nginx) and must not buffer or time
out quiet streams — see deployment.md for a working example. dsh's /api
browser-trust fence checks HTTP headers only, so remote browsers pass every endpoint —
including settings/credentials methods that are otherwise loopback-only. The proxy is therefore
the security boundary: anyone who can reach 3081 gets full control, so enableDSH_PROXY_USER/DSH_PROXY_PASSWORD and keep the port firewalled. Extra dsh web arguments can
be passed through the container command, e.g. ["--port", "8080"] (changes only dsh's internal
port; the exposed port stays 3081).
Inside the container, dsh web is supervised by dsh-web: if it exits or crashes it is restarted
automatically. To restart it manually without restarting the container, run:
docker exec dsh dsh-restart
Quick start
Docker Compose (Linux)
docker compose -f examples/compose.yaml up -d
# open http://127.0.0.1:3081
Podman Quadlet (Linux, recommended)
sudo mkdir -p /etc/containers/systemd
sudo cp examples/dsh.container /etc/containers/systemd/
sudo systemctl daemon-reload
sudo systemctl enable --now dsh.service
Persistence — both examples mount one volume at
/home/dsh. This is the user layer:~/.dsh,~/.cargo, npm/cache/config files, dsh-created working folders, and user-installed
tools survive image upgrades; the system layer (/usr/local, apt packages) comes from the new image.
Networking —
dsh weblistens on127.0.0.1(npm releases reject--host 0.0.0.0); the
entrypoint runs a Caddy reverse proxy on0.0.0.0:3081that rewritesHost/Originto loopback
(→ dsh's127.0.0.1:3080), so the examples can use plain bridge networking with port3081
published. See security.md and the
deployment guide for details.
Documentation
| Document | Contents |
|---|---|
| docs/deployment.md | Deployment & maintenance: prerequisites, Compose, Quadlet, auto-update, remote access, offline use, FAQ |
| docs/security.md | Security notes: network exposure tradeoff, credentials, trusted workloads |
| docs/build.md | Build configuration: build args, version pinning, reproducible builds |
| docs/releasing.md | Image tags, release workflow (GitHub Releases + version alignment), image cleanup |
| docs/design.md | Design references and related projects |
| docs/development.md | Directory structure and local development |
License
安装
装一次目录插件,之后本站所有插件都能让 DeepSeek Harness 自动找、自动装:
dsh plugin add dshbase-catalog 然后对 agent 说「帮我装 dsh-container」,它会在目录里找到并自动安装。文档:dshbase-catalog · 已验证场景包。
该插件是 GitHub 源码(未发 npm)——直接从仓库装:
Web profile:
dsh plugin --profile web add github:NIyueeE/dsh-container Headless(CLI)profile:
dsh plugin --profile headless add github:NIyueeE/dsh-container 实测报告
验证通过:从 GitHub 源码完成 L1 安装 + L2 加载 + L3 运行(dsh 0.1.0-rc.6)。
使用场景
扩展 agent 的编码能力面——给它一个新工具、工作流或集成,让它接手以前做不了的开发任务。
适合谁
想让 dsh 在真实代码库上像队友一样干活的开发者——能改、能跑、能验证,而不只是回答问题。
二次开发建议
工具/命令面就是缝:暴露更多 SDK 能力、加更聪明的上下文接线,或收紧改代码与验证之间的循环。