26 KiB
DeepSeek Harness 多租户容器管理平台 — 设计文档
状态:本机管理面板核心流程已实现并通过真实 Docker 集成检查;生产隔离、硬配额等仍有差距,以 README.md 的实现边界与验证范围为准。下文是目标设计,不代表所有条目已实现。 目标项目:
deepseek-ai/deepseek-harness(npm CLI@deepseek-ai/dsh,命令dsh web) 控制面技术栈:Go + 前端 SPA 部署形态:单机 Docker daemon;面板自身亦运行于容器中 鉴权/配额:内置账号 + 目录配额
0. 调研结论与硬约束
实现阶段实测更正:本地 npm
0.2.0-rc.2支持--trusted-host,但拒绝--public-url;下文基于 upstream master 的 public-url 描述不能直接用于该发行版。实际启动仅传 web/no-open/port/trusted-host,代理负责外部路径。scripts/probe-runtime.mjs已验证默认 token 交换为 303 + cookie、认证首页 200;完整代理以集成测试为准。
针对本体 deepseek-ai/deepseek-harness(npm CLI @deepseek-ai/dsh,命令 dsh web)确认:
| 事实 | 来源 | 对设计的影响 |
|---|---|---|
| 无官方 Docker 镜像/Dockerfile | repo 根、apps/、.gitlab-ci.yml 均无 |
必须自建镜像包装 dsh web |
dsh web 默认监听 127.0.0.1:3080,且启动时拒绝 --host 0.0.0.0(安全原因) |
packages/bundle/web-app/README.md |
容器内必须跑反向代理绑 0.0.0.0 转发到 loopback |
端口/主机是 CLI flag(--port/--host/--public-url/--trusted-host/--no-open),无 DSH_PORT/DSH_HOST 环境变量 |
同上 | 容器启动参数化靠 CMD args,不靠 env |
数据根 = $DSH_HOME(默认 ~/.dsh) |
docs/user/guide/providers.md、CLI README |
每实例独立 DSH_HOME,bind-mount 即可保留数据 |
多实例硬约束:每个容器必须独立 DSH_HOME + 独立端口(task-board 单写者) |
architecture.md | 一用户两容器 ⇒ 两个 DSH_HOME,不能共享 |
模型/API Key 存储:密钥在 $DSH_HOME/.credentials.yaml(YAML,write-only),模型/provider 配置在 $DSH_HOME/profiles/<profile>/cordis.patch.yml;cordis.patch.yml 只存 apiKeyEnv 引用 |
providers.md |
备份"模型"= 备份这两个文件 |
"profile 文件" = profiles/web/cordis.patch.yml(YAML,用户可编辑的 patch 层);profile 目录还有 package.json(含插件依赖 + dsh.profile bundle 清单)+ node_modules(pnpm 装的插件,可重建) |
apps/cli/README.md |
编辑 profile = 编辑 cordis.patch.yml;插件清单在 package.json |
升级:用户数据全在 DSH_HOME(npm 安装树之外),升级 @deepseek-ai/dsh 后数据保留;node_modules 由 pnpm 重建 |
README、CLI README | "保留数据 + 重启新版容器"可行 |
公网部署:--public-url <url/mount/> + --trusted-host;代理必须 剥前缀、保留 Host、把 Path=/ 的 cookie 改写为 mount + 加 Secure |
docs/user/guide/public-deployments.md |
面板反代要处理 cookie/前缀,是最复杂点 |
| 启动 URL 带 per-process token,首次访问换签名 cookie | 同上 | 面板需从容器日志取 token,服务端代换 cookie |
结论:DSH 不是为容器多租户设计,但靠"自建镜像 + 容器内反代 + 面板反代桥接 token/cookie + 每实例独立 DSH_HOME"可稳态支撑。
1. 目标与范围
- 通过 docker.sock 在单机上创建/启停/删除多个
dsh web容器,每用户最多 2 个。 - 每用户一个独立工作区,支持文件上传/下载/删除/重命名,并有容量配额。
- 工作区映射进每个 dsh 容器(agent 在工作区内干活)。
- 备份/还原 dsh 配置(含模型与插件清单)。
- 编辑 profile 文件(
cordis.patch.yml)。 - 升级:保留数据,用新版本镜像重启容器。
- 本地镜像管理:面板查询 DSH 最新版本,用户手动触发本机构建;创建实例时选择本地已有版本,或手动构建最新版后创建。不需要私有镜像仓库,不推送生成的镜像。
- 控制面:Go 后端 + SPA 前端;内置账号 + 目录配额;面板自身容器化运行。
2. 总体架构
面板不是宿主进程,而是挂了 docker.sock 的兄弟容器(sibling),通过 sock 编排 dsh 兄弟容器,并通过同一 bridge 网络反代它们。
┌────────────── 宿主 ──────────────────┐
│ docker.sock ──┐ │
│ /var/lib/dsh-mgr ──┐ (bind) │
│ │ │
│ ┌─────────────────▼─────────────▼───┐ │ dsh-net (bridge)
│ │ 面板容器 dsh-mgr │ │ :80/:443 发布
│ │ - /data ← /var/lib/dsh-mgr │ │
│ │ - /var/run/docker.sock │ │
│ │ - SPA + API + 反代 + 编排引擎 │ │
│ └─────────┬───────────────────────────┘ │
│ │ sock │
│ ┌─────────▼───────────┐ ┌───────────────┐ │
│ │ dsh-web:<v> 容器1 │ │ 容器2 ... │ │ 不发布端口
│ │ caddy:8080 → 3080 │ │ │ │ /data/dsh-home、/workspace ← 宿主 bind
│ └─────────────────────┘ └───────────────┘ │
└─────────────────────────────────────────────┘
组件:
- 面板服务(Go 容器):REST API + 静态 SPA + 反向代理子系统 + docker client + 配额/备份/升级引擎。
- dsh-web 镜像(自建):Node +
@deepseek-ai/dsh+ caddy + 入口脚本。 - dsh-net:内部 bridge 网络,容器不发布主机端口,仅面板可达。
- (可选)socket-proxy:面板与 sock 之间的白名单代理。
3. 宿主路径 ↔ 面板内路径映射(关键)
Docker bind 的 Source 必须是守护进程所在宿主路径,不是面板容器内路径。面板容器把宿主 /var/lib/dsh-mgr 挂到 /data,于是同一文件树有两个视角:
| 用途 | 面板直接读写(容器视角) | 创建 dsh 容器 bind.Source(宿主视角) |
|---|---|---|
| 用户工作区 | /data/workspaces/<uid>/files/... |
/var/lib/dsh-mgr/workspaces/<uid>/files |
| 实例 DSH_HOME | /data/workspaces/<uid>/instances/<id>/home/... |
/var/lib/dsh-mgr/workspaces/<uid>/instances/<id>/home |
| 备份包 | /data/backups/<uid>/<id>/... |
(不入容器,仅面板读写) |
面板配置:
HOST_DATA_ROOT=/var/lib/dsh-mgr # 传给 docker API 的 bind 源
CONTAINER_DATA_ROOT=/data # 面板自己 fs 操作的根
- 所有"面板直接动文件"(文件 API、备份 tar、profile 读写)用
CONTAINER_DATA_ROOT。 - 所有"喂给 docker API 的 bind.Source / build context"用
HOST_DATA_ROOT拼接。 - 封装
toHostPath(containerPath)翻译函数集中调用,避免散落拼接出错——这是 sibling 容器最常见的 bug。
4. 数据与目录布局(宿主机)
/var/lib/dsh-mgr/
db.sqlite # 用户、实例、配额、备份索引
workspaces/<uid>/
files/ # 用户工作区(面板文件 API + 各 dsh 容器共享挂载)
└─ <用户自由目录结构>
instances/<instId>/
home/ # DSH_HOME(bind → 容器 /data/dsh-home)
.credentials.yaml # API Key(机密)
cordis.patch.yml # home 级 patch
profiles/web/
package.json # 插件依赖 + dsh.profile bundle 清单
cordis.patch.yml # profile 级 patch(= "profile 文件")
node_modules/ # 插件实体(可重建,不入备份)
skins/ pets/ agent-presets/ .agent-presets/
dsh-ssh.json # 0600
sessions/ worktrees/ # 运行态(不入配置备份)
backups/<uid>/<instId>/
<timestamp>.tar.gz # 配置备份包
# 镜像层由本机 Docker daemon 管理,不作为此目录下的文件保存;DB 记录镜像索引
配额作用域:workspaces/<uid>/ 整棵树(files + 所有 instances 的 home)。每用户一个配额单位。
5. 自建 DSH 镜像设计
FROM node:22-slim
# 1) 装 dsh CLI(版本参数化)
ARG DSH_VERSION=0.2.0-rc.2
RUN npm i -g @deepseek-ai/dsh@${DSH_VERSION}
# 2) 装 caddy(容器内反代,绕过 dsh 拒绝 0.0.0.0)
COPY caddy /usr/bin/caddy
COPY Caddyfile /etc/caddy/Caddyfile # :8080 { reverse_proxy 127.0.0.1:3080 }
COPY entrypoint.sh /entrypoint.sh
ENV DSH_HOME=/data/dsh-home
WORKDIR /workspace
ENTRYPOINT ["/entrypoint.sh"]
# CMD 由面板按实例注入:dsh web --no-open --port 3080 --public-url <url> --trusted-host <host>
entrypoint.sh:tini接管信号 → 起 caddy(后台)→exec dsh "$@"。dsh 绑 loopback:3080,caddy 绑 0.0.0.0:8080。- 存储方式:生成的镜像仅保存在本机 Docker daemon,不推送 registry,也不从 registry 拉取成品
dsh-web镜像。所有用户共享面板管理的本地镜像目录。 - 版本查询:面板从 npm 查询
@deepseek-ai/dsh的dist-tags.latest,展示精确版本和查询时间;查询不触发构建或升级。不通过版本字符串排序猜测最新版。查询失败时保留本地版本列表,缓存结果需标明过期。 - 版本固定:用户确认构建时锁定页面展示的精确版本,后端校验其确实存在于 npm 元数据中;构建过程中不再次解析
latest,Dockerfile 不安装浮动的@latest。 - 本机构建:Docker API
POST /build接受 tar 流;面板将内置/image/的 Dockerfile+caddy+entrypoint+Caddyfile 打包后提交给 daemon,并通过 Docker API 的 buildargs 传入DSH_VERSION。用户不能提交任意 Dockerfile、构建上下文、镜像地址或其他构建参数。 - 镜像身份:tag 使用
dsh-web:<dshver>-<recipeRev>,其中recipeRev标识镜像模板修订;记录精确 DSH 版本、模板修订、架构、本地 image ID 和构建时间。创建实例使用已确认的 image ID,不依赖可能被覆盖的 tag。 - 构建任务:手动触发后异步执行,展示排队、构建中、验证中、成功、失败状态及脱敏日志;任务持久化。同版本、模板修订和架构的并发请求复用同一任务,已有可用镜像直接复用。默认全局串行构建,并限制用户触发频率、任务时长及日志大小,构建前检查磁盘空间。
- 可用性判定:Docker build 成功后,用临时隔离容器验证版本和 DSH 启动就绪;验证通过才加入可选择列表。失败保留错误记录,不自动替换已有可用镜像。面板重启后对账未完成任务及本地镜像,不盲目重复构建。
- 本地清单:以 Docker inspect/list 和面板管理标签对账,展示精确版本、构建时间、可用状态及使用情况。外部删除导致 image ID 不存在时标记缺失,不再允许新建;管理员清理时保护被实例、待执行任务或回滚流程引用的镜像,不自动运行全局 prune。
- 网络要求:仅本地保存不代表离线构建;首次构建仍可能需要拉取基础镜像及下载 npm 包。网络故障不影响选择已存在的本地可用镜像。
创建实例交互:
- 选择本地版本:从本地可用列表选择版本,校验 image ID 仍存在后创建。
- 构建最新版并创建:查询并展示精确版本 → 用户确认 → 事务预留实例名额 → 构建或复用任务 → 验证成功 → 用该 image ID 创建实例。等待构建的实例也计入每用户 2 个名额;构建/创建失败或取消时清理未完成资源并释放名额,不回退到其他版本。取消某个用户的等待不取消其他用户共享的构建任务。
- 面板另提供独立的“构建最新版”按钮,仅生成本地镜像,不创建或升级实例,也不占实例名额。
6. 容器生命周期(docker.sock + socket-proxy)
Go 端用 github.com/docker/docker/client:
- 创建:
- Image = 本地可用镜像的固定 image ID(对应精确 DSH 版本及模板修订,见 §5)
- Env:
DSH_HOME=/data/dsh-home(密钥不入 env;用户在 dsh UI 里设 key,落到.credentials.yaml) - Binds(Source 用 HOST 路径):
workspaces/<uid>/instances/<instId>/home→/data/dsh-homeworkspaces/<uid>/files→/workspace
- Network =
dsh-net(不发布端口) - Cmd =
web --no-open --port 3080 --public-url https://<panel>/dsh/<instId>/ --trusted-host <panel> - Labels:
dsh-mgr.uid、dsh-mgr.instId、dsh-mgr.version、dsh-mgr.role=dsh - 资源:
--memory=2g --cpus=2(可配);--tmpfs /tmp;只读 rootfs + 必要写入挂载
- 操作:start/stop/restart/remove/logs/inspect/exec,全走 SDK。
- 2 实例上限:按逻辑实例在 DB 事务中预留名额,等待构建/创建中的实例也占名额;失败清理后释放。Docker labels 用于对账,不作为并发名额控制。
- 对账:面板启动时按 label 枚举容器与 DB 对账(孤儿容器回收或纳管)。
- sock 安全:面板挂裸 sock = 等效宿主 root。缓解:
- socket-proxy(推荐):只放行白名单 API(containers/exec/images/networks/volumes/logs/build),关 plugins/daemon/swarm。面板连 proxy 而非裸 sock。
- 面板自身非 root、只读 rootfs + tmpfs /tmp、资源限制。dsh 容器不
--privileged、去 cap。
7. 网络与访问方式
- 面板启动时 ensure
dsh-net(bridge)存在,并把自身加入(compose 声明或运行时network connect)。 - dsh 容器挂
dsh-net,不发布主机端口。 - 面板反代
/dsh/<instId>/→http://<container-name>.dsh-net:8080,容器名 = instId。 - 访问方式(待定,见 §18):默认推荐"路径前缀反代 + token 桥接"(§8),面板天然为 ingress。备选:子域名(需泛 DNS+TLS)/ 直发端口(MVP,隔离弱)。
8. 访问与认证(token 桥接 + cookie 改写)
此为最复杂点。推荐方案:路径前缀反代 + token 桥接。
- 面板路由
/dsh/<instId>/*→ 反代到http://<instId>.dsh-net:8080,剥掉/dsh/<instId>前缀,保留浏览器 Host,加X-Forwarded-*。 - token 桥接:容器启动后,面板从
docker logs流式抓 dsh 打印的http://127.0.0.1:3080/?token=xxx里的 token;用户首次打开/dsh/<instId>/时,面板服务端用该 token 打一次容器换 cookie,再把Set-Cookie的Path=/改写为Path=/dsh/<instId>、加Secure/HttpOnly/SameSite=Lax下发给浏览器。后续请求带该 cookie 透传。 - 鉴权:面板先校验用户登录 + 该 instId 归属当前 uid,再放行反代。dsh 自身 token cookie 为第二层。
- WebSocket/SSE:反代需支持 WS 升级;cookie 改写后需实测 WS 握手是否正常(关键风险)。
9. 工作区文件管理 + 容量配额
文件 API(path 校验:解析后必须在 files/ 内,拒 ../绝对路径):
GET /api/ws/tree?path=列目录(带 size/mtime)POST /api/ws/mkdir、POST /api/ws/rename、DELETE /api/ws/deletePOST /api/ws/upload(分片/流式,写前查配额)GET /api/ws/download?path=(单文件流;目录自动 zip)GET /api/ws/usage(当前用量/配额)
配额实现(容器化面板的约束):
- 默认:软配额(无特权,随处可跑)。面板写前
du校验 + 周期巡检 + 写入并发限流。并发大文件可能短暂超限。 - 可选硬配额(二选一,需运维确认):
- XFS prjquota:面板
--privileged+cap_add:[SYS_ADMIN,CHOWN]+ 把宿主 XFS 挂载点 bind 进来,面板内对/data跑xfs_quota/chattr -p。每用户唯一 project ID(uid+1000)。 - ZFS refquota(若宿主用 ZFS):面板
--privileged+ zfsutils,每用户zfs create pool/users/<uid> && zfs set refquota=10G,再 bind 该 dataset 到/data/workspaces/<uid>。 - 或在宿主跑极小特权配额 sidecar(gRPC/HTTP),面板远程调它做 quota 操作——面板自身保持非特权。
- XFS prjquota:面板
- 配额值存 DB,按用户可配(默认 10GiB)。面板展示用量。
具体选型见 §18 待拍板。
10. 配置备份与还原(含模型与插件)
备份内容(tar.gz,排除可重建/运行态):
| 包含 | 说明 |
|---|---|
.credentials.yaml |
模型 API Key(机密)→ 备份包加密 |
cordis.patch.yml(home 级) |
home patch |
profiles/web/package.json |
插件清单(含 dsh.profile bundle 列表) |
profiles/web/cordis.patch.yml |
模型/provider 配置 + apiKeyEnv 引用 |
profiles/web/pnpm-lock.yaml(若有) |
插件版本锁 |
skins/ pets/ agent-presets/ .agent-presets/ |
用户资源 |
dsh-ssh.json |
SSH 凭据(0600,加密入包) |
manifest.json |
dsh 版本、备份 schema 版、时间戳 |
排除:node_modules/(pnpm 重建)、worktrees/、sessions/、日志。
备份包加密:用用户密码派生密钥(Argon2id → AES-GCM)加密整包;密钥不落库。下载/上传均加密包。
备份流程:停容器(或仅停写)→ tar 指定文件 → 加密 → 落 backups/<uid>/<instId>/ + 索引入库 → 重启容器。支持立即下载。
还原流程:校验+解密包 → 停容器 → 备份当前 home 为 .pre-restore.bak → 覆盖配置文件(不动 node_modules)→ 按 package.json 重建插件(dsh plugin --profile web install 或 entrypoint 首启时 pnpm reconcile)→ 启容器 → 健康检查 → 失败则回滚 .pre-restore.bak。
11. profile 文件编辑
- 编辑对象:
profiles/web/cordis.patch.yml(YAML)。 - 前端用 Monaco 编辑器;后端
GET返回原文、POST校验(YAML 合法 + 可选--dump-config-schema比对)后原子写(临时文件 + rename,dsh-atomic-write风格)。 - 保存后重启容器使生效(dsh 有
dsh-hmr,但配置类变更稳妥起见重启;后续可探热更)。 - 只读展示
dsh.profile(bundle 清单)与--dump-config预览(面板docker exec跑dsh --profile web --dump-config取回渲染)。 - 不暴露
.credentials.yaml原文(write-only 语义)。
12. 升级流程(保留数据 + 重启新版容器)
1. 用户选择本地可用目标版本;若需最新版,先手动触发 §5 构建并等待验证成功。锁定本地 image ID 后才进入备份/停机流程,不自动 pull 成品镜像
2. 自动备份当前配置(§10)→ 标记 pre-upgrade 备份
3. docker stop <old>(不 remove,保留作回滚)
4. docker create <new>:同名 instId、同 binds(home + files)、同 labels、新 image、
Cmd 按新版本要求重算 --public-url/--trusted-host
5. entrypoint 首启:若 profiles/web/node_modules 缺失/与 package.json 不一致 → pnpm install 重建插件
6. docker start <new> → 面板抓 token、健康检查(轮询 caddy :8080)
7. 成功:docker rm <old>;失败:stop <new>、start <old>、标记回滚、提示用户
关键:bind 不变 ⇒ 数据零丢失;node_modules 在新镜像里按 package.json 重建(跨版本兼容性由 pnpm/dsh-plugin-manager 保证)。
13. 安全与资源
- docker.sock:面板容器能访问 sock 即等效宿主 root;socket-proxy 白名单 + 面板非 root + 只读 rootfs 缓解。
- dsh 容器:不
--privileged、去 CAP_NET_RAW 等、只读 rootfs、tmpfs /tmp、mem/cpu 限制、不挂 sock。 - 面板账号:bcrypt + JWT(短期 access + refresh),失败锁定。
- 备份包加密、
.credentials.yaml不经 API 明文返回。 - 路径穿越校验、上传大小/类型白名单(可配)。
- 审计日志:容器操作/备份/还原/升级/profile 改动入库。
14. 技术栈与项目结构
- 后端:Go 1.22+,
net/http(或 chi/gin),github.com/docker/docker/client,github.com/docker/go-connections,sqlite(modernc.org/sqlite纯 Go 驱动,免 CGO),Argon2id、AES-GCM。 - 前端:Vite + React + TypeScript,Monaco(profile 编辑),shadcn/ui。
dsh-mgr/
cmd/server/ # Go 入口
internal/
auth/ # 账号/JWT
docker/ # 容器编排(镜像、生命周期、对账、路径翻译)
workspace/ # 文件 API、配额(软 du + 可选 xfs/zfs)
proxy/ # /dsh/<id> 反代 + token 桥接 + cookie 改写
backup/ # 备份/还原/加密
upgrade/ # 升级编排
profile/ # cordis.patch.yml 编辑
store/ # sqlite schema/查询
api/ # HTTP handlers
web/ # SPA
image/ # 自建 dsh-web 镜像(Dockerfile + Caddyfile + entrypoint.sh + caddy 二进制)
deploy/ # 面板自身 docker-compose / socket-proxy / systemd
15. 数据模型(sqlite 概要)
users(id, username, pass_hash, quota_bytes, created_at)instances(id, uid, name, dsh_version, image_id, build_id, status, container_id, created_at, ...)backups(id, uid, inst_id, path, size, schema_ver, created_at, kind=auto|manual|pre-upgrade|pre-restore)images(image_id, version, recipe_rev, platform, tag, status, built_at)(本地镜像索引,status=validating|available|failed|missing;与 Docker 实际状态对账)image_builds(id, requested_by, version, recipe_rev, platform, status, image_id, error, created_at, finished_at)(持久化构建任务;同版本+模板修订+架构的活动任务去重)audit(id, uid, action, target, detail, at)
16. 面板自身部署(compose 示例)
services:
dsh-mgr:
image: dsh-mgr:latest
restart: unless-stopped
ports:
- "80:80" # 前置 TLS 反代或直接 443
user: "65532:65532" # 非 root
environment:
HOST_DATA_ROOT: /var/lib/dsh-mgr
CONTAINER_DATA_ROOT: /data
PANEL_PUBLIC_URL: https://dsh.example.com
DSH_NET_NAME: dsh-net
DEFAULT_QUOTA_BYTES: 10737418240
DOCKER_HOST: unix:///var/run/docker.sock # 或指向 socket-proxy
volumes:
- /var/lib/dsh-mgr:/data
- /var/run/docker.sock:/var/run/docker.sock:ro # 或 socket-proxy
networks: [dsh-net]
read_only: true
tmpfs: [/tmp]
deploy: { resources: { limits: { memory: 1g } } }
# 硬配额(XFS)可选: privileged: true; cap_add: [SYS_ADMIN,CHOWN]; volumes: [xfs mount]
# 可选 socket-proxy
socket-proxy:
image: tecnativa/docker-socket-proxy
environment:
CONTAINERS: 1; EXEC: 1; IMAGES: 1; NETWORKS: 1; VOLUMES: 1; LOGS: 1; BUILD: 1
POST: 1
volumes: [/var/run/docker.sock:/var/run/docker.sock:ro]
networks: [dsh-net]
restart: unless-stopped
networks:
dsh-net: { driver: bridge }
17. 关键风险与待验证
- token/cookie 桥接(§8)最脆——需实测 dsh token 换 cookie 在反代+前缀下的行为,确认 cookie 改写后 WS/SSE 正常(dsh web 可能有 WebSocket)。
- 插件首启重建:跨 dsh 版本
pnpm install在 entrypoint 无网时失败——可能需升级时由面板docker exec联网重建后再切流量。 - XFS/ZFS 硬配额依赖宿主 FS 与特权/agent;否则退化为软配额。
- dsh agent 工作目录:
dsh-fs-local/dsh-fs-sandbox的根是否等于 cwd——需确认/workspace作为 cwd 即可让 agent 在工作区操作,或需额外配置 fs root。 - dsh 版本仍为
0.2.0-rc.*(RC),API/flag 可能变动——需维护版本兼容矩阵。 - sibling bind 路径翻译:
toHostPath出错会导致 bind 挂错目录或挂空目录,需单测覆盖。 - 本地镜像构建:验证 npm 查询失败仍可选本地版本、并发构建去重、失败释放实例名额、面板重启对账、镜像被外部删除,以及构建磁盘不足等路径;构建最新版不保证兼容,必须通过启动验证。
18. 待拍板决策点(open)
已确认:镜像仅保存在本机;面板支持查询并手动构建最新版;创建实例可选本地已有版本,或构建最新版后创建。不需要 registry/CI 镜像分发。
以下访问方式和配额选型仍待确认;此前审查发现的隔离、文件安全和升级回滚问题仍需另行修订。
- 访问方式:路径前缀反代 + token 桥接(推荐,面板天然 ingress)/子域名(需泛 DNS+TLS)/直发端口(MVP,隔离弱)。
- 推荐默认:路径前缀反代。
- 配额:宿主 FS 是 XFS / ZFS / 其他?是否接受"特权面板"或"宿主配额 sidecar"做硬配额?还是软配额默认即可?
- 推荐默认:软配额(du)起步,硬配额作为后续增强;若宿主为 XFS/ZFS 且可接受特权,则上硬配额。
19. 里程碑
- M1 镜像 + 单容器手动跑通:自建本地
dsh-web镜像,验证精确版本、caddy+dsh 启动与就绪。 - M2 面板骨架:账号/鉴权 + npm 最新版查询 + 本地镜像列表/手动构建任务 + 创建时选本地版本或构建最新版 + docker 编排(创建/启停/删除/2 上限)+ 对账 + 路径翻译。
- M3 反代 + token 桥接:
/dsh/<id>/可用(含 WS)。 - M4 工作区文件 API + 软配额。
- M5 备份/还原 + profile 编辑。
- M6 升级流程 + 回滚。
- M7 SPA 完善 + 审计 + 资源限制 + 可选硬配额。
附:调研依据(关键 URL)
- 仓库:https://github.com/deepseek-ai/deepseek-harness
- README:https://raw.githubusercontent.com/deepseek-ai/deepseek-harness/master/README.md
- CLI README:https://raw.githubusercontent.com/deepseek-ai/deepseek-harness/master/apps/cli/README.md
- web-app README:https://raw.githubusercontent.com/deepseek-ai/deepseek-harness/master/packages/bundle/web-app/README.md
- providers 指南:https://raw.githubusercontent.com/deepseek-ai/deepseek-harness/master/docs/user/guide/providers.md
- 公网部署指南:https://raw.githubusercontent.com/deepseek-ai/deepseek-harness/master/docs/user/guide/public-deployments.md
- npm 包:https://registry.npmjs.org/@deepseek-ai/dsh/latest