起因:用户 2026-09-17 明确「所有文档都要同步,都放在开发仓库 docs 对应文件夹下」。
判据:工作区根 *.md 中在仓库(git ls-files --quotepath=false)搜不到的那些。
入仓 33 份(一律复制,工作区根原件保留不动,避免引用断链):
- 04-调整方案/113–128(16 份 · 原子占号后落盘):覆盖网络 传输方案取舍 / 应用场景与待完善清单 /
插件化vs改内核 / 问题逐条推演 / 参数表与观测口径;会合中继拆分取证与改造方案;
集群化改造方案 Manager-Worker;跨节点迁移与节点自举;项目代码分层范式与迭代风险评估;
搬运与共享重建方案 guest w47→w106;方案规划方法提炼;文档无效信息审计报告;
会话接续机制复盘与修复;会话接续规范;dsh 客户端化部署方案;dsh 桌面客户端开发方案
- 交接单/archive/交接单-已完成/T09–T21(13 份 · 覆盖网络线已完成单归档)
- ops/(2 份运行态指针:接续入口 / 接续包 · 覆盖网络线)
- archive/(2 份临时与内部简报)
已排除(无需重复入仓):覆盖网络线 10 份方案正文已入档案 103–112(文件名不同)。
登记:INDEX.md §二 新增 04-113–128 共 16 行 + §四 追加 T09–T21 说明 + 机器摘要行刷新
(⛔ 未跑 docs-index-stats.py --write:该脚本会按 \r\n 归一化全文件行尾,故改为字节级单行替换);
README.md 追加 1 条入仓指针;docs-manifest.json 复跑 scripts/docs-manifest.py 刷新。
验收:工作区根 47 份 .md —— 同名已入仓 8 / 本次内容一致 29 / 已知改名映射 10 / 未入仓 0;
git status 待提交清单只含本次新增与登记 3 件(未涉 src/ 与 relay 代码面)。
15 KiB
DSH 平台客户端化部署方案 —— 单机自用
- 版本:v3(2026-09-16;v1 规划稿 → v2 多用户 → v3 范围收窄为单机自用)
- 性质:本文只做规划,不含任何代码改动
- 定位:与
集群化改造方案_Manager-Worker_20260914.md同级,是「客户端化」这件事平台侧的单一来源 - 分层指针(2026-09-16 收口):本文管平台侧(能不能在 Windows 跑起来、形态怎么切);交付载体层(Electron 壳 / 打包 / 签名 / 自动更新)以
dsh桌面客户端_开发方案_20260916.md为准 —— 两份合起来才是完整的「客户端化」,⛔ 不各写一份
0. 一句话方案
把平台装到用户自己的 Windows 电脑上,本机浏览器打开 http://127.0.0.1:3080,一个人用。
不需要局域网、不需要域名、不需要证书、不需要开机自启(可选)。 平台默认就监听回环地址 —— 这一项连配置都不用改。
1. 本次范围收窄的两条原则
1.1 使用者只有他自己
不考虑当作服务器、不考虑别人访问。 由此一次性消掉的东西:
| 消掉的 | 原因 |
|---|---|
监听 0.0.0.0 + 防火墙放行 |
只有本机访问,回环地址即可 |
| 多账号 / 逐用户开通 | 只有一个人 |
| 子域或子路径分流 | 直接访问门户首页即可 |
| 租户隔离 | 没有"别人" |
| 开机自启(服务化) | 双击启动即可,非必需 |
1.2 平台机制全部保留,只是在客户端部署时不启用
这是一条硬约束:不为客户端化去删改多租户相关的代码。 客户端形态下这些机制照旧存在、照旧可用,只是没有使用场景。
用户的判断依据:多租户需要域名才能跑起来,而客户端没有域名 ⇒ 客户端就用单用户形态。
因此本方案对代码的要求只有一条:让平台能在 Windows 上把实例启起来(见 §4)。 多租户、门户、账号体系、隔离档位、配额、集群 —— 一行都不动。
1.3 多人形态没有被去掉 —— "配域名即启用"
形态开关就是一个配置项,平台本来就支持,⛔ 不需要改任何代码:
| 配置 | 形态 | 用户地址 |
|---|---|---|
| 域名留空(默认) | 单机自用 | http://127.0.0.1:3080 |
| 配了域名 | 多人访问 | http://<用户名>.<域名> |
- 开关 =
DSHS_BASE_DOMAIN(配DSHS_COOKIE_DOMAIN;有 HTTPS 时再加DSHS_SECURE_COOKIES)—— 三个都是环境变量,见src/config.ts:320-321 - 配套(用户侧):内网 DNS 泛解析
*.<域名>→ 本机地址 - ✅ 不需要 HTTPS 也能跑:门户域与用户子域属于同一 site,Cookie 的
SameSite=Lax足以支撑跨子域跳转(src/web/auth.ts:55-57即按"有没有 HTTPS"区分这两档) - ⚠️ 隔离档位保持默认
soft⇒ 多人访问时用户之间无隔离,与 §1.1 的取舍一致(且account档位在 Windows 上本来就不可用)
本节结论:客户端版本天然保留"可以当服务器"的能力 —— 启用它不需要写一行代码,只需要用户填一个域名。
🔒 切多人形态的前置检查(2026-09-16 加 —— 未过则不得开这个开关) 这个开关零代码,但它会同时打开三个被"单机自用"关掉的风险面:
- 隔离档仍是
soft⇒ 用户之间没有隔离(且account档在 Windows 上本来不可用)⇒ 多人形态的前置条件 = 先有隔离方案;在拿到之前,只能明确限定为"仅互信小圈子使用",并且页面要写明这一点。- 平台级凭据不得随客户端分发:域名配置要透传给平台,但透传必须是白名单 —— 只放
DSHS_BASE_DOMAIN/DSHS_COOKIE_DOMAIN/DSHS_SECURE_COOKIES三项,⛔ 不得把平台共享模型密钥一并继承下去;模型密钥只走"用户自己的密钥"那一层。- 实例必须属于机器主人:一台机器上的实例只能归该机器使用者 —— 多人形态下需显式保证,不能靠"默认只有一个用户"蒙过去。
2. 范围收窄带来的变化(相对 v2)
| 维度 | v2(内网多用户) | v3(单机自用) |
|---|---|---|
| 监听地址 | 改 0.0.0.0 + 防火墙 |
默认 127.0.0.1,零配置 |
| 账号 | 多账号 + 逐个开通 | 一个本地账号即可 |
| 入口 | 纯 IP + 子路径分流 | 直接 http://127.0.0.1:3080 |
| 隔离 | 不需要 | 不需要(没有"别人") |
| 服务化 | 开机自启(必需) | 可选 |
| 风险「用户互读文件」 | 🔴 须书面告知客户 | 不存在 |
| 待改代码 | 配置 + 打包 + spawn 适配 | 只剩 spawn 适配 + 打包 |
3. 现成能力盘点(不需要动的东西)
| 能力 | 现状 | 依据 |
|---|---|---|
| 监听回环 | 默认就是 127.0.0.1 |
config.ts:162 / cli.ts:32 |
| 数据根 | 默认 ~/.dshs(非硬编码) |
config.ts:257 |
| 数据库 | 默认 SQLite 单文件 | config.ts:271 |
| 免 HTTPS | --secure-cookies 默认关 |
config.ts:281 |
| 文件属主处理 | chown/chmod 块条件是 process.getuid() === 0 |
实测 Windows 上 typeof process.getuid === 'undefined' ⇒ 整块自动跳过(local-user-fs.ts:40) |
| 端口守卫 | 默认 false | config.ts:323 |
| 单机自检 | dshs doctor |
装机与排障直接用 |
| 版本巡检 | runtime-baseline.cjs(Node 写) |
跨平台 |
4. 硬阻塞点(已在本机 Windows 实测)
4.1 唯一的硬阻塞:平台启动实例的方式在 Windows 上不成立
平台用 spawn(command, args) 启动实例,不带 shell(orchestrator.ts:663-665)。
Windows 上 npm 全局包是 .cmd 垫片,于是:
| 尝试 | 本机实测结果 | 判定 |
|---|---|---|
spawn('npm', ['--version'])(裸名,=平台当前做法) |
ERROR(event): ENOENT |
❌ 起不来 |
spawn('…\\npm.cmd', ['--version'])(显式 .cmd) |
THROW(sync): EINVAL |
❌ Node 安全限制 |
spawn('cmd.exe', ['/c', 'npm', '--version']) |
OK exit=0,输出 10.9.7 |
✅ 可用 |
spawn(p, { shell: true }) |
OK exit=0 |
✅ 可用 |
复现(Windows + Node 22):
node -e "require('child_process').spawn('npm',['--version'],{stdio:'inherit'}).on('error',e=>console.log(e.code))"→ 打印ENOENT
结论:必须让平台在 Windows 上以 cmd.exe /c 或 shell: true 启动实例。这是全案唯一必须改代码的地方。
4.2 附带的小点
| # | 点 | 判定 |
|---|---|---|
| A | 播种工作区时无条件 chownSync(orchestrator.ts:503) |
🟢 非阻塞:有 try/catch 兜底,只会刷一行失败日志 |
| B | dshs doctor 会探测 cgroup 等 Linux 项 |
🟢 非阻塞:只是自检输出不准 |
5. 改造清单
5.1 必做
| # | 事项 | 量级 | 说明 |
|---|---|---|---|
| C1 | Windows 子进程启动适配 | 小 | 实例启动处对 win32 走 cmd.exe /c;建议做成可配置 |
| C2 | 安装包 / 一键安装脚本 | 中 | 装 Node → 装平台与 dsh → 建目录 → 初始化本地账号 → 建启动快捷方式 → 自检 |
5.2 该做
| # | 事项 | 说明 |
|---|---|---|
| C3 | 启动器 | 双击启动 + 自动打开浏览器;退出时干净收尾(别留孤儿进程) |
| C4 | 容量参数按单机重算 | 现有默认按 2C2G 多用户宿主定的;Windows 无 cgroup,需靠并发上限约束 |
| C5 | 备份与清理的 Windows 实现 | 现有 .sh 那批要换;Node 写的那批可直接用 |
5.3 可选
| # | 事项 |
|---|---|
| C6 | 开机自启(单机自用非必需) |
| C7 | 顺手修 §4.2 的两处小点 |
| C8 | 免登录形态(本机单人,可考虑省掉登录步骤) |
⛔ 不在清单里 = 不做:多租户、门户、账号体系、隔离档位、配额、集群 —— 一律不改(§1.2)。
6. 落地步骤
| 步 | 动作 | 验证方式 |
|---|---|---|
| S1 | 装 Node.js(22+) | node -v 有输出 |
| S2 | 装平台 + 官方 dsh | dshs --help 与 dsh --version 都有输出 |
| S3 | 初始化本地账号 | 能登录门户 |
| S4 | 第一次启动 | 浏览器打开 http://127.0.0.1:3080 出现门户 |
| S5 | 打开实例(这一步验证 C1) | 实例能起来并进入对话界面 |
| S6 | 建启动快捷方式 | 双击即可启动并自动开浏览器 |
| S7 | 断网验证 | 断开公网(保留模型 API 通路)后一切正常 |
7. 验收标准
| # | 验收项 | 判据 |
|---|---|---|
| V1 | 本机可用 | http://127.0.0.1:3080 门户正常 |
| V2 | 实例可用 | 能打开实例、能正常对话、能改工作区文件 |
| V3 | 断网可用 | 断开公网(保留模型 API 通路)后,登录/实例/改文件全部正常 |
| V4 | 一键启动 | 双击快捷方式即可启动,无需命令行 |
| V5 | 数据可恢复 | 按备份流程恢复后,历史会话与工作区文件完整 |
| V6 | 无残留外部依赖 | 全程不装 Linux 环境、不装容器运行时 |
| V7 | 不对局域网暴露 | 从另一台机器访问本机端口不通(默认回环即满足) |
8. 风险
| # | 风险 | 等级 | 说明与应对 |
|---|---|---|---|
| R1 | 单实例无内存上限 | 🟡 中 | Windows 无 cgroup,失控实例可能拖慢整机;靠并发上限与人工干预约束 |
| R2 | 与现网形态不同 | 🟡 中 | 出问题不能直接对照现网排查,需另建排障知识 |
| R3 | 模型 API 需出网 | 🟡 中 | 需确认用户网络出网策略;必要时配代理 |
| R4 | Windows 上实例的沙箱强度低于 Linux | 🟡 中 | dsh 本体的 Windows 沙箱官方标注为"部分强制";单机自用可接受,需知悉 |
✅ 相对 v2 消失的风险:用户之间互读文件、无 HTTPS、局域网暴露。
9. 回滚
| 层 | 回滚 |
|---|---|
| C1(代码) | 按平台分支判断,Linux 侧行为不变 ⇒ 现网零影响 |
| 安装 | 卸载脚本 + 删除数据目录(安装时记录路径) |
| 整体 | 客户端化完全不接触现网(47/106 形态不动) |
10. 附录 A · 证据与复核命令
| 结论 | 复核命令 / 来源 |
|---|---|
| 默认监听回环 | grep -n "DEFAULT_HOST" src/config.ts |
| 启动实例不带 shell | sed -n '663,665p' src/supervisor/orchestrator.ts |
| chown 块在 Windows 自动跳过 | sed -n '40,47p' src/fs/local-user-fs.ts |
| seed 的 chown 无条件调用 | sed -n '502,505p' src/supervisor/orchestrator.ts |
| portGuard 默认关 | grep -n "DSHS_PORT_GUARD" src/config.ts |
| Windows spawn 行为 | §4.1 四行实测(本机 Windows + Node 22.22.2) |
| 无域名时有降级路径 | sed -n '58,62p' src/web/routes/dsh.ts |
| dsh 本体支持 Windows | 官方 .agents/notes/** 三篇 |
11. 附录 B · 保留但不启用(给以后接手的人)
这些机制在客户端形态下存在但不使用,不属于缺陷、不需要清理:
| 机制 | 客户端下的状态 | 事实备注 |
|---|---|---|
| 多租户 / 多账号 | 存在,只建一个账号 | — |
| 子域分流 | 不启用(baseDomain 留空) |
平台内置了降级路径:baseDomain 为空时入口自动回落到子路径 /u/<id>/dsh/(src/web/routes/dsh.ts:58-62)。即"没有域名跑不起来"在代码层面已有兜底;客户端单人场景不需要依赖它 |
租户隔离(account 档位) |
不启用(默认 soft) |
— |
| 端口守卫(nft) | 不启用(默认关) | 仅在 Linux+root 下可用 |
| 集群(Manager/Worker) | 不启用(local 模式) |
— |
| 开机自启 | 可选 | — |
12. 附录 C · 被放弃的选项(记录决策依据)
| 曾考虑 | 放弃原因 |
|---|---|
| 内网多用户形态(v2) | 用户明确「考虑用户自己使用就可以了,不用考虑当作服务器 其他人访问」 |
| 官方桌面版(Electron)作为载体 | dsh桌面客户端_开发方案_20260916.md 用「保留官方壳、换内核为我们的平台进程」规避了这条 —— 门户能力不丢。本行仅作历史记录 |
| Linux 宿主 / WSL2 / 容器 | 需额外环境;单机自用无必要 |
| 域名 + 证书形态 | 客户端无域名,且单人自用不需要 |
若范围再次变化(例如要回到内网多人),v2 的对比表与结论仍然有效,可在本文基础上恢复。
13. 附录 D · 官方桌面客户端:能否沿用(调研结论)
问题:能否沿用官方 Electron 桌面版,把这个项目装进去?
结论:❌ 直接装不进去(三条硬理由);✅ 但它的形态与打包链可以沿用。
13.1 官方桌面版是什么
| 项 | 事实 |
|---|---|
| 组成 | apps/desktop(Electron 壳)+ apps/desktop-host(私有 Node host 进程)+ resources/dsh(内置运行时) |
| 网络 | 不开监听端口;用分帧字节管道承载 Fetch 请求与流式响应 |
| profile | Electron 独占 $DSH_HOME/profiles/desktop;CLI 不能启动或修改它 |
| 插件 | profile 的 dependencies 只放外部插件;有独立插件管理窗口(增删改查) |
| 数据 | 与 CLI 共享 $DSH_HOME 下的 会话 / 设置 / 凭据 / 工作区 / 存储 |
13.2 装不进去的三条硬理由
- 载体只接受 dsh 插件,我们的平台不是插件。 平台是独立的 HTTP 服务(路由 + 数据库 + 子进程编排),没有"作为 profile 插件被加载"的形态。
- 桌面版没有 web server,而我们的插件是平台的前端。 官方原文:「Desktop does not provide a
webServer」,官方自己的「Open In...」插件就因此被禁用。我们的business-plugins数据全部来自平台 API(/api/plugins/mine、/api/dsh/status、/api/skills/mine、/api/me/keys…,见poc/business-plugins/lib/client.js)—— 装进去就是空壳。 - 两套编排者会争同一份
$DSH_HOME。 桌面版自己就在跑 dsh;平台还要为每个用户再起 dsh 实例。
13.3 可以沿用的部分
| 可沿用 | 说明 |
|---|---|
| 形态 | Electron 桌面应用:双击打开、不暴露端口 |
| 打包链 | 官方 apps/desktop/scripts/ 的 electron-builder + NSIS + Windows 签名 + 冒烟脚本,可作参照 |
| 数据互通 | 官方桌面版与 CLI 共享 $DSH_HOME 的会话/设置/凭据 —— 我们的壳若指向同一 $DSH_HOME,用户数据可互通 |
做法:自建一个薄 Electron 壳(不复用官方壳代码)——
主进程启动平台 → 等 127.0.0.1:3080 就绪 → 窗口加载它 → 退出时收干净子进程。
用户看到的是桌面应用,里面是机制完整保留的平台。
13.4 三档形态与投入
| 档 | 形态 | 投入 | 体验 |
|---|---|---|---|
| 1 | 快捷方式 + 浏览器(v3 现有 C3) | 最小 | 中 |
| 2 | 薄 Electron 壳 + 借用官方打包链 | 中(壳小,难在跑通打包链) | 好 |
| 3 | Fork 官方桌面版源码改造 | 大 | 最好但最脆 |
⛔ 不建议第 3 档:官方桌面版的整个设计(不开端口、独占 profile、只装插件)与"承载一个平台服务"方向相反,改造量大于重写。