Files
dsh_ai1net_server/docs/客户端与桌面/可行性评估_客户端安装与覆盖网络互联_20260916.md
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复:
- 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录)
- tmp/(32.4 M,按接续棒命名的过程临时区)
- .workbuddy/tmp/(39.5 M)
- 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物)
- tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留

入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与
接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、
.workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。

排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、
打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

157 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 可行性评估:服务部署 + 客户端安装(客户端壳套服务器项目)+ 覆盖网络互联
> 日期:2026-09-16 | 状态:**调研稿**(只读取证完成,未改任何代码/服务器)
> 触发:用户问「当前项目是否可以改造为支持服务部署以及客户端安装(客户端壳套服务器项目),让他们通过覆盖网络互联」
> ⚠️ **本稿落点说明**:文档库(`dsh-server-docs/`)当时被**全局执行锁**占用(持有者 `guest迁移-2307`,09-15 23:08 起)⇒ 按纪律**未写入文档库**,暂落工作区根。锁释放后应转为 `04-调整方案/<NN>-*.md` 正式档案并登记。
---
## TL;DR
**可行,而且现有架构已经给出约 80% 的形状** —— 因为「一台机器 = 一个可被平台拨入的执行体」这件事,T08 集群化已经定义完了,并且**已经有两条现成能力**正好解决"没有公网 IP":
① `soft` 隔离模式下实例**只是一个普通子进程**(不需要 root / bwrap / systemd);
② Worker 用**拨出式 SSH 反向隧道**接入 Manager —— **节点本来就不需要公网 IP**。
真正的缺口是 4 条:**客户端运行时落点**、**节点身份(现为共享令牌)**、**信任模型反转**、**分发与版本矩阵**。
---
## 一、现成资产(可直接复用 · 均带代码坐标,已核实)
| # | 资产 | 证据 | 为什么关键 |
|---|---|---|---|
| 1 | **`soft` 隔离模式**:实例 = 裸子进程 | `src/supervisor/orchestrator.ts:663` —— `isolationMode !== 'account'` 时直接 `spawn(command, args)`,**无 bwrap / 无 setpriv / 无 systemd-run / 不需要 root**;`src/config.ts:14,180`(`'soft'|'account'`,**默认 `soft`**) | 客户端侧不需要任何特权即可承载实例;Linux 隔离层(bwrap·scope·uid)是**可选增强**,不是前置依赖 |
| 2 | **Worker agent 协议**(≈ 现成的"节点契约") | `src/worker/agent.ts` 头部 4 条纪律:**单向拨入**(不反向连 Manager、不写控制面)· **幂等键 `operationId`** · **最小接口白名单** · **self-fencing(epoch 落后即自停)**;端点:`/healthz` `/launch` `/stop` `/status` `/endpoint` `/fence` `/watchdog` `/fs/*` | 「个人电脑作为节点」所需的核心语义**已经定义并跑通过**(S0–S7 全绿 + 真跨机演练) |
| 3 | **拨出式反向隧道**(解决无公网 IP) | `src/worker/tunnel.ts:152` —— `ssh -O forward -R <port>:127.0.0.1:<port>`;含 20 s 本地定时器自愈(T08 §16.5 ②) | 节点**主动拨出**即可被平台访问,**无需公网 IP、无需端口映射** —— 这正是覆盖网络"中继支"的等价物 |
| 4 | **归属/租约分层判据** | 集群化设计 §1.3:**归属与租约只有 Manager 能写**;Worker 自有本地库 | 客户端天生**只能是数据面**,不会出现"客户端写权威状态"的脑裂 —— 与"个人电脑不可信"天然相容 |
| 5 | **Spawner 接口已抽象** | `src/supervisor/spawner.ts`(`launch / stop / list`)+ `local-*` / `remote-*` 两个实现 | 新增一种"客户端节点"= **多一个 Spawner 实现**,不是改内核 |
| 6 | **平台无跨平台分支** | 全仓仅 `src/supervisor/firewall.ts:42` 一处 `process.platform === 'linux'`(且仅用于 nft 联网护栏) | 除隔离与防火墙外,**代码本身跨平台** |
**结论**:能移植的不是"平台",而是**实例运行时 + 节点代理**这两块;管理面(门户/DB/归属/租约)留在服务器不动。
---
## 二、缺口(4 条,按必须先解决排序)
### 缺口 1 · 客户端运行时落点(技术,自决范围)
- Linux 侧原语分布(已清点):`bwrap`(`orchestrator.ts` / `spawner.ts` / `agent.ts` / `cli.ts` / `routes/admin.ts`)· `systemd-run`(`cli.ts` / `orchestrator.ts`)· `setpriv`(`cli.ts` / `config.ts` / `orchestrator.ts` / `routes/business-plugins.ts`)· `nft`(`cli.ts` / `config.ts` / `orchestrator.ts` / `agent.ts`)· 绝对路径 `/var/lib/dshs` `/opt/dshs` `/usr/local/dsh-runtime`。
- ⇒ 客户端侧需要一套**平台无关的落点**:Node + `dsh` 可执行 + profile + 工作区目录(Windows 用 `%LOCALAPPDATA%`、macOS 用 `~/Library/Application Support`)。
- ⚠️ **未验证(须实测)**:`dsh` 自身在 Windows / macOS 上的**沙箱后端行为**。Linux 侧注释显示 dsh 有"沙箱后端探针",探针失败会 `refusing to run the command unconfined`(实例内 bash 被整体拒绝)—— Windows / macOS 上走哪条后端、是否可用,**尚无证据**。
(验收口径 L2 起:客户端上起一个实例 → `curl` 实例页 200 + 实例内跑一次 bash 工具。)
### 缺口 2 · 节点身份(现在是共享令牌,公网上不成立)
- 现状自陈(`agent.ts` 头部):token 是**共享密钥 + bearer 式比较**,"仅内网 + nft 白名单";HMAC / 防重放**留待后续**。
- 个人电脑在公网 ⇒ 共享令牌**无法吊销单个节点**、无法抗重放、一台泄露等于放行全部。
- ⇒ 必须升级为**每节点独立身份**(客户端证书 / 独立密钥对)+ 准入与吊销,且**通道本身要端到端加密**。
### 缺口 3 · 信任模型反转(最重要的一条,不是技术问题而是边界问题)
- 现平台假设:**宿主机由我们掌控**(所以用 uid 隔离"用户 A 防用户 B")。
- 客户端形态下:**宿主机由用户掌控** ⇒ 原有隔离语义在"防机器主人"这一维上**直接失效**(机器主人本来就能读自己机器上的一切)。
- ⇒ 必须新增硬约束:**一台客户端上的实例只能属于该客户端的主人**;平台**不得**把别人的实例、别人的数据、或平台级凭据下发到客户端。
- ⚠️ 现状反例:平台会把**共享模型密钥**注入实例(档案 85/87 的两层密钥中的"共享"那一层)—— 下发到用户掌控的机器等于**把平台密钥交给用户**。
- ⇒ 客户端形态**必须只用"用户自己的密钥"这一层**;共享层只在服务端实例上使用。
### 缺口 4 · 分发与版本矩阵(运维)
- 客户端安装包 + 自动升级 + **客户端版本 × 平台版本兼容矩阵**(现有 `plugin-compat` / `dsh-install.ts` 是同类问题的服务端解,需要客户端版对应物)。
- 客户端一多,**故障面从"我们的机器"扩到"用户的机器"**(系统时间、磁盘、杀软、代理),排障成本上升。
---
## 三、形态三档(**真取舍,需用户拍板**)
> 判据:三档的**最终效果**对用户完全不同(数据在哪、算力归谁、谁能调度),属"业务目标与优先级",不能由技术标准裁决。
**A 档 · 客户端只做"壳"(远程访问)**
- 优点:改动最小;安全模型**零变化**;不引入任何跨租户风险;沿用现有实例与全部隔离。
- 缺点:个人电脑**不成为节点**,算力仍在服务器;离线不可用;覆盖网络在这一档**没有实际用途**。
**B 档 · 客户端自带"自己的实例"(平台退化为控制面 + 中继 + 目录)**
- 优点:数据与算力都在用户自己机器上,**服务器成本大降**;离线可用;能用到本机文件与本地软件;不引入跨租户风险(每位用户只碰自己的机器)。
- 缺点:`soft` 模式隔离强度**低于**现在的 `account` 模式(本机场景可接受,但需明确边界);Windows / macOS 运行时**未验证**;平台对实例的可控性下降(升级、清理、取证都要客户端配合)。
**C 档 · 客户端作为集群 Worker(平台可把实例调度到用户机器)**
- 优点:算力可横向扩张且复用**已跑通的集群协议**(agent 四纪律 + 归属/租约分层已就位);对平台运营方收益最直接。
- 缺点:**信任模型反转**(别人的代码、别人的数据、平台凭据落到用户掌控的机器上);必须有节点准入 + 吊销 + 强身份;**合规与责任边界要重新界定**;Windows / macOS 的隔离层基本**要从零做**(无 bwrap / 无 systemd / 无 uid 隔离)。
**我的倾向:先做 B**(唯一一档能"增量落地 + 不引入跨租户风险",且正好吃满资产 1/3/4 的现成能力)。A 档价值有限(覆盖网络用不上);C 档应等服务端能力与合规边界都清楚后再谈。
---
## 四、若走 B 档:落地顺序(每步可单独回滚)
1. **验证 dsh 在 Windows / macOS 的可运行性**(只读 + 本地装,不动服务器)——产出:能否起实例 + 实例内 bash 工具是否可用(L2/L4)。
2. **客户端节点代理**(复用 agent 协议,落 `soft` 模式):先只做 `/healthz` + `/launch` + `/stop` + `/fence`,台账式幂等。
3. **通道**:先用现有**拨出式 SSH 反向隧道**打通(零新依赖,当天可验),再评估换"打洞优先 + 中继兜底"。
4. **身份**:每节点独立凭据 + 吊销(替换共享 bearer)。
5. **强约束落码**:实例所属 == 客户端主人;共享密钥不下发客户端(**红线级**,须有断言/测试钉住)。
6. 分发与自动升级。
**回滚**:全程只新增(新客户端形态 + 新 Spawner 实现),**服务端现有路径不动** ⇒ 删客户端即回到现状。
---
## 五、未验证 / 待确认(诚实清单,勿当已知)
| # | 项 | 状态 |
|---|---|---|
| 1 | `dsh` 在 Windows / macOS 起实例 + 实例内 bash 是否可用 | ⚠️ **未验证**(本轮无对应环境) |
| 2 | `soft` 模式下实例的实际隔离强度(能读到宿主什么) | ⚠️ **未验证**(本轮只读了代码分支,未实测) |
| 3 | 客户端形态下"共享密钥不下发"是否已有现成开关 | ⚠️ **待查**(档案 85/87 是两层密钥,需确认能否按形态裁剪) |
| 4 | 打洞优先方案与现有隧道的替换代价 | 📋 方案调研(见同日讨论结论:自托管 NetBird / headscale + 自建中继) |
| 5 | 客户端 × 平台版本兼容矩阵的判据 | 📋 待设计(可仿 `plugin-compat`) |
---
## 附:本轮取证命令(可复跑)
```bash
cd /d/github/dsh_shenxian
sed -n '660,665p' src/supervisor/orchestrator.ts # soft 模式 = 裸 spawn
grep -n "isolationMode" src/config.ts # 'soft'|'account',默认 soft
sed -n '1,30p' src/worker/agent.ts # 节点四条协议纪律
sed -n '147,155p' src/worker/tunnel.ts # -R 反向转发(拨出式)
grep -rn "process.platform" src # 全仓仅 1 处(firewall.ts)
```
---
## 六、与另两份客户端文档对账(2026-09-16 08:5x 追加)
> 对账对象(同为大工作区根,**均晚于本稿 8 小时**,08:06 产出):
> `dsh客户端化部署方案_20260916.md`(v3 · 单机自用 · 自称「客户端化」的单一来源)
> `dsh桌面客户端_开发方案_20260916.md`(v1 · 基于官方 Electron 壳 · 独立仓库)
### 6.1 结论:**不冲突,是互补;但两处需要收口**
| 维度 | 本稿 | 部署方案 v3 | 桌面客户端 v1 | 判定 |
|---|---|---|---|---|
| 形态选择 | 三档 A/B/C,倾向 **B** | **单机自用**(用户原话已定,§12) | 单机自用为默认,域名可切多人 | ✅ **同向**(本稿的 B 即其单机自用;三档问题已被用户拍板,见 6.2) |
| 地基 | 资产 1:`soft` 默认、无需 root | 明确"隔离档保持默认 soft" | 同 | ✅ 互相印证 |
| 平台改动面 | 资产 6:仅 firewall 一处平台分支 | "唯一硬阻塞 = spawn 方式" | P1 同一条 | ✅ 不同维度,可并存 |
| 分发与版本 | 缺口 4 | — | §3.4 上游同步 / §4.6 自动更新 / §6 打包 | ✅ 已被其覆盖 |
### 6.2 本稿需修正的两点(**如实记录,勿当原判**)
1. **缺口 1 的位置标错了**:本稿把"客户端运行时"列为缺口、并怀疑 `dsh` 在 Windows 的**沙箱后端**不可用。部署方案 v3 **已在本机 Windows 实测**:真正的硬阻塞是**平台自己的启动方式** —— 裸名 `spawn('npm')` 在 Windows 报 `ENOENT`(全局包是 `.cmd`),显式 `.cmd` 被 Node 拒(`EINVAL`),改用 `cmd.exe /c` 或 `shell: true` 即通。⇒ **阻塞点在平台调用层,不在 dsh 运行时**,且**代价很小**(一处调用方式)。
2. **三档"需用户拍板"已不再是开放问题**:用户此前已定「考虑用户自己使用就可以了,不用考虑当作服务器 其他人访问」⇒ **A/C 两档的多人维度已被放弃**。本稿第五节的三档保留为**历史决策依据**,不再是待拍板项。
### 6.3 仅本稿覆盖、另两份未涉的:**覆盖网络**(用户最初问题里的那一项)
- 两份文档的"多人形态"路线 = **内网 DNS 泛解析 + 域名**(局域网内多机访问),与"覆盖网络"是**同一需求的两种替代实现**:
- 局域网 + 域名:只能在同一内网;跨公网不行;需要域名与 DNS 泛解析。
- 覆盖网络:跨公网可直连(打洞优先、中继兜底);不需要域名;但需要客户端常驻 + 节点身份。
- ⇒ **本稿与另两份文档在"能力面"上不冲突,但在"要不要跨公网互连"上是分叉**;两份文档默认不跨公网。若确定不做跨公网,本稿的缺口 2/3 可整体降级为"不适用"。
- 📄 **百台规模的逻辑预演见 `覆盖网络_百台规模推演_20260916.md`**(2026-09-16):结论 = 100 台规模下**控制面不是瓶颈,中继带宽与"大流量过网"才是**(实例首屏含 ≈10.8 MB 客户端脚本);而"单机自用"天然把大流量留在回环上 ⇒ 该形态在 100 台规模下是**正确版而非妥协版**。
### 6.4 两处需收口的隐患(**建议进文档库时一并处理**)
1. **双源声明**:部署方案 v3 自称「'客户端化'的**单一来源**」,而桌面客户端 v1 与其**同日并存**且内容不同层(平台侧 vs 交付载体层)⇒ 属项目明令禁止的"第二个漂移源"风险。建议:**v3 定平台侧、v1 定交付层,v3 里补一行指针指向 v1**,或合并。
2. **判定句互相矛盾**:v3 §12 附录 C 把「官方桌面版(Electron)作为载体」列为**放弃项**(理由:换载体会丢掉门户能力),而 v1 **恰恰选了这条路**。v1 的"保留壳、换内核为我们的平台"已实际**消解**该理由(门户能力并未丢),但 **v3 的判定句未更新** ⇒ 并列阅读时会读成"同一件事既放弃又采纳"。建议在 v3 §12 该行加注"已被 v1 以'换内核'方式规避,判定失效"。
### 6.5 本稿的**缺口 3 仍然有效,且是"切多人形态时的前置门禁"**
- 部署方案 v3 §1.3 明确写:切多人形态**零代码**(只配域名),且**多人时隔离档仍是 `soft` ⇒ 用户之间无隔离**;桌面客户端 v1 §4.7 进一步要求"**客户端 spawn 平台时不要清理环境变量、不要白名单化**"(为透传域名)。
- 这两条与本稿缺口 3 的硬约束**正面相交**:
- ① **实例只能属于机器主人** —— 单机自用下天然成立;一旦配域名多人用,**立刻不成立**(soft 档无隔离)。
- ② **平台级共享模型密钥不得随客户端分发** —— 与"环境变量原样继承"相冲。**建议改为白名单透传**,只放形态开关三项(`DSHS_BASE_DOMAIN` / `DSHS_COOKIE_DOMAIN` / `DSHS_SECURE_COOKIES`),模型密钥只走"用户自己的密钥"那一层。
- ⇒ 结论:**不是"不兼容",而是"范围收窄把风险面暂时关掉了、但开关就在配置里"**。建议把上述两条写成**切多人形态的前置检查项**(而非留在散文里)。