Files
RemoteDesk/docs/architecture.md
曾志威 19a8e03a83
ci / rust (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / package-preview (push) Canceled after 0s
ci / package-installer (push) Canceled after 0s
ci / linux-agent (push) Canceled after 0s
ci / edge-service (push) Canceled after 0s
ci / coturn-pop (push) Canceled after 0s
ci / package-windows-host (push) Canceled after 0s
Document all-Rust migration and extend native media stack
2026-08-14 14:31:57 +08:00

20 KiB
Raw Permalink Blame History

RemoteDesk 总体架构

当前生效的目标架构以 ADR 0001 为准: Agent、Client、协议和实时通信运行时全部使用 Rust,客户端采用 winit、wgpu、egui,实时传输采用 str0m WebRTC。下文若出现 Tauri、 WebView、Go、Hysteria2、兼容模式或独立 Edge,均视为迁移历史。

1. 目标和边界

RemoteDesk 解决两个问题:

  1. 用 Windows 客户端连接 Windows RDP 和现代 Linux 被控端。
  2. 在不依赖 VDI 的前提下,连接长期存在的固定工作站。

项目以单用户和小规模设备列表为第一目标。连接目标是长期存在的固定机器,不负责创建桌面、分配用户或回收虚拟机。Linux 只作为被控端,不构建 Linux 客户端。

2. 为什么采用双通道

Windows 的 RDP 服务成熟,重复实现服务端没有价值。现代 Linux 的桌面栈以 Wayland 为主,而 xrdp 通常建立独立 Xorg 会话,不能准确复现当前桌面状态,也容易丢失 GPU 加速、桌面扩展和音频体验。Linux 被控端因此固定使用自研 Agent,不依赖桌面环境自带的 RDP 服务。

因此客户端统一,协议不强行统一:

目标 首选通道 说明
Windows 11 Pro/Enterprise、Windows Server RDP 使用 Windows 系统服务
GNOME/KDE 等 Wayland 桌面 Native Desktop Agent + Portal/PipeWire/libei
Xorg 桌面 Native Desktop Agent + XComposite/DRI3/XDamage/XTest
无图形桌面的 Linux Native Terminal Agent + PAM/PTY
Linux 登录界面 Console fallback 后续按合成器适配

所有连接在 UI 中表现为相同的“主机配置”,具体通道由配置和能力探测决定。

3. 系统组件

3.1 Windows Desktop Client

Windows 客户端负责:

  • 管理连接配置、收藏和最近连接。
  • 使用系统密钥库读取凭据。
  • 创建 RDP 或 Native 会话。
  • 渲染远程画面并发送输入。
  • 处理剪贴板、音频、显示器和文件传输。
  • 展示延迟、帧率、码率和丢包等质量指标。

客户端内部按能力拆分:

Tauri UI (React/TypeScript)
        |
Session Controller ---- Profile Store ---- Secret Store
        |
  +----------+--------------+-------------+
  |          |              |
RDP Helper  Native Video   Terminal/Edge
  |          |              |
IronRDP   GStreamer       xterm.js

客户端使用 Tauri 2 + React/TypeScript 构建主界面,终端使用 xterm.js。Linux 桌面在独立 GStreamer/D3D11 原生 helper 中完成 WebRTC、硬件解码和呈现,原始帧不进入 Tauri/WebView。Windows RDP 在另一个独立 Rust helper 中使用 IronRDP/D3D11。

3.2 RDP Adapter

RDP helper 封装 IronRDP,不向 Tauri UI 暴露 IronRDP 数据结构或图形帧。主要职责:

  • NLA/TLS 认证与证书校验。
  • 图形更新、动态分辨率和多显示器布局。
  • 键盘布局、鼠标、Unicode 输入和组合键。
  • 音频播放、麦克风和剪贴板通道。
  • 本地目录重定向,默认关闭。
  • 会话断开原因映射和有限次数重连。

渲染层将 IronRDP 图形更新映射为 D3D11 脏矩形上传。IronRDP 的 NLA、动态分辨率、虚拟通道和 RDP UDP multitransport 必须先通过技术验证;关键能力不足时,MVP 以系统 mstsc 作为隔离回退。

主程序与 helper 通过当前用户 ACL 限制的 Named Pipe 交换版本化控制消息。凭据不进入命令行,视频帧不进入 IPC。

3.3 Linux Agent

Linux Agent 是 Linux 唯一的被控端组件,不调用 GNOME Remote Desktop、KRdp 或 xrdp,也不提供发起远程连接的客户端界面。Agent 使用 Rust 实现,媒体管线通过 GStreamer 及其 Rust bindings 接入。

Agent 分为两个进程和权限层:

remotedesk-agent-session (graphical user)
├── Wayland: Portal/PipeWire/libei
├── Xorg: XComposite/DRI3/XDamage/XTestXShm 仅兼容模式
├── 编码与会话信令
└── 用户音频与剪贴板

remotedesk-shell-session (target user)
├── PTY 与登录 Shell
├── 终端尺寸、信号与环境
└── 可靠 DataChannel

remotedesk-agentd (system)
├── 开机启动、设备配对和客户端授权
├── 发现已登录用户并联络会话进程
├── 更新与版本兼容检查
├── 可选的受控 uinput Helper
└── 受限 Unix Socket IPC

agentd 不捕获桌面、不编码媒体,也不读取剪贴板或终端内容。所有桌面处理保留在普通用户会话中。命令行会话由独立进程在完成 PAM/设备授权后降权到目标 UID,再创建 PTY。可选的 uinput Helper 必须代码面尽量小,并通过固定消息类型、调用方身份验证和最小设备权限限制风险。

3.4 Edge Relay

远程链路支持 Direct、Single Edge 和 Dual Edge。默认同时测量直连和 CDN 路径;当双边缘路径更优时,客户端与 Agent 分别进入最近 TURN POPPOP 间通过 CDN 私有/优质骨干传输。TURN 只转发 DTLS-SRTP/SCTP 密文,不解码、不转码、不缓存远程内容。

Agent 通过出站 TLS WebSocket 连接最近的 Rendezvous Gateway,客户端以已配对设备密钥签名会话请求。控制面只负责在线路由、信令和短期 TURN credential,不替 Agent 授权桌面或终端。详细设计见 CDN 与边缘中继

Windows RDP 不使用 ICE/TURN。其 CDN 加速需要目标网络中的出站 L4 Edge Connector,在两端 POP 间透明转发 RDP TCP/UDPRDP TLS/NLA 仍由 IronRDP 与目标 Windows 端到端完成。

3.5 Windows Headless Native Endpoint

Windows 被控端的高性能桌面会话使用 IDD/IddCx 创建 Headless SDR 虚拟显示器。IDD 只负责虚拟显示器、显示模式和 swap-chain 生命周期,不负责编码、网络或业务授权。当前产品约束为 SDR 8-bitIDD 不作为 HDR10/10-bit 采集源。

正式组件边界如下:

Windows Service / Go Backend
- session、认证、配置和生命周期
- 输入路由(独立高优先级控制面)
- Hysteria2 路径和媒体策略

Windows Capture WorkerRust/C++ 原生 helper
- IDD frame 或 Desktop Duplication 兼容捕获
- BGRA8 GPU texture -> NV12 GPU conversion
- NVENC / AMF(VCE/VCN) / Quick Sync
- x264 / x265 / SVT-AV1 软件兜底
- 独立视频和音频队列
- 有界媒体 datagram / 控制流

IDD Driver
- Headless SDR 虚拟显示器和 4K/120 模式

Windows Headless 主路径优先直接消费 IDD GPU frame;第一阶段允许使用 Desktop Duplication 复制已存在的虚拟 output 作为兼容实现。两种路径都必须在同一 DXGI Adapter 上完成 GPU 处理,原始 BGRA8 不得进入 Go、JSON、Tauri IPC 或网络队列。

Windows Headless 视频默认支持 H.264/AVC、H.265/HEVC 和 AV1。编码器先尝试同 Adapter 的硬件后端:NVIDIA 使用 NVENCAMD 使用 AMF/VCE/VCNIntel 使用 Quick Sync;硬件能力验证失败时按协商结果切换 x264、x265 或 SVT-AV1。软件编码只能作为明确的兼容性降级,并自动降低分辨率、帧率或并发上限。

Windows Headless 音频独立使用 WASAPI loopback 或配置的虚拟音频 endpoint 采集,编码为 Opus,通过独立音频流发送。音频和视频使用同一会话单调时钟;输入不等待任何音频、视频帧、编码器或呈现确认。

无硬件 GPU 时启用 compatibility 模式。启动阶段依次探测硬件 DXGI Adapter、IDD/DDA output、WARP/Basic Display Adapter、硬件编码器和软件编码器;如果只有软件路径可用,允许 DDA/IDD 继续采集,但把像素路径标记为 software,并按 CPU 实测上限自动降低分辨率、帧率和并发数。compatibility 模式不得继续宣称 zero-copy、hardware_encode 或多路 4K/120 保证;strict 性能模式则在硬件编码器不可用时拒绝请求。

4. Linux 会话模式

Wayland 不允许普通进程静默截屏和任意注入输入,这是安全边界,不应通过默认 root 运行 Agent 来绕过。

4.1 Assisted Session

适合已经登录且有人可确认授权的桌面:

  1. Agent 请求 RemoteDesktopScreenCast Portal 会话。
  2. 桌面弹出系统授权窗口。
  3. Portal 返回 PipeWire 节点和 libei/EIS 输入通道。
  4. Agent 编码画面并建立 WebRTC 会话。
  5. 用户结束共享或锁屏后,根据桌面策略暂停连接。

这是跨桌面环境最标准的模式,应优先实现。

4.2 Paired Unattended Session

目标是在用户首次本地授权后允许同一设备再次连接:

  • Agent 保存 Portal 返回的恢复令牌和已授权显示器信息。
  • 后续连接尝试恢复 Portal 会话,并继续使用 PipeWire/libei。
  • 若合成器拒绝恢复或要求再次确认,Agent 必须返回明确状态,不能静默降级到 root 抓屏。
  • 不同发行版和合成器对持久授权的支持不同,能力探测结果必须展示给用户。

该模式仍由自研 Agent 实现,只复用 Wayland/Portal 提供的安全接口。

4.3 Pre-login And Lock Screen

登录前没有普通用户的 Portal/PipeWire 会话。通用实现需要 DRM/KMS 捕获、虚拟显示、uinput 和显示管理器集成,容易与合成器、安全策略及显卡驱动冲突。因此首期规则如下:

  • 已登录会话锁屏后的行为按桌面能力检测,不承诺跨桌面一致。
  • 物理机登录界面控制属于后续按发行版适配的实验能力。
  • 不能通过让整个 Agent 以 root 运行来绕过限制。

4.4 Xorg Desktop Session

Xorg 会话 Agent 以当前图形用户身份运行,并读取该用户的 Xauthority:

  • 使用 XDamage 获取变化区域,减少静态桌面复制。
  • 默认通过 XComposite 获取 pixmap,并用 DRI3 导出 DMA-BUF 直达同 GPU 编码器。
  • XShm/XImage 只在用户显式 compatibility 模式中启用。
  • 使用 XTest 注入键盘和鼠标事件。
  • 使用 XRandR 获取显示器拓扑和分辨率变化。
  • 音频统一从 PipeWire 获取,不建立另一套音频协议。

Agent 只能连接自己所属用户的 Xorg 会话,不复制其他用户的 Xauthority,也不通过关闭 X server 访问控制来简化部署。

4.5 Command-line Session

纯命令行模式不需要 Wayland、Xorg 或 PipeWire

  1. 已配对客户端请求目标普通用户的终端权限。
  2. agentd 验证设备权限,并通过 PAM 建立受审计的用户会话。
  3. 独立 remotedesk-shell-session 进程完成组、GID、UID 和环境降权。
  4. 进程创建 PTY 并执行该用户允许的登录 Shell。
  5. Windows 客户端使用终端控件显示 UTF-8 输出并发送输入、窗口尺寸和信号。

Linux 终端允许在配对授权中明确加入 root 用户。Agent 仍通过 PAM、独立会话进程和授权用户列表校验目标身份,不保存提权密码;未获授权的用户仍会被拒绝。

5. Native 协议

Native 通道用于 Linux Agent。第一版采用 WebRTC,避免自行设计媒体拥塞控制、时钟同步和加密传输。

5.1 通道划分

通道 内容 可靠性
Video RTP H.264/HEVC/AV1 视频 允许丢包
Audio RTP Opus 音频 允许少量丢包
Input DataChannel 键盘、鼠标、触控 有序,低延迟
Control DataChannel 显示器、质量、心跳 可靠有序
Clipboard DataChannel 文本、图片元数据 可靠有序
File channel 分块文件数据 可靠,可取消
Terminal DataChannel UTF-8 字节流、尺寸、信号 可靠有序

会话优先使用直连 candidate,并同时支持由边缘 Allocator 签发的短期 TURN candidate。公网模式通过 Rendezvous 转发信令;本地模式可以直接连接 Agent。两种模式共享相同设备身份、Session Ticket 和端到端 DTLS-SRTP。

5.2 差网络自适应

WebRTC 内置拥塞控制负责估算可用带宽,RemoteDesk 在其上增加会话策略:

  • 使用 TWCC/GCC、RTT、抖动、NACK、PLI 和丢包 EWMA 判断链路等级。
  • 协商 NACK/RTX;FEC 仅在双方支持且收益高于带宽成本时启用。
  • 丢包上升时依次降低视频码率、帧率和分辨率,恢复时缓慢升档并设置迟滞。
  • Opus 音频可启用 in-band FEC/DTX,优先级高于视频。
  • 键盘、鼠标按键和控制消息可靠发送;高频鼠标移动只保留最新状态。
  • 剪贴板和文件传输限流,严重丢包时暂停,不能阻塞输入。
  • 解码端丢弃过时视频帧,连续解码失败时请求关键帧。

策略不以单次丢包采样立即切档,而使用短期/中期滑动窗口和恢复迟滞,避免画质频繁震荡。详细规则见 差网络自适应设计

5.3 会话状态机

Idle
  -> Resolving
  -> Authenticating
  -> Negotiating
  -> Connected
  -> Reconnecting
  -> Connected | Failed | Closed

所有 Adapter 必须映射到同一状态机和错误类型,UI 不根据协议拼接错误字符串。

5.4 编码策略

  • 默认 H.264,优先硬件编码,保证最大兼容性。
  • HEVC 和 AV1 仅在双方能力协商成功时启用。
  • 办公模式优先清晰度和文字边缘,限制静态画面的无效刷新。
  • 动态模式优先低延迟和帧率。
  • 分辨率变化应触发编码器重配置,不重建整个会话。
  • 严格模式下硬件编码器或 GPU surface 导入不可用时拒绝图形会话;软件编码仅允许在用户显式开启的兼容模式中使用。

5.5 零拷贝边界

Linux Wayland 的默认发送路径为 PipeWire DMA-BUF -> GPU conversion/scale -> hardware encoderXorg 默认尝试 XComposite pixmap -> DRI3 DMA-BUF -> hardware encoder。Windows 默认接收路径为 hardware decoder -> GstD3D11Memory -> d3d11videosink。两端原始像素都不得经过 Rust Vec<u8>、GStreamer appsink、JavaScript、Tauri IPC 或其他 CPU 映射。

压缩后的 H.264/HEVC/AV1 码流、RTP 包和控制消息进入普通内存不属于原始帧复制。默认策略 required_end_to_end 要求 Agent 和客户端 helper 都报告经过验证的 zero_copy,否则返回 CAPABILITY_ZERO_COPY_UNAVAILABLE。跨 GPU Adapter、PipeWire memory frame、XShm/XImage、软件编解码、WebView2 opaque path 和 IronRDP CPU bitmap 均不满足该策略。用户只有显式选择 compatibility 才允许这些路径。

6. 认证与安全

6.1 RDP

  • 默认启用 NLA 和 TLS。
  • 首次连接不要求用户配置或确认服务器证书指纹。
  • 证书变化必须阻断自动连接并提示用户重新确认。
  • 密码只保存到系统密钥库,SQLite 仅保存凭据引用。
  • 磁盘、剪贴板、麦克风重定向按连接配置单独授权。

6.2 Linux Agent

  • 首次配对由 Agent 生成一次性短码和设备公钥。
  • 客户端与 Agent 交换长期设备公钥,配对码只用于确认本次配对。
  • 后续使用双向设备认证和 TLS 1.3。
  • Agent 保存允许连接的客户端公钥,可本机撤销。
  • 会话开始和结束必须有可见系统通知。
  • 日志不得记录密码、剪贴板内容、文件内容或配对私钥。

6.3 本地数据

SQLite 保存:

  • 主机名、地址、端口和协议类型。
  • 显示、音频、剪贴板和质量选项。
  • 证书指纹、Agent 设备 ID 和密钥库引用。
  • 最近连接时间与非敏感错误摘要。

数据库不保存明文密码或私钥。

7. 用户体验

主界面直接展示远程主机列表,不设置营销首页。核心工作流:

  1. 新建主机,选择“Windows RDP”或“Linux Agent”,Linux 可选自动桌面、Wayland、Xorg 或命令行模式。
  2. 执行能力检测并给出可用通道。
  3. 点击连接并等待目标服务就绪。
  4. 进入独立会话窗口,工具栏默认自动隐藏。
  5. 断开后返回原主机条目,并保留可诊断的结束原因。

会话工具栏提供显示器、分辨率、全屏、本地缩放、画质、剪贴板、文件传输、统计和断开按钮。分辨率和本地缩放是两个独立设置:前者控制 RDP 会话尺寸或 Linux 编码尺寸,后者只控制客户端如何把收到的画面放入窗口。危险操作如发送安全注意序列、重启远端应放入菜单并二次确认。

分辨率支持以下模式:

  • 自动跟随:默认。窗口稳定后发送新的目标尺寸,并使用防抖避免拖动时反复重配。
  • 原始分辨率:保持远端显示器/桌面源尺寸,客户端只做本地缩放。
  • 固定档位:720p、1080p、1440p、4K,以及经过能力校验的自定义宽高。

RDP 通过 Display Control/动态分辨率修改远程会话桌面;不支持时可提示重连后应用初始尺寸。Linux Wayland/Xorg 默认保持实体桌面模式不变,只在捕获与编码之间缩放。这样调整串流清晰度不会改变被控端显示器布局、打乱本地窗口或触发合成器权限问题。

8. 性能目标

首个稳定版本的工程目标:

场景 目标
办公静态桌面 文字清晰、低带宽、无持续满帧编码
1080p 动态画面 60 fps,局域环境端到端延迟可感知但不拖沓
4K 办公 30/60 fps 可配置,优先硬件编解码
输入 不因视频帧阻塞输入发送
重连 短时中断自动恢复,超过阈值明确失败

实际延迟与显卡、编码器、桌面合成器和网络条件有关,测试报告必须记录完整环境,不能只给出单个延迟数字。

9. 可观测性和诊断

  • 客户端提供可导出的结构化诊断包。
  • 诊断包包含版本、能力、会话状态转换和非敏感错误。
  • Native 会话记录 RTT、抖动、丢包、码率、编码/解码时间和丢帧。
  • RDP 会话记录协商能力、重定向通道、IronRDP 错误和 UDP multitransport 状态。
  • 日志默认滚动保存并限制大小。
  • 导出前对用户名、地址和路径进行可选脱敏。

10. 测试矩阵

最低覆盖:

  • Windows 11 Pro 与 Windows Server 的 RDP。
  • Ubuntu/Fedora 当前稳定版 GNOME Wayland 上的自研 Agent。
  • KDE neon 或主流发行版 Plasma 6 Wayland 上的自研 Agent。
  • 至少一种 GNOME/KDE Xorg 会话。
  • 无图形环境的 Debian/Ubuntu Server 命令行会话。
  • NVIDIA、AMD 和 Intel 至少各一种硬件编解码环境。
  • 单显示器、双显示器、HiDPI 和动态分辨率。
  • 中文输入、剪贴板、大文件和音频。
  • 证书变化、错误密码、锁屏、休眠、VM 重启和断网重连。

测试分层为核心状态机单元测试、协议适配集成测试、虚拟机端到端测试和长时间稳定性测试。

11. 主要风险

风险 应对
IronRDP 关键 RDP 能力不足 M0 设置功能门槛,保留 mstsc 隔离回退 Adapter
WebView2 GPU 无法证明内部 surface 路径 Linux 桌面默认使用原生 video helperWebView2 仅显式兼容模式
驱动或桌面不提供可导入的 GPU surface 默认拒绝图形会话;用户显式启用 compatibility 后才允许回退
Tauri 与原生 helper 生命周期不一致 Named Pipe heartbeat、generation、独立崩溃恢复和幂等关闭
Web 输入无法覆盖系统组合键/Raw Input 系统组合键走 Rust command,高要求场景切换原生 helper
差网络下视频震荡或输入阻塞 WebRTC GCC + 分层退化 + 有界独立队列 + 恢复迟滞
Wayland Portal 行为随桌面变化 维护能力探测和发行版测试矩阵
Xorg 捕获权限配置错误 只使用目标用户 Xauthority,禁止关闭 X 访问控制
PTY 远程终端扩大权限面 PAM 建立会话、强制降权、设备权限独立控制
无人值守 Linux 缺乏统一接口 首次授权后尝试 Portal 恢复令牌,登录界面单独适配
硬件编码器兼容性 默认拒绝非零拷贝会话;显式 compatibility 才允许 H.264 软件回退
输入法和键盘布局错误 统一物理键与 Unicode 输入模型,专项测试中英文布局
IronRDP/GStreamer 等依赖安全更新 固定版本、生成 SBOM、建立升级回归测试