# dshs > 📌 **本部署的两处文档别混(档案 19 §C10)** > - `docs/`(本仓库内)= **仓库自带 7 篇**(blueprint / deployment / k8s-deploy …)——**不要往这里写我们的改造记录**; > - **本项目的改造文档库**在另一仓库:`git@work.alotbuy.com:maogeigei/dsh_shenxian_doc.git`(服务器镜像 `/opt/dsh/docs`,root 600), > 含档案 01–26、`DEPLOY-本部署.md`(构建/部署/回滚)、`ops/`(nginx 与切换脚本)、`scripts/`(运维工具)。 > 运维排障先看 `DEPLOY-本部署.md` 与档案 02 附录 C。 **面向公网的多租户 DSH 托管平台** —— 部署到一台公网服务器后,多个用户注册并经管理员审核,各自获得一套**相互隔离**的 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)环境,随时通过域名安全访问(已适配手机端)。 > 以 [DSH 插件市场](https://github.com/bradeGithub/DSH-Plugins-Marketplace) 的 cordis-plugin 形态分发,遵守 [STANDARD.md](STANDARD.md)。 ## 它解决什么 DSH 本身是单用户本地工具:没有认证、没有多租户隔离、Web 远程访问缺认证层。`dshs` 在其之上补一层**服务端登录 + 多租户编排**: ``` 用户浏览器 │ HTTPS ▼ nginx(TLS 终结,主域 + *.子域 通配) ▼ 编排服务 dshs(Fastify,单进程) ├─ 认证 / 审核 / 管理台 / 网页桌面 / 域名 API └─ 按 Host 或 /u//dsh/* 路由 └─ 反向代理 → 各用户的 DSH 子进程(只绑 127.0.0.1 动态端口,不出公网) ``` 流程:管理员审核注册用户 → 每个用户落到自己的文件桌面 → 按文件夹启动 DSH → 通过域名访问,彼此文件隔离。 ## 核心能力 | 能力 | 说明 | |---|---| | **登录与审核** | `bootstrap-admin` 创建首个管理员;注册后需审核通过才能登录;禁用用户会同时删除其会话并停止运行中的 DSH | | **每用户隔离 DSH** | 主 DSH 常驻对外服务;崩溃时**按需拉起一次守护 DSH** 修复并自动重启主实例 | | **网页桌面** | 文件浏览 / 新建 / 上传;按文件夹启动 DSH;所有路径经「词法包含 + 符号链接分量」双重围栏校验 | | **每文件夹插件** | 自动检测该用户 profile 已安装的插件,按文件夹勾选启用,持久化并注入 cordis patch | | **多形态访问** | 默认子路径 `/u//dsh/`;每用户子域名 `<用户名>.`(HTTP + WebSocket);自定义域名 + nginx `server {}` 生成接口 | | **凭据隔离** | 每用户命名 API 密钥库,AES-256-GCM 加密落库(references-not-secrets);换 key 自动重建实例;spawn 只注入当前启用的 key | ## 部署形态(二选一) 同一套代码,靠 `DSHS_DEPLOY_MODE` 切换: | | 模式 A:直接部署(单服务器) | 模式 B:K8s + 容器化 | |---|---|---| | 形态 | 单机裸机,`child_process` + setuid/iptables | 多机 ACK,每用户独立 Pod | | 数据 | SQLite(本机文件) | PostgreSQL(CloudNativePG) | | 隔离 | 软隔离 / OS 账号硬隔离 | Pod 网络 + SecurityContext + NetworkPolicy | | 弹性/HA | 无(单点) | 控制面 3 副本 + leader election,DSH Pod 自动重建 | | 交付 | `git clone` + 脚本 | `kubectl apply -f deploy/` | 模式 B(K8s)**已于 2026-09-15 下线**:K8s 后端代码从本仓移除(备份在 `D:\github\_dsh_shenxian_K8s后端备份_20260915\`),`deploy/` 清单与设计文档**保留**作为未来可选路线;当前生产形态为**模式 A 单机部署**。历史实现(每用户 DSH Pod + tcp-bridge sidecar + file sidecar(8082)+ Headless Service + NetworkPolicy + 控制面 3 副本 + Lease 选主 + reconcile)见 [K8s 部署教程](docs/k8s-deployment.md) 与 [踩坑记录](docs/k8s-deploy.md)。 ## 快速开始(模式 A) 前置:Linux 服务器、Node **^22.19 或 ≥24**、已安装 DSH CLI(`npm i -g @deepseek-ai/dsh`)。完整的生产流程(systemd / DNS / 通配证书 / nginx)见[部署教程](docs/deployment.md)。 ```sh git clone https://work.alotbuy.com/maogeigei/dsh_shenxian.git cd dshs npm install && npm run build # tsc → lib/ # 1) 创建首个管理员(用它登录管理台) node lib/cli.js bootstrap-admin --username admin --password '<强密码>' --db ./dev.local.db # 2) 启动编排服务 node lib/cli.js --port 3080 --db ./dev.local.db ``` 打开 `http://127.0.0.1:3080/`: 1. 用 admin 登录进入管理台; 2. 另开浏览器注册一个普通用户 → 回管理台点「通过」; 3. 该用户重新登录进入桌面 → 上传文件 / 建文件夹 → 点「在此文件夹启动 DSH」; 4. 首次使用在桌面的「管理密钥」里填入自己的 DeepSeek API Key(加密存储;未设置时 DSH 能启动但无法调用模型)。 > ⚠️ 本地试跑可以验证登录 / 审核 / 桌面 / 启动全链路;但要**打开 DSH 聊天界面**,需要配置域名与每用户子域名(`BASE_DOMAIN` + `COOKIE_DOMAIN` + nginx 通配,见[部署教程](docs/deployment.md)第 6–9 步)——DSH 的 SPA 使用绝对路径,纯子路径方式加载不了静态资源。 ## 配置 环境变量均可省略;同名 CLI flag(`--port` / `--db` / `--isolation-mode` 等)优先级更高,全部见 `src/config.ts` 与 `node lib/cli.js --help`。 | 环境变量 | 默认 | 说明 | |---|---|---| | `DSHS_PORT` | `3080` | 编排服务绑定端口(nginx 上游) | | `DSHS_DATA_ROOT` | `~/.dshs` | 每用户 home/workspace 根(生产 `/var/lib/dshs`) | | `DSHS_DSH_BIN` | `dsh` | 子 DSH 可执行文件(建议绝对路径,systemd 下 PATH 精简) | | `DSHS_ISOLATION_MODE` | `soft` | `soft` 软隔离 / `account` 账号级硬隔离(Linux,需 root) | | `DSHS_BASE_UID` | `100000` | 账号级隔离的 uid 基数 | | `DSHS_PORT_GUARD` | `false` | iptables owner-match 端口守卫(Linux+root):阻止同机其他账号直连各用户 DSH 的回环端口;不支持的环境下开启会拒绝启动(fail loud) | | `DSHS_SECURE_COOKIES` | `false` | HTTPS 部署设为 `true`(cookie 加 `Secure`,SameSite=None 以跨子域共享) | | `DSHS_BASE_DOMAIN` | 空 | 每用户子域名的基域(如 `dsh.example.com`);空 = 仅子路径访问 | | `DSHS_COOKIE_DOMAIN` | 空 | 会话 cookie 的 `Domain`(如 `.dsh.example.com`,注意前导点);空 = host-only | | `DSHS_SESSION_TTL` | `604800` | 会话有效期(秒,默认 7 天) | | `DSHS_MAX_UPLOAD` | `25MB` | 上传请求体上限(base64 JSON) | | `DSHS_PACKAGE_DIR` | (自动探测) | **平台内置 dsh 的包根**。默认**按序探测**:`DSHS_DSH_BIN` 解软链反推 → `/usr/local/lib/node_modules`、`/usr/lib/node_modules` → `npm root -g`。⚠️ `npm i -g` 的落点随发行版变(发行版包管理器装的 Node 常落 `/usr/lib/node_modules`),**写死会让「模型设置」的厂家目录与「插件兼容性预检」静默失效**(无报错、只剩降级行为)⇒ 探测失败时才需要显式设它。 | | `DSHS_COMPAT_ROOT` | (= `DSHS_PACKAGE_DIR`) | 同上的旧名(兼容);同时接受 `DSH_PACKAGE_DIR` / `DSH_COMPAT_ROOT`(导出侧命名) | | `DSHS_PI_AI_DATA_DIR` | (自动探测) | `@earendil-works/pi-ai` 的**厂家目录**数据位置(`<包根>/node_modules/@earendil-works/pi-ai/dist/providers/data`);一般无需设置 | | `DSHS_RESTART_BACKOFF` | `1000` | 子 DSH 崩溃后的自动重启延迟(毫秒) | | `DSHS_ENABLE_PATCH` | `false` | 是否向子 DSH 注入 `--patch`(运行时插件 + 每文件夹插件;旧版 dsh 不支持时可关) | | `DSHS_SECRET` | 自动生成 | 每用户密钥库的加密主密钥;未设置时生成并持久化到 `/secret.key`(0600) | | `DSHS_DEPLOY_MODE` | `local` | `local` 单机 / `k8s` 每用户 Pod(模式 B) | | `DSHS_DB_URL` | 空 | Postgres DSN;设置即启用 Postgres(k8s 必填) | | `DSHS_NAMESPACE` | `dsh` | (k8s)每用户资源所在命名空间 | | `DSHS_DSH_IMAGE` | 空 | (k8s 必填)每用户 DSH Pod 镜像 | | `DSHS_CONTROL_PLANE_IMAGE` | 空 | (k8s 必填)file sidecar / tcp-bridge / init 容器镜像 | | `DSHS_IMAGE_PULL_SECRET` | `dsh-acr-pull` | (k8s)生成 Pod 的拉镜像 secret | | `DSHS_EGRESS_CIDRS` | 空 | (k8s)每用户 Pod 443 出站白名单 CIDR;空 = 全网(始终排除私网段与 169.254.169.254) | | `DSHS_K8S_SERVICE_ACCOUNT` | `dsh-orchestrator` | (k8s)控制面 ServiceAccount | ## 文档 | 文档 | 内容 | 什么时候读 | |---|---|---| | [部署教程(模式 A)](docs/deployment.md) | 从零到公网可用:Node / DSH / systemd / DNS / HTTPS / nginx | 第一次部署单机版,照着做即可 | | [硬隔离教程](docs/hard-isolation.md) | 每用户独立 OS 账号(`setpriv` 降权),生产环境建议开启 | 模式 A 跑通后的进阶加固 | | [域名配置示例](docs/domain-config.md) | 主域 + 每用户子域 + 自定义域名的 nginx 配置 | 配置域名 / 排查路由问题 | | [常见问题排查](docs/troubleshooting.md) | 502 / 404 / 401 / 403 / SSL / 端口冲突,按现象索引 | 出错了先来这里对现象 | | [K8s 部署教程(模式 B)](docs/k8s-deployment.md) | ACK + CNFS + CNPG 分步部署(多机 HA) | 要部署容器化多机形态 | | [K8s 踩坑记录](docs/k8s-deploy.md) | 模式 B 实机部署的坑与根因(镜像源 / 存储 / 网络 / 备案) | 模式 B 部署卡住时查 | | [技术蓝图](docs/blueprint.md) | 权威技术设计:拓扑 / 双 DSH / 数据模型 / API / 安全模型 | 想了解原理或参与开发 | | [PoC 实验记录](poc/README.md) | k8s 关键假设的四项实测(RWX / socat / NetworkPolicy / CNPG) | 评估模式 B 可行性时 | | [代码分层与依赖方向](docs/architecture.md) | 四层划分 + 依赖方向规则 + 三条工程纪律(契约前置 / 模块头注释 = 索引) | **动手改 `src/` 前读一遍**,尤其新增模块或目录时 | > 学习路线:**模式 A** 先读「部署教程」跑通 → 再看「硬隔离」加固,出问题查「常见问题排查」;**模式 B** 直接从「K8s 部署教程」开始,卡住查「K8s 踩坑记录」。 ## 开发 ```sh npm install # pnpm/npm 均可;Node ^22.19 || >=24 npm run typecheck # tsc --noEmit npm test # 构建 + node:test 单测(DB 双后端 / k8s spawner / leader / fs 围栏) npm run smoke # 及 smoke:* 系列:对运行中的服务做端到端冒烟(scripts/smoke*.mjs) ``` ## 安全 - **会话**:不透明随机 token,仅 SHA-256 哈希落库;cookie `HttpOnly` + `SameSite`,HTTPS 下加 `Secure`。 - **密码**:scrypt 加盐哈希,常数时间比较。 - **密钥**:每用户 API key 以 AES-256-GCM 加密落库,主密钥来自 env 或 `/secret.key`(0600)。 - **路径**:所有文件操作先做词法包含校验,再逐段拒绝符号链接分量,防止越出用户根目录。 - **隔离分层**:软隔离(默认)→ 账号级硬隔离(OS 账号 + `setpriv` 降权,`0700` 真正生效)→ 端口守卫(iptables owner-match)→ k8s Pod 边界(non-root / drop ALL / 只读 rootfs / NetworkPolicy)。 - **审计**:注册 / 登录 / 审核 / 改密钥等动作写入 `audit_log`。 细节与威胁模型见[技术蓝图 §7](docs/blueprint.md)、[硬隔离教程](docs/hard-isolation.md)。 ## 问题反馈与支持 这是我开发的第一个正式项目,功能可能还不够完善,非常欢迎大家提出意见和反馈! 如果你在使用过程中遇到问题,或者有改进建议,欢迎提交 [Issue](../../issues): - 🐛 **遇到 Bug**:请描述你的复现步骤、报错信息、以及运行环境(系统/DSH 版本等) - 💡 **功能建议**:请说明你的使用场景和你期望的效果 - 📖 **文档疑问**:指出 README 中看不懂或描述不清的地方 我会**尽量在 1~2 天内回复**,确认有效的问题会尽快修复并发布更新。 感谢你的支持!🌟