Files
dsh_ai1net_server/交付物/平台新增功能总盘点与插件化归属-20260926.md
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

385 lines
33 KiB
Markdown
Raw Permalink 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.
# 平台新增功能总盘点 × 插件化归属("基础服务插件"能不能做、能装哪些)
> **回答的问题**(用户原话):「把现在开发的平台基于 DSH 主体新增的所有功能全部列出来,看一看哪些能整合到插件中,相当于是基础服务的插件。基于这个插件可以去使用现有新增能力。」
> **本件口径**:先给一张**能自己判断的口诀**(§1),再给**完整盘点表**(§3–§8),最后给「基础服务插件应该长什么样」(§9)与**不可进清单**(§10)。
> ⚠️ 盘点来源 = 平台自己的**路由与目录清单**(`src/web/routes/` 15 个路由文件 + `src/` 各目录)+ 本工作区在册记录;**未逐行复核每个功能**(见 §12)。
---
## 1 一句话判据(为什么"不能"—— 用这个自己判断,比记结论有用)
> **判据:这件事发生在「插件存在之前」还是「之后」?**
> **之前** ⇒ 必须在**引导层**(不可插件化);**之后** ⇒ 应该尽量放进**插件**。
「插件」是**被装进去**的东西 —— 而"把它装进去"这个动作,需要一个**已经在跑的宿主**。所以固定有**四件**事发生在它之前(§10)。除此之外**几乎都能进插件**。
**两层心智模型**(这是此前没讲清的地方):
| 层 | 是什么 | 谁写的 | 与 DSH 主程序的关系 |
|---|---|---|---|
| **L0 官方 DSH** | 进程、窗口与界面框架、插件机制与扩展点、会话与工具 | 官方 | — |
| **L1 平台自有服务** | 登录/门户/多租户编排/插件投放/数据面/协作内核/网络中继 | **我们自己** | ⭐ **本来就不依赖 DSH**(官方没有账号体系,这些都是独立进程) |
| **L2 实例内 / 客户端内** | 界面扩展、平台能力的取用(读身份态、调平台接口、数据面、协作、启停插件) | **我们自己** | 寄生于官方的扩展点 |
⇒ **用户想要的"基础服务插件" = 把 L2 收成一个包**;**L1 从来不需要"插件化"**(它已经独立存在,客户端只是调用方);**L0 的四件固定事拿不走**。
---
## 2 判定图例
| 标记 | 含义 |
|---|---|
| ✅ | **能进基础服务插件**(客户端侧可承载:界面 + 调用) |
| ⚠️ | **半能**:界面能进,但**动作**属引导层/服务端;或它本就是服务器页面 |
| ❌ | **不能进插件**(宿主四件 + 服务器侧服务/运维面) |
---
## 3 官方 DSH 提供的基线(列出仅作对照,⛔ 不动)
| 功能组 | 官方基线 | 现在在哪 | 能进插件? | 说明 |
|---|---|---|---|---|
| 实例宿主(进程/会话/工具/模型调用) | 有 | L0 | ❌ | 宿主本体 |
| 窗口与界面框架 + 插件机制与扩展点 | 有 | L0 | ❌ | 插件机制**不能在插件里** |
| 技能加载机制 | 有 | L0 | ❌ | 平台新增的是"分层与投放管理"(§5) |
| 本地插件安装命令 | 有(本地) | L0 | ❌ | 平台新增的是"远程投放与分发"(§4) |
---
## 4 平台新增 · 插件体系与分发(`business-plugins.ts` · `plugin-data.ts` · 本会话的版本机制)
| 功能 | 官方有? | 现在在哪 | 能进插件? | 依据 / 卡点 |
|---|---|---|---|---|
| 候选池 → 投放 → 共享包库 | 无 | L1 服务 | ✅ 管理界面可进 | 客户端只做"看得到、点得动" |
| **把包拉下来并装进实例** | 无 | **引导层** | ❌ | "装"本身不能由**被装的那个包**做(先有鸡还是先有蛋) |
| 插件启停 / 装配 | 部分(本地) | 装配在引导层 · 台账在 L1 | ⚠️ | 启停**界面**可进;**装配动作**在引导层 |
| 插件数据面(一插件一库) | 无 | L1 网关 + 插件侧 SDK | ✅ | **本来就是给插件用的** ⇒ 天然属于基础服务插件 |
| 版本归档 / 通道 / 定向钉版 / 全局矩阵(本会话新落) | 无 | L1 服务 | ✅ | 矩阵与钉版**管理界面**可进 |
| ↑ 同上的**拉取器** | 无 | **引导层** | ❌ | 与"装插件"同源 |
---
## 5 平台新增 · 身份与账号(`auth.ts` · `admin.ts` · `admin-user-ops.ts` · `whitelist.ts`)
| 功能 | 官方有? | 现在在哪 | 能进插件? | 依据 / 卡点 |
|---|---|---|---|---|
| 注册 / 登录 / 会话凭据 | 无 | L1 服务 | ⚠️ | **登录动作**必须发生在"插件存在之前"(引导层);**身份态展示 / 账号设置**可进插件 |
| 角色与用户管理 | 无 | L1 服务 | ✅ | 管理界面 + 调接口 |
| 准入白名单 | 无 | L1 服务 | ✅ | 同上 |
---
## 6 平台新增 · 多租户与实例(`src/supervisor/` 12 文件 · `isolation.ts` · `src/fs/`)
| 功能 | 官方有? | 现在在哪 | 能进插件? | 依据 / 卡点 |
|---|---|---|---|---|
| 实例编排 / 守护 / 自愈 / 配额 | 无 | **服务器侧服务** | ❌ 概念上不适用 | 客户端是**调用方**;插件里只做**入口与展示** |
| 沙箱隔离 | 无 | 服务器侧服务 | ❌ 同上 | — |
| 用户文件系统(按用户隔离读写) | 无 | 服务器侧服务 | ⚠️ 界面可 | 动作走服务接口 |
> ⚠️ **本组是此前"为什么不能"的主要误会来源**:这些**不是客户端的客户端功能**,而是**服务器提供的能力**。插件在这里的角色是**入口**,不是**实现**。
---
## 7 平台新增 · 网络与协作(`src/net/` · `overlay*.ts` · `src/im/` 12 文件)
| 功能 | 官方有? | 现在在哪 | 能进插件? | 依据 / 卡点 |
|---|---|---|---|---|
| 入网拨号 / 中继 / 寻址 / 组密钥 / 打洞 | 无 | **引导层(原生守护)** | ❌ | 拨号必须发生在界面与插件**之前**(桌面线已有定论:网络载体是新写的原生守护) |
| 设备授权(换设备凭据) | 无 | L1 服务 + 引导层 | ⚠️ | **授权动作**在引导层(要已登录);**状态展示**可进插件 |
| 节点 / 设备矩阵(谁在线、哪个版本) | 无 | L1 服务 | ✅ | 展示 |
| 协作内核(房间 / 消息 / 发言规则) | 无 | **L1 进程** | ❌ | 客户端侧是**插件**,不是内核 |
| 插件 SDK / 扩展点 / 回调桥 | 无 | **插件侧** | ✅ | **本身就是给插件用的** ⇒ 天然属于基础服务插件 |
| 会话与工作区分组 | 部分 | L1 服务 | ✅ | 列表 / 切换界面可进;存储在服务 |
---
## 8 平台新增 · 能力资产、界面与运维(`skills.ts` · `dsh.ts` · `domain.ts` · `src/nginx/`)
| 功能 | 官方有? | 现在在哪 | 能进插件? | 依据 / 卡点 |
|---|---|---|---|---|
| 技能分层与统一投放 | 机制有 | L1 服务 + 实例内加载 | ✅ | 管理界面可进 |
| 共享模型逐用户授权 | 无 | L1 服务 | ✅ | 展示 / 申请入口可进 |
| 用户自配模型凭据 | 部分 | L1 保管 + 实例内落盘 | ⚠️ | 写凭据在实例内(插件可做),**保管**在服务 |
| 门户六页 / 登录页 | 无 | **L1 服务器页面**(我们的页面) | ⚠️ **不建议搬** | ⛔ 搬进插件 ⇒ 与"与服务器版本一模一样"冲突、出现**两份 UI 漂移**(详见 §11) |
| 工作台 UI / 窄档响应式 | 部分 | 插件 / 适配层 | ✅ | 插件本来就是干这个的 |
| 审计 / 日志 / 健康 | 无 | L1 服务 | ✅ | 展示 |
| 部署 / 回滚 / 备份 / 域名 / 证书 | 无 | 运维面 | ❌ | 不在客户端 |
---
## 9 「基础服务插件」应该长什么样
**一个包,两个半**(形态与既有先例同源:本地代理包就是"一个自研包两个入口"):
| 半 | 装什么 |
|---|---|
| **服务端半** | 平台能力的**统一入口**:身份态、可用插件与启停、数据面、协作、技能清单、模型授权态、网络与设备状态、更新检查 |
| **界面半** | **全部落在官方已有槽位**(设置 → 插件 / 能力管理 …),各类各一页;⛔ 不新造页面体系 |
⇒ 用户那句「**基于这个插件可以去使用现有新增能力**」—— ✅ **成立**。而且这正是把这些能力**统一出口**的正确做法。
---
## 10 ⛔ 不可进清单(只有 4 件,全在引导层)
1. **起实例**(把官方宿主进程跑起来)
2. **起窗口 / 渲染首屏**
3. **取身份**(登录 + 设备授权 —— 必须早于插件)
4. **把包拉下来并装进去**(含它的版本管理与拉取器)
> **+ 服务器侧服务**(多租户编排、隔离、协作内核、中继、门户、运维)—— 它们**不是"不能进插件",而是"本来就不依赖 DSH"**(官方没有账号体系 ⇒ 这部分从第一天就是我们的独立进程)。⛔ 把它们算作"插件的候选"是范畴错误。
---
## 11 一处**我不建议**,但可推翻
**门户六页(含登录页)不要搬进插件。** 理由:桌面线已定的口径是「**套壳 + 同步服务器代码**」「**UI 与服务器版本一模一样**」—— 门户是**服务器页面**,浏览器里就是它。若在插件里**再实现一份**,就会出现**两份 UI**,此后每次改页面都要改两处、必然漂移(属负向迭代)。⇒ 建议:门户**保持服务器页面**,插件只做**入口与内嵌**。
---
## 12 收益预期与不确定项(照实说)
**收益预期**:把 §4–§8 标 ✅ 的收进一个基础服务插件 ⇒ **客户端里"能改的功能"约九成都随插件更新**,不必重发安装包;剩下的引导层(§10 四件)建议**冻结到最薄**,因为**它一变就要重发安装包**。
**不确定项(⛔ 不假装确定)**:
1. 本盘点由**路由与目录清单 + 在册记录归纳**而成,**未逐行复核每个功能**;若某行要**驱动开工**,先补该行的最小取证(具名到文件与行号)。
2. 表格里 ⚠️ 的拆分("界面可进 / 动作不可进")是**按判据推演**的,仍需在实施时逐条落到扩展点上验证。
3. 「约九成」是**按功能组计数**的粗估,不是行级精算。
---
## 12.5 追问链结论速览(§13–§15 四问四答 · 可直接拿去决策)
> 本节只是 §13–§15 的**索引与判定**,⛔ 不复述论证;每条都指向原文小节。
| 你问的 | 判定 | 关键边界 | 详见 |
|---|---|---|---|
| 只装官方 DSH + 加我们的插件,能不能拿到 L1 与 L2? | ⚠️ **部分可行** | L2 全部 ✅;L1 **只用不搬** | §13 |
| 登录/身份能不能做进插件? | ✅ 能做 | 包「登录后才拿得到」⇒ 身份成为**信任链起点** | §13.2 |
| 装完后用户**自选角色**:当节点提供服务 / 只当用户设备? | ⚠️ **分三档** | 用户设备 ✅ · 轻节点 ✅ · **重节点 ❌** | §14 |
| **自选接入哪个节点**(106 接入 47,接入后可用其功能)? | ⚠️ **只有"无状态归属"能切** | A 内容 ✅ · B 网络 ✅ · **C 承载 ⚠️ 只能迁移** | §15 |
| **「当节点」到底能不能选?为什么不行?** | ⚠️ **只能选一半** | 当 **Worker** ✅ 自选角色 · 当 **Manager** ❌ **换控制面,不是角色** | §16 |
**四条硬约束(换任何形态都逃不掉)**:① 首装的包必须**匿名可取**(否则"拿到身份之前拉不到包"死循环)② 平台自有服务(L1)**只被使用、不搬进插件** ③ **系统级能力**(起实例 / 沙箱 / 网络编排)不可插件化 ④ **权威必须唯一** —— 判决者(谁托管谁、谁能进、谁是设备)**不能由被判决的一方自己选**(双写 = 脑裂 ⇒ 数据损坏)。
**🟡 唯一待拍板项**(仍等你定):「设备要不要获得**读取平台插件包**的权限」—— 它决定"轻节点当内容副本 / 设备当消费者"这条路走不走得通。
---
## 13 追问:只装官方 DSH + 加我们的插件,能不能拿到 L1 与 L2?(2026-09-26 08:2x)
**用户原话**:「用户只需要安装一个官方的 DSH 主程序,然后把我们的插件加进去就能实现 L1 和 L2 的功能,然后用户登录这个功能去使用相应的能力和模块。」
### 13.1 判定(逐条)
| 用户的三步 | 判定 |
|---|---|
| 只装**一个**官方 DSH 主程序 | ✅ 可行 —— **这正是平台节点用户的现有形态**(官方主程序 + 注入层 + 插件) |
| 把我们的插件加进去 | ✅ 可行 —— 官方本来就有插件安装命令与来源机制;我们要做的只是**提供一个可安装的包地址** |
| 就实现了 L1 与 L2 的功能 | ⚠️ **L2 全部 ✅**;**L1 是"使用面"✅、"实现面"❌ 不搬过来**(见 13.3-2) |
| 用户**登录**这个功能去使用相应能力 | ✅ 可行 —— **登录可以就做在这个插件里**(服务端半做登录界面 + 存凭据 + 校验) |
### 13.2 ⚠️ 更正我之前的三句话(诚实,具名)
| 我之前说 | 在**这个形态**下的更正 |
|---|---|
| 「登录必须在插件**之前**,做成插件会死锁」 | 🔴 **在"官方 DSH 当宿主"下不成立**:官方页面先渲染,插件在里面提供一个登录/身份区块 ⇒ **登录由插件提供完全可行**。原论证的前提是**桌面自研壳的既定形态**(壳要决定"进实例页还是登录页")—— **换宿主,前提就变了** |
| 「必须有薄壳 + 引导层」 | 🔴 **本形态下不需要自研壳**:**官方 DSH 自己就是引导层**。用户装官方程序 + 装插件,就够了 |
| 「拉取器必须在引导层,⛔ 不能进插件」 | 🔴 **收缩为"只在首装时成立"**:**第一次装上来**需要一个安装动作;**此后所有更新都可以由插件自己做**(插件拉新包 → 换装配 → 提示重启)⇒ **"插件更新 = 功能更新"在首装之后成立** —— 这正是用户要的效果 |
### 13.3 三条硬约束(不满足就不成立)
1. **首装包必须匿名可取**(不能要求先登录,否则才是真死锁)⇒ 需要一条公开的包分发渠道。⚠️ 因此我之前说的「未登录 = 既没功能也没代码」**只对后续增量包成立**,对首装包**不成立**。
2. **L1 只是"被使用",不会搬到用户机器上**:多租户编排、沙箱隔离、配额、中继**服务器**、协作内核这些是**服务器角色**,继续在平台侧跑;插件是**客户端**。⇒ 用户机器上不会有"一个小平台",只会有**一个通往平台的入口**。
3. **系统级能力仍不可插件化**:要在用户机器上跑"平台服务器那套"(容器/沙箱/多租户/端口)需要系统权限 ⇒ 插件给不了(且平台已定"不开端口")。⇒ 想"本地全自足"属**另一个产品形态**(单机/私有部署),不是这个插件。
### 13.4 这个形态的得失(诚实,供推翻)
**得**:⛔ 不再需要自研客户端 ⇒ **"客户端更新 = 插件更新" 100% 成立**;成本最低;用户只装官方程序。
**失**:① 界面受官方槽位限制(你看到的是官方界面 + 我们加进去的能力 —— 其实正合"与服务器版本一致"的口径);② 官方接口升级时插件要跟;③ 实例的**启动策略**(自启、离线兜底、窄档响应式)由官方决定,我们插不上手 —— 这正是桌面线当初自研壳换来的东西。
⇒ 判据:**若接受"用户看到的是官方 DSH 界面、能力由插件加进去"**,则此形态**是最优解**,而且**它已经是平台节点用户的现有形态**(把同一套插件发给单机用户即可)。
### 13.5 落地清单(补 5 件,逐项标状态)
| # | 事项 | 状态 |
|---|---|---|
| 1 | 把"基础服务插件"做成**一个可安装包**(服务端半 + 界面半) | 🔜 新做 |
| 2 | **公开可取的首装渠道**(匿名) | 🔜 新做(共享包库可复用,需加一条**匿名首装口**) |
| 3 | 插件内的**登录与身份态**(登录界面 + 存凭据 + 校验) | 🔜 新做(平台侧登录与设备授权接口**已具备**) |
| 4 | **入网拨号放进插件**(异步拉起拨号子进程,走 443 拨出) | 🔜 可做:**无需 root、无需新端口**;⚠️ 与桌面线"网络载体是原生守护"**不冲突**——那是桌面壳口径,此处宿主是官方 DSH |
| 5 | 插件**自更新**(拉新包 → 换装配 → 提示重启) | ✅ 机制已就绪(本会话)+ 落点从引导层**移进插件** |
---
## 14 追问:一个插件,用户装完后**自选角色** —— 「当节点提供服务」还是「只当用户设备接入」?
**用户原话**:「用户在 DSH 主程序上加入插件后,他可以选择去当**节点**提供服务,还是可以选择自己只是一个**用户设备**去接入。」
### 14.1 判定
⚠️ **"二选一的界面"可以做成插件;但"当节点"能不能成,不由勾选决定** —— 由**别人能不能连到你** + **平台放不放行** 决定。
| 角色 | 插件内能不能承载 | 卡点 |
|---|---|---|
| **用户设备(终端)** | ✅ **能,且已跑通** | 全程**只拨出**(走 443)⇒ 无需端口、无需 root;设备授权接口已在生产 |
| **轻节点**(提供中继 / 带宽 / **内容副本**) | ✅ 能(插件可异步拉起守护子进程) | ⚠️ **硬卡点 = 入向可达**:别人得能连到你(NAT / 防火墙);**同局域网零门槛**。且走**半自动升格**(候选池 → 平台放行),⛔ 不是本地开关 |
| **重节点**(替别人跑实例、承载他人数据) | ❌ **不能靠插件** | 需要**系统级隔离**(容器 / 沙箱 / 独立账号)+ 常驻系统服务 ⇒ 属**正式节点部署**(原生守护 / 系统服务),不是插件能兜的 |
### 14.2 三条判据(为什么"选择"≠"生效")
1. **物理判据优先于意愿**:当节点 = 提供服务 = **别人能连到你**。这是网络事实,插件只能**探测并上报**。平台既有口径:**升格判据 = 入向可达实测**。
2. **平台放行是第二道门**:节点提供的是**对外能力**(别人的流量 / 负载交给你)⇒ 必有治理:白名单(**默认拒绝** ⇒ 加节点是**平台侧配置动作**)、上限(升格真上限 8 条)、半自动升格。⇒ 插件侧能做的只有「**体检 → 上报候选 → 等放行**」。
3. **隔离是第三道门**:若节点要**承载他人实例**,必须有沙箱与独立账号(Linux 上是 bubblewrap 那一套,要 root 级部署)⇒ 在"用户只装了官方 DSH"的机器上**不具备**(Windows 上更没有)。
### 14.3 ⇒ 建议落法:**三步升格**(选择权在用户,生效权在"网络 + 平台")
| 步 | 谁做 | 内容 |
|---|---|---|
| ① 体检 | **插件**(本地) | 探测入向可达性 / 带宽 / 是否同局域网 + 报告本机能力 ⇒ 出一张「能不能当节点」的体检单 |
| ② 申请 | **插件** | 用户主动点「申请成为节点」⇒ 体检单交给平台 |
| ③ 放行与下发 | **平台** | 半自动升格:放行 → 下发节点配置 → 插件按配置切换角色 |
⭐ **一个额外好处**:单插件双角色 ⇒ 用户**装一次**、先当普通用户,条件成熟再升格 ⇒ 与既有「半自动升格」口径**天然吻合**,⛔ 不需要为节点另做一套客户端。
### 14.4 两个必须点名的代价
1. **对外承诺与责任**:当节点 = 你的机器承接**他人的流量 / 数据** ⇒ 这是**治理与责任问题**,不是技术选择 ⇒ 界面上必须写清"当节点意味着什么",且必须能**撤销**(撤回授权即失效 —— 与设备授权同源)。
2. **"看起来能开其实开不了"的风险**:若插件里直接放一个"当节点"的开关,而多数家用网络**根本不可达**,用户会以为平台坏了。⇒ **必须先体检、再显示**;⛔ **不给假的可用开关**(本平台反复踩过的一类事故:把"未知"显示成"已启用")。
### 14.5 与本会话已落地机制的衔接
- 节点当「**内容副本 / 分发源**」**不需要**系统级隔离 ⇒ 这是**插件可承载的节点形态**之一,且与本会话落地的**版本管理与拉取机制直接衔接**(节点本来就在拉包)。
- 设备当「**内容消费者**」⇒ 对接单已交付(AI1Net Desktop 工作区)。
- 默认形态 = **终端**(平台已定「先不开端口」);节点能力**按需开**,与覆盖网络线口径一致。
### 14.6 不确定项(⛔ 不假装确定)
1. 「轻节点」具体提供哪些服务(中继 / 带宽 / 副本 / 只是在线心跳)—— **属业务目标**,需你定方向后再设计;本件只判"能不能承载"。
2. 家用网络下的真实可达率**未取数**;体检单的判据与阈值需实测标定。
---
## 15 追问:**节点归属可选**场景(47 当节点,其他用户可选"成为节点"或"接入某节点"如 106 接入 47,接入后即可用该节点功能与其提供的三方插件)
**用户原话**:「47服务器就是一个节点它加入了这个插件后,就可以选择成为一个节点。其他的用户加入了这个插件后也可以选择成为节点,也可以选择接入某个服务(106接入47)。接入后就可以使用节点对应的功能和提供的三方插件。」
### 15.1 判定:**要把"接入"拆成三种**,否则会得出一半对一半错的结论
| 「接入」指什么 | 现状对应什么 | 用户能自选吗 |
|---|---|---|
| **A 内容归属**(从哪个节点拉插件与能力) | 节点侧的**拉取源地址** + 身份 + 对方放行 | ✅ **能,且已实测跑通**(106 接入 47:2 MB / 3,437 ms,就是本会话验的) |
| **B 网络归属**(属于哪个网络、走哪条中继) | 覆盖网络:**组密钥 + 中继候选 + 设备授权** | ✅ 能(同局域网**零门槛**;跨公网需对方**可达**) |
| **C 承载归属**(实例跑在哪台机器上) | 平台编排 + 沙箱 + **实例 home 与数据** | ⚠️ **不能"一键切"**:它是**有状态**的(数据/文件都在原机)⇒ 切换 = **迁移**;且**当承载体需系统级部署** |
### 15.2 一句话结论
> **"插件内选角色 + 选接入谁"这个形态可行** —— 因为平台本来就是**控制面联邦**(每个节点自带内容源与承载体),"接入谁"在协议上就是一个**可切换的归属**。
> **但可自由切换的只有"无状态归属"(A 内容 / B 网络);"实例落在哪台机器"(C 承载)是有状态归属,只能迁移、不能切开关。**
### 15.3 现状已经有的(⭐ 大部分构件已在生产上跑着,⛔ 别重造)
- **A 内容归属**:已上机(本会话的节点拉取机制;106 → 47 实测)。
- **B 网络归属**:已实测(登录 → 设备凭据 → 拨号入网;租约 24 h)。
- **准入治理**:中继/节点**白名单默认拒绝** ⇒ 加节点 = **平台侧配置动作**;**半自动升格 + 上限**。
- **归属判据三条**(⛔ 不许破):① 归属与租约**只有 Manager 能写**(双写 = 脑裂)② **跟用户走** ⇒ 实例 home ③ **本机运维** ⇒ Worker 本地库。
### 15.4 四条必须点名的
1. 🔴 **"节点"不等于"插件"**:节点 = **服务进程**(我们那套服务)+ 插件两端;插件是节点的**开关与入口**,⛔ 不是节点本身。⇒ 「47 加入插件后成为节点」的准确表述是:**47 侧本来就在跑服务,插件让它"可被选为接入点"**;只装插件不跑服务 ⇒ 它只是个客户端。
2. 🔴 **归属必须排他**:一个实例/设备只能属于一个承担体(否则脑裂/双写)⇒ "用户自选"要落在**一个明确归属**上;**切换 = 按迁移处理**(不是改个开关)。
3. 🔴 **信任链**:接入某节点 = 把"用谁的内容"交给它 ⇒ **包签名校验**从"要件"升为**必需**(否则"接入"就等于"允许对方往你机器里塞代码")。跨线议题,本线只登记。
4. ⚠️ **跨区可见性默认会被这条推翻**:现状倾向"各区**互不可见**";若允许用户自由接入任意节点 ⇒ 该默认必须重新明确(属覆盖网络线口径)。
### 15.5 建议的最小可行版本(三步,与上一轮的"三步升格"同形)
| 步 | 内容 |
|---|---|
| ① 插件内"接入点选择" | 列出可用节点 + **本地体检**(可达性/带宽)+ 用户点"申请接入" |
| ② 平台放行并下发归属 | 放行 → 下发**内容源归属(A)+ 网络凭据(B)** |
| ③ 插件按归属切换 | A / B 立即可用;**C(承载)留到有迁移方案再开**,⛔ 不先开"看起来能选其实切不动"的入口 |
### 15.6 不确定项(⛔ 不假装确定)
1. **跨节点迁移(C)的机制未设计** —— 实例 home 搬迁属"不可逆操作"级的事,需**单独立项**。
2. 「节点提供服务」的**具体服务清单**仍未定(与上一轮 §14.6-1 同一项)。
3. 家用/机房网络的**真实可达率未取数** ⇒ 体检判据与阈值要实测标定。
---
## 16 追问:「为什么不行」逐层拆解 —— 并把「当节点」拆成 Manager / Worker 两半(2026-09-26 09:0x)
**用户原话**:「还没有弄明白继续分析,为什么不行 1、选择当节点:还包括区分 manager 和 worker」
> 承上:§15 的结论是「承载归属 ⚠️ 不能一键切」。本节回答**到底卡在哪一层**,并补上用户新点出的「Manager / Worker 之分」。
> ⚠️ 本节结论**全部有代码级依据**(文件名 + 机制名),⛔ 不是推演。
### 16.1 先把「节点」拆开 —— 它其实是**两个进程、两个身份**
| | **Manager(控制面 · 判决者)** | **Worker(承载体 · 执行体)** |
|---|---|---|
| 它是什么 | 平台的**唯一权威**:决定"谁托管谁、谁能进、谁是设备" | 只负责"**在这台机器上把实例起停好**" |
| 自己有库吗 | ✅ 有(唯一权威库:用户/会话/工作区/**实例归属与租约**/插件池/域名/凭据/审计/网络) | ❌ **没有** —— `src/worker/` 全目录**零 DB 引用**(本次实测,非推测) |
| 地址面 | 域名 + 证书 + 门户 + nginx + **向中继注册的拨号身份** | 一个**内部** HTTP 服务,**只被 Manager 拨入** |
| 对外动作 | 全平台 | **只有四个白名单动作**;**单向拨入** —— 不反连、不写控制面数据 |
| 怎么防双写 | 判据的**唯一落点**:原子 CAS + `epoch`(fencing token) | 收到更高 `epoch` ⇒ **主动停掉该实例**(self-fencing) |
| 47 上是什么 | `dshs` 进程 | `dshs-worker` 进程(`w-47`)—— **同机、两进程、两身份** |
🔴 **所以「47 是个节点」这句话本身就把两半压成了一半。** 47 之所以"是个节点",是因为它**同时**跑了这两半。
**依据**:`src/worker/agent.ts`(模块头「它不做归属决策」+四条协议纪律)|`src/db/schema.ts` v7 集群化注释(为什么必须落 DB 原子 CAS、`epoch` 是 fencing token、抢占语义)|`src/supervisor/lease.ts`(三条不变量:单写者 / TTL>2×续租 / fencing 必须自杀)。
### 16.2 「选择当节点」⇒ 只能选 **Worker 那一半**
| 你想选的 | 能否由用户自选 | 为什么 |
|---|---|---|
| 当 **Worker**(出机器、承载实例) | ✅ **能** | 它**无库、不做归属决策**,只是执行体 ⇒ 天然**可水平增加**、可替换 |
| 当 **Manager**(出权威) | ❌ **不能** | 三条理由见下 |
**为什么 Manager 那一半不能自选**
1. 🔴 **它是"判决者",判决者不能由被判决的一方自己选出来。** 现有防双写机制(原子 CAS + `epoch` fencing + 租约)**整体假设只存在一个判决者**;出现第二个 = 对同一份数据各写一次 = **脑裂**,而脑裂的下游后果是"两个进程同时写同一个实例的家 ⇒ 会话日志 append 冲突 ⇒ **数据损坏**"(`schema.ts` v7 注释原话)。
2. 🔴 **它同时是"引导层"。** 插件装在**实例**里,而实例是 Manager 起的 ⇒ **Manager 不可能由插件提供**(与 §10 那条"包还没装、服务已经要跑"同一判据)。且本版平台**没有"实例 → 平台"的身份通道**,实例的入网凭据是**控制面代签**的(`fs/user-fs.ts` 明确写了)⇒ 签发者只能在上层。
3. 🔴 **换 Manager = 换锚点,下游全部要跟着改。** 域名与证书、门户入口、nginx、**向中继注册的拨号身份**(`hostId` 必须同时在该中继的密钥文件与白名单里)、设备授权签发者 —— 客户端认的**地址**都变了。这不是"配置项",是**换了一个平台**。
> ⇒ 一句话:**「当 Worker」是自选角色;「当 Manager」是换控制面**(= 另一套部署、另一种治理关系),⛔ 不能做成插件里的一个开关。
### 16.3 「接入后连实例一起用」为什么不行 —— 卡在四个**物理**事实上
| # | 事实 | 依据 |
|---|---|---|
| 1 | 实例的"家"是**那台机器上的绝对路径**,不是平台上的一个逻辑对象 | `fs/user-fs.ts` 注释:⛔ 不许直接读该路径 —— 实例在别的机器上时,平台侧只会读到**一个不存在的位置**,**返回空串而不报错**(2026-09-19 实测:托管清单被清空、目标文件一个字节没动) |
| 2 | 实例在 OS 层的**真实载体**是 systemd scope(形如 `dsh-<uid>-<hash>.scope`),启动参数就在 scope 描述里 | `supervisor/orchestrator.ts`:「实例真实生命周期在 OS 层(systemd scope)」+ scope 名与 `--reuid <uid>` 交叉验证 |
| 3 | 实例的网络位置是**全局分配的资源**:端口按 worker 划**互不重叠**区间,而所有跨机隧道落点**全挤在 Manager 的 `127.0.0.1`** | `supervisor/spawn.ts` `findFreePortInRange` 注释:⛔ 用 `listen(0)` 会让两台 worker 撞号,`ssh -R` 失败被静默忽略 ⇒ Manager 仍按该端口拨 ⇒ **打到别人的实例**(2026-09-16 实测) |
| 4 | 实例还带着一串**机器绑定的身份**:Linux uid(每用户一个)、cgroup 内存配额、防火墙放行、到 Manager 的入向可达 | `isolation.ts`(每用户确定性 uid)|`orchestrator.ts`(`MemoryHigh` 软限 + `MemoryMax` 硬限,V8 堆按配额推导)|`supervisor/firewall.ts`|`net/reachability.ts`(无可达信息即拒绝,不静默降级) |
**⇒ 「切换承载归属」等价于一次迁移**,动作序列至少是:停实例 → 搬 home 与数据 → 在目标机按**它的** uid/端口区间/配额重建 scope → 改归属行(`host_id`)→ 老机 self-fence。
**这不是"改开关",是"不可逆操作级"的动作** ⇒ ⛔ 不先开"看起来能选、其实切不动"的入口(与 §15.5-③ 同款纪律)。
### 16.4 「接入」的正确切法(把 §15 的结论落到**角色**上)
| 用户动作 | 对着哪一半 | 判定 |
|---|---|---|
| 装插件 → 选"只当用户设备" | 都不是 | ✅ |
| 装插件 → 选"当 Worker(当节点)" | **Worker** | ✅ 能,但要有**入向可达** + 平台放行(白名单**默认拒绝**) |
| 装插件 → 选"接入某节点(用它的内容)" | 内容归属 | ✅ **已实测**(106 → 47:2 MB / 3,437 ms) |
| 装插件 → 选"接入某节点(进它的网)" | 网络归属 | ✅ 能(同局域网**零门槛**) |
| 装插件 → 选"把我的实例挪到某节点跑" | **承载归属** | ⚠️ **只能迁移**,见 16.3 |
| 装插件 → 选"我来当 Manager" | **Manager** | ❌ **不是角色,是换控制面**,见 16.2 |
### 16.5 不确定项(⛔ 不假装确定)
1. **迁移(承载归属)的机制未设计** —— 与 §15.6-1 同一项,需**单独立项**。
2. **"当 Worker"的准入门槛未标定** —— 入向可达率、带宽、沙箱后端版本(实测 47 与 106 的 bubblewrap 版本**不同**)等体检项的阈值要实测。
3. **多 Manager(控制面联邦)的治理口径仍在覆盖网络线** —— 本节只回答"能不能由用户一键选",⛔ 不替代那边的架构定稿。
---
## 附 · 盘点来源(单一来源,⛔ 不复制正文)
- 平台自己的路由清单:`D:/github/dsh_shenxian/src/web/routes/`(`auth` · `admin` · `admin-user-ops` · `whitelist` · `business-plugins` · `plugin-data` · `skills` · `sessions` · `desktop` · `dsh` · `domain` · `im` · `overlay` · `overlay-device` · `overlay-nodes`)。
- 平台自有服务各层:`src/supervisor/`(12)· `src/fs/`(6)· `src/im/`(12)· `src/db/`(10)· `src/net/` · `src/worker/`(3)· `src/isolation.ts` · `src/config.ts` · `src/crypto.ts` · `src/platform-paths.ts` · `src/nginx/`。
- 形态与先例:`E:/ProgramData/AIProject/dsh-ai1net-desktop/MEMORY.md` §1(乙方案、"零 UI"口径)+ `docs/S12_…_20260925.md` §9。
- 本会话机制与对接单:`交付物/版本管理与包更新机制-执行报告-20260926.md` · `交付物/平台能力插件化-形态判定与分层_20260926.md` · `E:/ProgramData/AIProject/dsh-ai1net-desktop/docs/对接单_桌面端接入版本管理与包更新机制_20260926.md`。
- **本件的两个成品下游**(⛔ 本文只做分析,判定以它们为准):`交付物/基础服务插件-能做不能做总清单与拍板路径-20260926.md`(能做什么/不能做什么 + **拍板台账 P1–P4**)· `交付物/节点与端-互联互通与调用关系-20260926.md`(节点与端全表 + 三个门 + 联通矩阵 + 数据/实例/插件四层关系)。