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)、记忆修复前备份。
This commit is contained in:
admin committed 2026-09-24 07:51:03 +08:00
commit ce8e6ceed9
396 files changed
+66045

No files matched your search

@@ -0,0 +1,156 @@
# 可行性评估:服务部署 + 客户端安装(客户端壳套服务器项目)+ 覆盖网络互联
> 日期: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`),模型密钥只走"用户自己的密钥"那一层。
- ⇒ 结论:**不是"不兼容",而是"范围收窄把风险面暂时关掉了、但开关就在配置里"**。建议把上述两条写成**切多人形态的前置检查项**(而非留在散文里)。