Files
DevFlow/docs/02-架构设计/专项设计/devflow集成三方IM-飞书企微钉钉Web-规划设计-2026-10-04.md

25 KiB
Raw Permalink Blame History

DevFlow 集成三方 IM + 自定义 Web:AI Chat 远程接入规划

创建: 2026-10-04 | 状态: 📐 规划设计(待评审) 上级索引: ../INDEX.md | 姊妹篇: F-260620-01-跨端AIChat-微信小程序

一、目标与范围

在飞书、企业微信、钉钉三个 IM 内以及自定义 Web 页面中远程使用 DevFlow AI Chat:发消息、停止、重新生成、审批工具调用、切换会话——体验对齐桌面端。

范围外(本期不做):IM 内直接查看项目/任务列表等富 UI(依赖对话即入口,由 AI 工具调用返回文本结果);多人协作/团队版。

二、现状盘点(可复用的基础设施)

组件 现状 复用方式
u-relay(E:/wk-lab/u-relay) 生产运行(u-relay.1216.top:9180,nginx+systemd),channel_id 配对点对点转发,payload 透传零业务耦合,25 项测试 直接复用,IM 网关作为新 Commander 接入
df-tunnel(devflow crate) 桌面端出站 WS 隧道(Agent 角色),已实现 TunnelCommand 五指令路由(Send/Stop/Approve/Regenerate/Switch)+ TunnelEvent 四事件透传(TextDelta/ToolCall/Approval/Completed)+ 指数退避重连 零改动,桌面端不感知控制端是小程序还是 IM 机器人
df-miniapp(小程序端) F-260620-01 方案先行(Commander 模式先例) 自定义 Web 端直接套用该模式
AI Chat 引擎 Agentic Loop + 工具审批,多会话 conv_id 路由(F-09) 不动,仅靠隧道透传

核心洞察:三方 IM 机器人 + 自定义 Web = 新增的 Commander 端。桌面端(Agent)与 AI 引擎完全无感,全部新增工作收敛在「网关层」。

三、总体架构:IM 网关(df-im-gateway)

飞书 ─┐                                                        ┌─ DevFlow 桌面端 (Agent)
企微 ─┼─► df-im-gateway ──WS Commander──► u-relay ──WS Agent ──┤   df-tunnel + AI Chat 引擎
钉钉 ─┤   (新增,Rust 独立服务)            (生产已有,零改动)      │   (零改动)
Web ──┘  协议转换: IM API/回调 ↔ TunnelCommand/TunnelEvent      └─ 多桌面端=多 channel_id

df-im-gateway 职责(唯一新增组件):

  1. 以 WS Commander 身份连 u-relay(每条 channel 绑一个 DevFlow 桌面端)
  2. 接收 IM 平台事件(回调 Webhook 或长连接推送),转 TunnelCommand::Send/Stop/Approve
  3. 接收 TunnelEvent,聚合节流后调各 IM 平台消息 API 回复(流式 → 卡片刷新或一次性回复)
  4. Approval 事件 → 发交互卡片(同意/拒绝按钮)→ 点击回填 TunnelCommand::Approve
  5. IM 用户白名单 + channel_id 路由管理

为什么独立服务而不并入 u-relay:u-relay 的设计原则是「纯转发、零业务耦合、无外部依赖」。IM 对接需要凭证管理、回调验签、卡片模板、节流聚合等大量业务逻辑,塞进去会让中继失控。独立部署同机即可。

语言选型 Rust:直接 path 引用 df-tunnel 的 TunnelCommand/TunnelEvent 类型(同 workspace 或 git 依赖),编译时保证协议类型一致——这正是 F-260620-01 已论证的 Rust 中继优势。三方 IM SDK 均为 HTTP API,reqwest 直调即可,无 SDK 依赖硬伤。

三A、新组件必要性分析(df-im-gateway 为什么必须存在)

新增组件的正当性须逐一排除所有替代方案,全部不成立才立得住:

替代方案 排除理由
并入 u-relay ① u-relay 设计原则是「纯转发、payload 透传、零业务耦合、无外部依赖」,IM 对接需引入各平台 HTTP 客户端/AES 加解密/卡片模板/SQLite/节流器,塞入即毁掉其小审计面与高可用定位;② 故障域耦合:u-relay 是全局单点(小程序+IM+未来端全挂它),IM 平台 API 迭代快(钉钉卡片版本频繁),会让生产中继被迫高频重启;③ 二进制膨胀:3.6MB musl 单二进制的部署优势丧失
做进桌面端(df-tunnel/src-tauri) ① NAT 硬伤:企微回调必须公网 URL+可信 IP/域名,桌面端在内网/笔记本无法满足;飞书/钉钉长连接虽是出站,但桌面端休眠/离线时连「桌面端离线」提示都发不出(只有云端网关能兜底回复);② 桌面端会变成三套 IM 协议怪兽,Tauri 依赖拖重;③ 桌面端 IP 不固定、生命周期随人,不适合承载对外服务
Go/Node 跨语言独立服务 违背 F-260620-01 已论证的选型结论:TunnelCommand/TunnelEvent 跨语言镜像必然漂移,运行时消息丢失;Rust git 依赖 df-tunnel 类型 = 编译期协议一致 + 复用 publish-df-relay 同款 musl→scp→systemd 发布流水线。诚实 trade-off:飞书/钉钉官方 SDK 无 Rust 版需自写 HTTP 客户端,但均为标准 REST+签名协议,工作量可控
现成开源桥接器(onebot/matrix bridge 等) ① 三家 IM 官方机器人协议均非 onebot 标准,桥接器面向「自建聊天服务」场景,与「驱动本机 AI 引擎+工具审批」语义不匹配;② 审批按钮/卡片流式深度交互现成方案覆盖不了;③ 徒增第三方供应链风险
不做(维持现状) 需求价值真实:手机端随时驱动工作机 AI、审批卡片当场点掉无需回工位——是「桌面 AI 使用场景从工位扩展到任何地方」的产品能力增量,非技术炫技

结论:df-im-gateway 是「协议转换 + 凭证管理 + 公网可达性」三个约束的唯一交汇落点,必要性成立。

三B、破坏性分析(对既有系统的侵入面)

零改动承诺(桌面端/u-relay/AI 引擎)逐项压力测试,暴露真实破坏点:

受影响面 破坏性 等级 缓解/收敛策略
u-relay 双 Commander 并存语义 网关与 df-miniapp 同 channel 时:TunnelEvent 广播给两个 Commander → 双端重复回复用户;审批卡片双消费(小程序已点同意,IM 端卡片仍挂起可再点 → 重复 Approve) 🔴 唯一高危点 P0 实测 u-relay 多 Commander 投递行为;桌面端 Approve 处理按 approval_id 状态机幂等(pending→approved,二次点击无效——待 P0 验证,若无则补);过渡期运营约定同 channel 单活跃控制端;长期可演进「每端独立 channel + 桌面端多连接」(代价是破坏桌面端零改动,仅双端并存真实痛点出现时再议)
df-tunnel 协议扩展压力 IM 场景或需新指令/事件变体(如列会话、选择会话),触发桌面端升级 🟡 低 「新增变体只加不减」契约已内建向后兼容(serde #[serde(other)] 兜底);u-relay payload 透传对新变体天然无感;版本错配窗口仅存在于桌面端未升级期间
桌面端资源与连接 无新增常驻负担(仍是一条出站 WS,连接数不变) 🟢 无 —
公网暴露面 攻击面从「u-relay 一个 WS 服务」扩大到「+网关 HTTP/WS(企微回调、Web Chat 端点)」 🟡 中 网关自身不执行任何命令/写盘(纯转发,真正工具执行仍在桌面端审批门禁之后);IM 用户白名单默认拒绝;审批分级(IM 端仅低风险工具)
密钥面 三家 IM app_secret 新增落地,泄漏面 +1 🟡 中 网关 env/600 权限配置文件,不进 git;泄漏即换 secret 的应急预案
运维面 同机新增 systemd 服务(Rust 常驻 ~10-30MB)+ nginx +1 路由 🟢 低 发布节奏与 u-relay 解耦,互不牵连
数据一致性 chat_id↔conv_id 映射漂移(桌面端删会话后悬空);IM 卡片状态与桌面端真实状态漂移(审批后卡片未刷新) 🟡 低 映射悬空时网关回复「会话不存在」并自动清理重映射;卡片带状态版本号 + 过期失效文案

总结论:破坏性集中收敛于「双 Commander 并存」单点(🔴→🟡 可收敛),其余均为低危可控。零改动承诺成立——不需要为接入 IM 而改动任何现有生产组件;仅当未来「多控制端并存成常态」时才需重审 channel 架构。

四、四端集成模式对比

维度 飞书 企微 钉钉 自定义 Web
应用形态 企业自建应用 企业自建应用 企业内部应用 + 机器人 网关托管静态页或桌面端内嵌页
事件接收 长连接模式(官方 WS 推送,免公网回调)✅ HTTP 回调(GET 验签 + POST AES 加密解密,需公网 URL) Stream Mode(官方 WS 推送,免公网回调)✅ 用户浏览器直连网关 HTTP/WS
主动回复 im/v1/messages(卡片消息) 应用消息 send(Markdown 卡片) 机器人发送 + 卡片 Stream 更新 WS 推流(复用 df-miniapp 模式)
流式体验 卡片流式更新(官方支持) 消息可编辑(弱流式,节流刷新) 卡片 AI 流式更新(官方支持) 原生流式,体验最佳
审批按钮 交互卡片(最完善) 按钮交互卡片(支持但样式受限) 卡片回调按钮(支持) 页面按钮(自由发挥)
接入复杂度 中(长连接最省事) 高(回调验签/AES/IP 白名单/可信域名) 中 低(无三方审核)
前置条件 管理员后台建应用+开权限 管理员后台建应用+配置可信 IP 开放平台建应用+Stream 开关 无

接入顺序建议:Web(0 审核成本,先打通网关全链路)→ 飞书(长连接,免公网回调)→ 钉钉(Stream,同免公网)→ 企微(回调配置最繁琐,放最后)。

五、消息协议映射

IM 用户 @机器人 "把登录任务推进到 in_review"
  → 网关识别平台+用户+群聊 chat_id → 映射 conv_id(chat_id ↔ conv_id 持久化映射表)
  → TunnelCommand::Send { conv_id, content }
  → [桌面端 AI 引擎执行,ToolCall 需审批时回 TunnelEvent::Approval]
  → 网关发交互卡片 "[审批] advance_task(id=xxx, in_review)  [同意] [拒绝]"
  → 用户点同意 → TunnelCommand::Approve { conv_id, approval_id, accepted: true }
  → TunnelEvent::TextDelta(节流聚合) / Completed
  → 网关更新卡片 / 发最终回复 + 来源标注("由 DevFlow AI 生成")

关键设计点:

  1. 流式节流:IM 平台消息 API 有频控(飞书 50 QPS/应用级、企微 200 次/分钟级),TextDelta 需聚合(如 800ms 窗口批量刷卡片);P1 可先非流式(进行中卡片 → Completed 后一次性回复)
  2. 会话映射:IM chat_id(单聊/群聊)↔ conv_id 一对一映射存网关侧 SQLite;群聊内仅 @机器人 时响应
  3. 多桌面端路由:网关支持配多 channel_id(如 chan-work→工作机、chan-home→家用机),IM 指令默认路由或按前缀路由(/dev2 任务描述)
  4. 离线兜底:Agent 不在线时网关直接回「桌面端未连接」(u-relay 有 PresenceState 可查询)+ 可选指令缓存重发
  5. 消息长度:IM 单条上限(飞书卡片较大、企微 Markdown 4096 字节),超长回复分段或转摘要+附件

六、安全设计

  1. 双因子:u-relay 侧 channel_id + token 配对(已有,常量时间比较);IM 侧回调验签(飞书 encrypt key/verification token、企微 AES+签名、钉钉 Stream 免验签)
  2. 用户白名单:企业成员 user_id 白名单表(默认拒绝所有),防止应用可见范围内任何人都能驱动桌面端 AI(高危:AI 可执行命令/改数据)
  3. 审批降权:IM 端默认只允许 Approve 低风险审批(只读工具);高风险(run_command/生产写入)强制回桌面端确认——网关侧按工具风险等级过滤
  4. 凭证管理:各平台 app_id/secret 存网关本地 env/配置文件(600 权限),不进 git;token 自动续期(企微 access_token 2h 缓存)
  5. 审计:网关侧记录 IM 消息 ↔ TunnelCommand 全量日志(谁在何时通过哪个平台驱动了什么指令)

七、实施分阶段

Phase 内容 产出 预估工作量 依赖
P0 网关骨架 df-im-gateway Rust 项目:WS Commander 连 u-relay(复用 df-tunnel 类型)、会话映射表、节流聚合器、配置管理、白名单框架 可连通桌面端收发消息的骨架 + 单测 2-3 天 u-relay 生产实例(已有)
P1 Web 端 自定义 Web Chat 页(复用 df-miniapp 交互模式:流式渲染/审批按钮/会话切换)+ 网关托管 浏览器/手机浏览器可用完整 AI Chat 2-3 天 P0
P2 飞书 长连接事件订阅 + 消息卡片(流式更新)+ 交互卡片审批 + 群聊 @识别 飞书内完整可用 3-4 天 P0;管理员开应用权限
P3 钉钉 Stream Mode + 卡片流式 + 按钮回调 钉钉内完整可用 3-4 天 P0;开放平台建应用
P4 企微 回调 URL 验签 + AES 解密 + IP 白名单 + Markdown 卡片(节流伪流式)+ 按钮审批 企微内完整可用 3-4 天 P0;管理员后台配置
P5 收尾 审批分级(IM 低风险白名单)、全平台 E2E 联调、运维脚本(复用 publish-df-relay 模式发布网关)、文档 生产上线 2 天 P1-P4

总计约 15-20 人天。里程碑切法:P0+P1 完成即有「自定义 Web 远程 AI Chat」可日常自用(最快见效);P2/P3 并行推进;P4 视需求紧迫度排后。

八、风险与对策

风险 影响 对策
IM 平台 API 频控/卡片更新限制 流式体验降级 节流聚合 + 降级为「进行中→完成」两段式
企微回调配置(可信域名/IP 白名单/AES) 接入周期拉长 放 P4;提前准备备案域名与出口固定 IP
IM 端误触高危操作(命令执行/生产写入) 安全事故 审批分级:IM 端仅放行低风险工具,高危强制桌面端确认
桌面端离线/休眠 IM 无响应 离线提示 + PresenceState 感知 + 桌面端防休眠提醒
u-relay 单 token 全局配对粒度粗 多渠道管理乱 网关侧单连接多路复用(channel 维度由网关统一持有);若需多 token 再扩 u-relay
三平台 API 版本迭代(钉钉旧版机器人已停更) 维护成本 统一走各家「新版卡片 + Stream/长连接」官方推荐路径,不碰 legacy

九、待决策项

# 决策点 选项 建议
D1 网关部署位置 a) 与 u-relay 同机部署 b) 独立小鸡 a(省成本,nginx 加路由即可)
D2 df-im-gateway 与 df-tunnel 类型复用方式 a) git 依赖 df-tunnel crate b) 复制镜像类型 a(编译期类型一致,协议演进单点维护)
D3 Web 端形态 a) 独立静态页托管在网关 b) 复用 df-miniapp 代码库分支 a(小程序与 Web 场景差异渐大)
D4 IM 审批放权策略 a) 全审批 IM 可批 b) 低风险白名单 b(安全优先)
D5 飞书/钉钉/企微三个应用是否现在就申请 a) 立即申请配权限 b) 到对应 Phase 再申请 b 中的 a:飞书立即申请(管理员审批周期不可控,先启动)

十、与现有架构关系

  • F-260620-01 跨端 AI Chat:本规划是其横向扩展(Commander 端从「微信小程序」扩展到「IM 机器人 + Web」),协议与中继完全复用
  • df-mcp:对外 stdio 暴露数据层(本地进程级);本规划是远程人类入口(IM 级),互补不重叠
  • u-relay:零改动(除非 D5 触发多 token 需求)
  • df-tunnel / AI 引擎:零改动

附录A、PostgreSQL 多租户 × DevFlow 结合分析(§十 S3 展开论证)

A.1 概念:多租户三模式

模式 结构 优点 缺点 适合
行级隔离 同库同表,每行 tenant_id 列,查询强制 WHERE 成本最低、迁移跑一份、跨租户统计易 隔离最弱(漏写 WHERE 即泄漏)、noisy neighbor 租户多单租户数据小,SaaS 主流
独立 Schema 一实例每租户一 schema 隔离较强,按 schema 备份 租户多时爆炸;DDL 迁移跑 N 遍 租户几百内
独立 Database 每租户一库 最强隔离 资源贵、连接池复杂、跨租户查询难 租户少而大、合规严

行级隔离通常配合 PG RLS(Row Level Security):租户过滤下沉数据库层——session 设 SET app.tenant_id,策略自动过滤,应用层漏写 WHERE 也不泄漏。这是 PG 多租户的核心王牌。

A.2 激活时机

DevFlow 当前是本地优先单机应用(SQLite/单用户/零运维),「租户」不存在。多租户仅在 S3 AI 引擎上云时被激活:SQLite 单写者模型扛不住云端多用户并发写,且「本地优先」范式失效——隔离必须显式设计。多租户是需求,PostgreSQL 是载体(PG 因并发写 + RLS + 生态成为默认答案)。

A.3 租户模型与选型

  • 租户模型:tenant=团队(付费主体),user=成员;现有全部表(projects/tasks/ideas/workflows/ai_messages/knowledge/audit…)挂 tenant_id
  • 选型:行级隔离 + RLS(非 schema/database 隔离)。决定性理由是迁移演进节奏:V1-V16+ 已 20+ 表且快速迭代,per-tenant schema 下每次迁移要循环跑 N 遍并处理失败重试,直接拖死迭代速度;行级隔离迁移只跑一份

A.4 落点三抓手

  1. df-storage trait 层(对齐 F-07 下沉思路):存储访问收敛 Repository trait,多租户化 = trait 实现层注入租户上下文(依赖 PG session 变量),上层 commands 无感——现有分层红利
  2. 迁移体系:SQLite 手写迁移换 sqlx/refinery 版本化迁移,一套脚本跑一份
  3. RLS 策略:每表 USING (tenant_id = current_setting('app.tenant_id')::uuid),连接池(pgbouncer)按事务设租户变量

A.5 配套改造清单(多租户 ≠ 换数据库驱动)

缓存键带租户(防 Redis 串数据)/ 对象存储按租户前缀隔离 / 日志审计按租户切分 / 备份恢复按租户粒度。全链路改造——这正是 §十 把 PG 多租户列入「显式不做」的原因:现在做 = 为不存在的需求付全链路成本。

A.6 与本期规划的交集

唯一交集 = P0 已预留的会话映射表 owner_user_id 列(S2 时是「用户归属」,S3 升级语义为「租户内用户」),成本≈0。时序红线重申:S2 各人数据仍在各自桌面端 SQLite,云端只有路由表,不需要 PG;S3 才引入 PG+RLS,与引擎多租户化统一做。

附录B、引入云端后的多人架构模式(C4 视图)

按 C4 模型整理:C1 上下文(现状基线 → S2 多人 → S3 引擎上云)+ C2 容器(网关内部)+ 部署视图 + 数据归属演进表。 贯穿不变量:TunnelCommand/TunnelEvent 是网关↔执行端唯一契约,执行端可从「桌面端 df-tunnel」无缝换成「云端 df-agent」,网关与 IM 端零改动。

B.1 C1 上下文 —— 现状基线(S0:单机 + 小程序遥控)

flowchart TB
    subgraph local["💻 用户本地(数据主权 100% 在个人)"]
        desktop["DevFlow 桌面端 (Tauri v2)<br/>AI Chat 引擎(Agentic Loop+审批) · SQLite · df-tunnel(Agent)"]
    end
    subgraph cloud["☁️ 云端 u-relay.1216.top(nginx TLS 终止)"]
        relay["u-relay :9180<br/>channel_id 配对 · payload 透传零业务耦合"]
    end
    mini["📱 df-miniapp 微信小程序<br/>(Commander)"]
    mini <-->|"WSS"| relay
    relay <-.->|"WS Agent 出站穿NAT<br/>单 channel 配对"| desktop

B.2 C1 上下文 —— S2 团队多人(云端路由化,数据仍归各人)

本期 IM 网关落地后的直接形态:云端只有「路由与审计」,业务数据仍在各桌面端。

flowchart TB
    subgraph users["👥 多人入口"]
        direction LR
        im["飞书 / 钉钉 / 企微<br/>企业机器人"]
        web["Web Chat<br/>浏览器/手机"]
        mini["df-miniapp<br/>微信小程序"]
    end

    subgraph cloud["☁️ 云端(nginx TLS 终止)"]
        direction TB
        gw["df-im-gateway :9190<br/>平台适配器 · 会话映射 · 白名单<br/>节流聚合 · 审批分级 · 审计"]
        relay["u-relay :9180<br/>多 channel 配对转发"]
        gw <-->|"WS Commander(多channel)<br/>TunnelCommand / TunnelEvent"| relay
    end

    subgraph deskA["用户A 本地"]
        dA["DevFlow 桌面端<br/>AI引擎 + SQLite(A) · df-tunnel"]
    end
    subgraph deskB["用户B 本地"]
        dB["DevFlow 桌面端<br/>AI引擎 + SQLite(B) · df-tunnel"]
    end

    im --> gw
    web <-->|"HTTP/WS"| gw
    mini <-->|"WSS Commander"| relay
    relay <-.->|"WS Agent · chan-userA"| dA
    relay <-.->|"WS Agent · chan-userB"| dB

路由链路:IM 消息 → 网关查映射表 chat_id → (owner_user_id, channel_id, conv_id) → 发 TunnelCommand::Send 到对应 channel → 该用户桌面端执行。用户隔离靠 channel 隔离(一人一 channel,per-channel token 为 u-relay 演进观察项)。

B.3 C2 容器 —— df-im-gateway 内部(适配器 trait 分层,S4 扩展点)

flowchart TB
    subgraph adapters["平台适配器层(各平台差异收敛于此)"]
        feishu["feishu_adapter<br/>长连接+卡片流式"]
        dingtalk["dingtalk_adapter<br/>Stream Mode"]
        wecom["wecom_adapter<br/>回调验签+AES"]
        webui["web_adapter<br/>WS 直连+静态页"]
    end
    subgraph core["核心(平台无关,S4 加端零改动)"]
        unify["统一模型<br/>InboundEvent / OutboundAction"]
        sess["会话映射表 (SQLite)<br/>chat_id | conv_id | owner_user_id | platform | channel_id"]
        throttle["节流聚合器<br/>TextDelta 时间窗"]
        acl["白名单 + 审批分级"]
        audit["审计日志<br/>IM消息 ↔ TunnelCommand"]
        unify --> sess
        sess --> throttle
        acl --> sess
        sess --> audit
    end
    relay["u-relay WS Commander"]
    feishu --> unify
    dingtalk --> unify
    wecom --> unify
    webui <--> unify
    core <-->|"TunnelCommand / TunnelEvent"| relay

B.4 C1 上下文 —— S3 引擎上云(云端多人完整形态,PG 多租户)

执行端从「各人桌面端」切换为「云端 df-agent 服务」,同一协议,网关/IM 端零改动;桌面端退化为瘦客户端/管理台,本地优先形态仍可用(不废弃)。

flowchart TB
    subgraph users["👥 多人入口"]
        direction LR
        im["飞书/钉钉/企微"]
        web["Web Chat"]
    end
    subgraph cloud["☁️ 云端"]
        direction TB
        gw["df-im-gateway<br/>(零改动)"]
        relay["u-relay<br/>(零改动)"]
        agent["df-agent 云端执行服务<br/>AI 引擎(Agentic Loop) · 工具沙箱<br/>多会话/多租户调度"]
        gw <--> relay
        relay <-.->|"同协议<br/>TunnelCommand / TunnelEvent"| agent
    end
    subgraph data["数据层(附录A 落点)"]
        pg[("PostgreSQL<br/>行级 tenant_id + RLS<br/>projects/tasks/messages/audit")]
        redis[("Redis<br/>租户前缀缓存键")]
        oss[("对象存储<br/>租户前缀隔离")]
    end
    thin["💻 桌面端=瘦客户端/管理台<br/>本地 SQLite 留个人配置"]
    im --> gw
    web <--> gw
    agent --> pg
    agent --> redis
    agent --> oss
    thin <-.-> relay

B.5 部署视图(S2 落地形态,单机双服务 + nginx)

flowchart LR
    subgraph inet["互联网"]
        u["用户 IM / 浏览器 / 小程序"]
    end
    subgraph server["云端主机(u-relay 同机,复用 nginx+systemd 模式)"]
        nginx["nginx :443 TLS 终止<br/>/ws/* → 9180<br/>/im/* /chat/* → 9190"]
        ur["u-relay :9180<br/>systemd · musl 单二进制 ~3.6MB"]
        gig["df-im-gateway :9190<br/>systemd · musl 单二进制<br/>静态 Web Chat 页托管"]
        nginx --> ur
        nginx --> gig
    end
    subgraph homes["各用户本地"]
        dt["DevFlow 桌面端<br/>出站 WSS → /ws/agent"]
    end
    u -->|"wss://u-relay.1216.top"| nginx
    dt <-.->|"出站穿NAT"| nginx

B.6 数据归属与信任边界演进表

维度 S0 现状 S2 团队多人 S3 引擎上云
身份/会话映射 桌面端 Settings 网关 SQLite(owner/channel 列已预留) 网关 → 并入 PG
项目/任务/工作流 桌面端 SQLite 各桌面端 SQLite(主权在个人) PG(tenant_id+RLS)
AI 消息/溯源 桌面端 ai_messages 各桌面端 PG
IM↔指令审计 — 网关审计日志 PG(租户维度)
信任边界 个人设备 云端只见「指令与脱敏结果」 云端见全部业务数据(需租户隔离+合规)
桌面端角色 全栈主体 执行主体 瘦客户端/管理台(可选保留本地优先)

B.7 演进的三条设计不变量

  1. 协议不变性:网关↔执行端唯一契约是 TunnelCommand/TunnelEvent(df-tunnel crate 类型),S3 换执行端零成本——这是全方案最重要的远期红利
  2. u-relay 纯转发零耦合不破例:业务逻辑只进 df-im-gateway / df-agent,中继永远轻
  3. 数据主权渐进上移:S0/S2 数据在个人(本地优先不被废弃),S3 才集中上云并配套 RLS 隔离——每一步都由真实需求触发,不过度设计