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/ 知识文件,按口径入库)
This commit is contained in:
admin committed 2026-10-10 23:13:22 +08:00
1 parent 30b46dbd0c
commit c1b5e4d966
735 files changed
+153192 -2415

No files matched your search

@@ -0,0 +1,384 @@
# 平台新增功能总盘点 × 插件化归属("基础服务插件"能不能做、能装哪些)
> **回答的问题**(用户原话):「把现在开发的平台基于 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`(节点与端全表 + 三个门 + 联通矩阵 + 数据/实例/插件四层关系)。