Files
RemoteDesk/docs/technology-stack.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

16 KiB
Raw Permalink Blame History

RemoteDesk 技术栈规划

当前决策:所有发布运行时使用 Rust;Agent 是服务端;原生客户端使用 winit、wgpu 和 egui;实时传输使用 str0m WebRTC。Go、Hysteria2、 Tauri 和 WebView 不属于目标组件。详见 ADR 0001。下文保留的旧技术栈内容仅用于 迁移对照,后续随代码迁移删除。

1. 混合架构

RemoteDesk Windows 客户端采用 Web UI 与 Rust 原生会话进程混合方案。JavaScript 负责管理界面和终端;Linux 桌面 WebRTC、Windows RDP 和 GPU 呈现均由 Rust 原生 helper 承载,原始视频像素不进入 WebView。

模块 技术栈 职责
Client Shell Tauri 2、Rust、Tokio 应用生命周期、凭据、配对、窗口和 helper 管理
Management UI React、TypeScript、Vite 主机、设置、诊断和会话工具栏
Linux Terminal xterm.js、DataChannel ANSI/VT 终端和输入
RDP Protocol Probe Rust helper、IronRDP 无凭据检查 DNS、TCP、X.224 与 RDP 安全协议协商
Native RDP Viewer Rust helper、IronRDP、winit、D3D11/DXGI Windows RDP 原生窗口;优先使用 D3D11 swap chain CPU 上传,失败时回退 software framebuffer
Native Linux Video Rust helper、gstreamer-rs、D3D11 默认 WebRTC、硬解和零拷贝呈现
Linux Agent Rust、Tokio、GStreamer、systemd Wayland/Xorg/PTY 被控端
协议 Protobuf、TLS WebSocket、WebRTC 配对、会话和媒体数据
Edge Rust Rendezvous/Allocator、双 POP TURN、L4 Connector CDN 优质骨干路径
数据 SQLite、Windows Credential Manager 配置和敏感凭据

2. 进程模型

remotedesk.exe (Tauri/Rust)
├── WebView2 main window
├── WebView2 terminal window
├── Rust session/auth/storage/edge services
│
├── remotedesk-rdp-session.exe
│   └── IronRDP protocol + TLS certificate probe (no credentials)
│
├── remotedesk-rdp-viewer.exe
│   └── IronRDP + winit native window + D3D11 CPU upload fallback
│
└── remotedesk-native-video.exe (default Linux desktop)
    └── GStreamer WebRTC + selected D3D11 adapter

原生 helper 是独立进程,原因:

  • Tauri/WebView2 与 winit/D3D11 不共享事件循环。
  • RDP 或 GPU driver failure 不拖垮主界面。
  • helper 可以拥有独立 GPU、DPI、Raw Input 和全屏生命周期。
  • 主进程只交换状态和命令,不传输视频帧。

3. Tauri Client Shell

3.1 Rust Core

  • Tauri 2 application runtime。
  • Tokio 处理信令、配对、Edge 和 helper IPC。
  • rusqlite 保存 HostProfile 与非敏感设置。
  • Windows Credential Manager 保存密钥、密码和 Token。
  • reqwest/rustls 调用边缘控制面。
  • tracing 输出结构化日志。

Tauri command 使用最小 capability allowlist。JavaScript 不能直接读取 Credential Manager、任意文件、Agent 长期私钥。

3.2 React UI

  • React + TypeScript + Vite。
  • 状态按 Host/Profile/Session/Diagnostics 拆分。
  • 图标使用成熟图标库,不在业务代码中手写 SVG。
  • 页面资源全部本地打包,不加载 CDN 脚本或远端网页。
  • Release 禁用开发者工具、任意导航和不必要 Web API。

UI 只接收结构化状态,不接收密码、长期私钥或媒体帧。

5. Linux Terminal

  • xterm.js 负责 ANSI/VT、Unicode、中文宽字符、选择和滚动区。
  • PTY 字节通过可靠 DataChannel 直接进入页面。
  • 大量输出有有界缓存和背压。
  • 多行粘贴前确认。
  • Alternate Screen 与普通历史分离。
  • JS 不解析或拼接远端 shell 命令,只呈现 PTY 字节。

xterm.js 性能通过高频输出 Spike 验证;若无法满足,再启用 Rust 原生终端 helper,不影响协议。

6. Native RDP Helpers

6.1 当前实现

  • Rust stable。
  • remotedesk-rdp-session.exe:从有界 stdin JSON 接收目标地址,完成 DNS、TCP、X.224、RDP Negotiation Confirm 和无凭据 TLS 证书交换,不接收账号密码,也不进入 CredSSP/NLA。
  • remotedesk-rdp-viewer.exe:使用 IronRDP 与 winit 建立单屏或全部本地显示器原生会话;账号和密码只在 helper 的本地遮罩提示中输入。
  • viewer 的桌面帧留在原生进程,不进入 React、Tauri IPC 或浏览器。
  • 当前 viewer 优先报告 d3d11_cpu_upload,并通过独立能力 d3d11_dirty_rect_upload 声明局部上传;D3D11/DXGI 创建失败时报告 software_framebuffer。单屏支持窗口尺寸驱动的动态分辨率,全部本地显示器使用 Win32 geometry 和 RDPEDISP 多 monitor layout,自定义显示器子集仍切换系统 mstsc

6.2 目标渲染路径

渲染路径:

IronRDP dirty rect
 -> D3D11 upload/merge
 -> GPU texture
 -> native swap chain

IronRDP 关键能力不足时,主程序启动 mstsc 回退;mstsc 不伪装成内嵌原生 helper。

当前已完成 D3D11 device、持久 CPU framebuffer 与完整 GPU frame texture、dirty region 紧凑像素转换、多个待呈现 dirty rect 合并、局部 UpdateSubresource 和 swap chain 呈现。为保证 discard swap chain 下未变化区域正确,每次呈现仍从完整 frame texture 复制到 back buffer。因此该路径继续报告 d3d11_cpu_upload,不得报告硬件解码或零拷贝。

6.3 Helper IPC

主程序与 helper 使用 Windows Named Pipe

  • Pipe 使用当前用户 SID 的严格 ACL。
  • RDP viewer 启动时命令行只接收随机 pipe name;一次性 32 字节 bootstrap key 只进入目标子进程环境并在读取后清除。
  • 控制服务校验 pipe API 返回的客户端 PID、进程创建时间、实际可执行文件路径和 SHA-256 build hash,再完成 HMAC-SHA256 challenge。
  • viewer 严格校验版本化配置后返回载荷 SHA-256 与 challenge 绑定的 HMAC ACK;服务端验证版本、摘要和 MAC 后才确认配置已接受。
  • 连接、challenge/auth、配置帧和 ACK 共用有界绝对 deadline;非阻塞分段读取支持正常短读,但不会因已连接 helper 停滞而永久占用握手线程。
  • viewer 启动后立即加入设置 KILL_ON_JOB_CLOSE 的独立 Job Object;控制服务退出或握手失败时由 Job 终止遗留进程。
  • 当前 RDP 管道发送证书指纹、opaque 凭据引用和连接参数,密码仍由 viewer 直接从 Credential Manager 读取;Session Ticket 后续复用同一有界协议。
  • IPC 只传控制、状态、指标和输入命令,不传视频 frame。
  • helper crash 后主程序显示结构化错误并可重启。

7. Native Linux Video Helper

这是 Linux 桌面默认客户端媒体进程,负责 WebRTC、DataChannel、Raw Input、硬件解码和呈现。默认选择窗口显示器所属 Adapter,用户也可手动指定。

当前 helper 的 Windows 源码除 D3D11 device 和 H.264 decoder allocation probe 外,还提供有界本地文件解码及原生窗口播放入口。它使用启用 DXVA/hardware transforms 且绑定 D3D11 device manager 的 Media Foundation Source Reader,将 H.264 MP4/M4V/MOV 请求为 NV12;输出必须是同一 device 上的 IMFDXGIBuffer texture,系统内存 buffer 会失败。播放器跟踪媒体类型的可见尺寸和变化标志,按 100 ns 时间戳调度 texture subresource,以同 device ID3D11VideoProcessor 转换和缩放到 BGRA swap chain,黑边由 render target 清除,过程中不映射 CPU frame。该链路提交真实压缩样本并实际调用窗口呈现,但在 ETW/GPUView 尚未证明 video decode engine 和无隐藏 copy 前仍报告 hardware_decode_verified=falsezero_copy_verified=false

Linux 桌面控制 helper 的 Windows 模块还将远端 H.264 RTP 按 RFC 6184 重组为有界 Annex-B AU,覆盖单 NAL、STAP-A 和 FU-A。跨帧序号中断或本地一槽邮箱积压会清除参考链、请求 PLI 并等待新关键帧;重连时销毁旧解码器。AU 优先以 MFVideoFormat_H264_ES 输入 D3D11-aware H.264 MFT,只接受同一 device 的 NV12 IMFDXGIBuffer,再复用 VideoProcessor/swap-chain 模式呈现到 Linux 桌面窗口。第一帧呈现后,协议 minor 9 在完整 zlib 帧 ACK 后确认 H.264-only;随后每个 sequence 只在 D3D11 呈现成功后 ACK,Agent 才捕获下一帧,不再执行同帧 zlib 编码和传输。初始化、解码、媒体、sender/encoder 或 ACK 失败时恢复 zlib 软件 surface。该远程链路尚未编译和实机验证,不能据此打开 linux.native_video

管线:

gstreamer-rs webrtcbin
 -> D3D11 hardware decoder on selected adapter
 -> GstD3D11Memory
 -> d3d11videosink
 -> native HWND

decoder、sink 和 swap chain 必须位于同一 Adapter;验证到 CPU map 或跨 Adapter copy 时,required_end_to_end 协商失败。该 helper 与 RDP helper 共享 WindowHost、GPU selection、IPC 和诊断 crate,但不共享协议 Adapter。

8. Linux Agent

agent/
└── crates/
    ├── agentd
    ├── desktop-session
    ├── shell-session
    ├── agent-core
    ├── agent-media
    ├── wayland-backend
    ├── xorg-backend
    └── platform-linux

主要技术:Tokio、Axum/rustls、zbus/ashpd、gstreamer-rs、x11rb、rustix/nix、prost、rusqlite 和 tracing。

Wayland 使用 Portal/PipeWire/libeiXorg 默认使用 XComposite/DRI3/XDamage/XRandR/XTestXShm 仅兼容模式;终端使用 PAM/PTY。unsafe FFI 必须隔离在专用 crate。

Linux 视频内存策略:

preferred:
PipeWire memory:DMABuf
 -> GPU color convert/scale
 -> VA-API or NVENC hardware encoder
 -> RTP

explicit compatibility fallback:
PipeWire MemPtr / XShm
 -> CPU or GPU upload
 -> encoder
 -> RTP

Agent 媒体核心只传递带所有权和 fence 的 surface handle,不提供接收原始 RGBA Vec<u8> 的通用接口。禁止用 appsink 拉取原始帧再送入编码器。每种 GStreamer/驱动组合必须通过 tracer、perf 和 GPU 工具证明没有 CPU map/copy 后,才能把会话标记为 zero_copy

Agent 默认从 DMA-BUF/DRI3 元数据解析 DRM render node,并在同一设备上选择 VA-API/NVENC video processor 和编码器。服务端手动 GPU 只有在能直接导入该 surface 时可用;跨 GPU 选择不满足严格模式。

9. 协议与 Edge

  • Protobuf/Buf 定义共享消息契约。
  • Rust 端使用 prostWeb UI 使用生成的 TypeScript codec。
  • TLS WebSocket 负责配对和信令。
  • WebRTC RTP/RTCP 负责媒体和质量反馈。
  • IronRDP 原生会话把 RDP NetworkCharacteristicsResult 中服务器测得的平均 RTT、基础 RTT 和带宽提升为脱敏诊断事件;不以帧间隔推算网络时延。
  • IronRDP 图形事件记录网络 PDU 在本地 active stage 的处理与 framebuffer 像素转换耗时;本地事件保持空值,且该指标不代表服务端编码或 GPU 视频解码。
  • DataChannel 负责输入、终端、控制、剪贴板和文件。
  • 智能比较 Direct、Single Edge 和 Dual Edge。
  • Linux WebRTC 使用双 POP TURNWindows RDP 使用透明 L4 Connector。

TURN credentials、SDP 和 Session Ticket 通过受限 Named Pipe 交给 native-video helper;长期设备私钥、Agent 授权和短期会话材料均不进入 JavaScript。

10. GPU 选择

配置模式:

inherit global
default
manual(adapter identity)
  • RDP helper:默认和手动均可精确控制 DXGI Adapter。
  • Linux native default:选择窗口显示器所属 Adapter,使 decoder、sink 和输出保持同 GPU。
  • Linux manualnative-video helper 使用指定 LUID;窗口不在该 Adapter 的 output 上时严格模式拒绝跨 GPU copy。
  • 指定 GPU 缺失时本次回退默认,不修改用户配置。
  • 诊断展示 requested/effective GPU、decoder、driver 和 copy path。
  • 零拷贝策略为默认 required_end_to_end 或显式 compatibility;前者要求 Agent/client 两端同时验证通过。

11. 分辨率实现

  • Tauri/React:提供自动跟随、原始、固定预设和自定义尺寸菜单,保存主机级策略。
  • IronRDP helper:把尺寸请求映射到 Display Control Dynamic Monitor Layout;不支持时仅在确认后重连。
  • Linux Agent:使用 VA-API/CUDA 等 GPU video processor/scaler 和 DMA-BUF caps 在编码前缩放,不使用可能落入 CPU raw frame 的通用 videoscale,也不改变 Wayland/Xorg 实体显示模式。
  • Native renderer:按收到的实际编码尺寸重建呈现资源,保持等比缩放并正确换算黑边输入坐标。
  • Protobuf:传递 capability、request/applied、generation 和 source/encoded size,防止乱序 resize 覆盖新状态。

自动跟随窗口必须防抖;固定尺寸默认允许弱网临时下调,也可由用户锁定。所有模式都受双方最大纹理尺寸、编码器像素率和资源预算限制。

12. 仓库结构

RemoteDesk/
├── Cargo.toml
├── rust-toolchain.toml
├── client/
│   ├── src-tauri/
│   ├── web/
│   └── helpers/
│       ├── rdp-session/
│       └── native-video/
├── agent/
├── shared/
│   ├── protocol/
│   ├── session/
│   ├── helper-ipc/
│   └── diagnostics/
├── services/
│   ├── rendezvous/
│   └── relay-allocator/
├── protocol/
├── packaging/
└── tests/

13. 构建

Rust

  • Cargo workspace、Cargo.lockrust-toolchain.toml
  • rustfmt、Clippy、cargo-deny 和 cargo-audit。

Web

  • pnpm lockfile。
  • TypeScript strict mode。
  • ESLint、Vitest 和 Playwright。
  • Vite 构建为本地静态资源。

Windows

  • Tauri/WiX MSI。
  • 安装主程序、native helpers 和必要 GStreamer runtime。
  • WebView2 Evergreen Runtime 作为前置条件。
  • 主程序、helper 和安装包全部签名。

Linux

  • Cargo + 发行版 GStreamer/PipeWire/PAM/X11/libei。
  • DEB/RPM 与 systemd units。

14. 测试

层级 工具
Rust cargo test、proptest
React/TypeScript Vitest、Testing Library
WebRTC 页面 Playwright + synthetic MediaStream
RDP IronRDP + Windows VM
GPU GPUView/ETW、GStreamer tracer
Agent Cargo integration、真实 Wayland/Xorg
Edge coturn、多 POP/netem
端到端 pytest + 虚拟化测试机

Web 视频测试必须证明 frame 不经过 JS/Canvas:页面不注册逐帧像素处理,Chromium media internals 显示硬解路径,CPU/拷贝指标符合基线。Linux Agent 同时使用 GStreamer tracer、perf 和驱动工具验证 DMA-BUF 未被 CPU map;仅看到 Video Encode 引擎占用不能作为零拷贝证据。

15. 安全

  • WebView 使用严格 CSP:本地资源、必要的 media/connect source,禁止任意导航。
  • Tauri capability 按窗口最小授权。
  • Release 禁用 devtools 和远端脚本。
  • Web 页面不获取长期私钥或 RDP 密码。
  • Native helper IPC 使用 Named Pipe ACL、一次性 token 和进程验证。
  • TURN/SDP/session material 限时并在窗口关闭后销毁。
  • 依赖生成 SBOMnpm 与 Cargo 同时审计。

16. 许可证

  • Tauri、React、xterm.js、IronRDP 和 Rust crates 进入许可证 allowlist。
  • WebView2 Runtime 遵循 Microsoft 分发条款。
  • GStreamer 插件逐项审计,不默认捆绑 GPL x264。
  • 每个安装包附带第三方许可证和机器可读 SBOM。

17. M0 门槛

  1. Native-video helper 证明 D3D11 decoder 到 d3d11videosink 无 CPU/cross-GPU copy。
  2. Linux 输入、DataChannel 和系统组合键回退正确。
  3. xterm.js 通过中文、全屏 TUI 和高频输出测试。
  4. Native RDP helper 的 IronRDP/NLA/D3D11 通过。
  5. Named Pipe IPC 的 ACL、崩溃和重启通过。
  6. 默认显示器 GPU 与手动 GPU 的 native-video 零拷贝路径通过。
  7. Direct/Single/Dual Edge 路径通过。
  8. Wayland DMA-BUF 和 Xorg DRI3 到硬件编码器的零拷贝路径通过;任一端回退时默认拒绝。

18. 明确不做

  • 不把视频 frame 通过 Tauri IPC 传给页面。
  • 不用 Canvas/WebGL/WebGPU 逐帧绘制 Linux 远程视频。
  • 不在 Web UI 中实现 RDP 协议。
  • 不从零实现 RDP、WebRTC、TURN、编解码或密码学。
  • 不把 mstsc 回退描述为内嵌 RDP。

该方案用 Web 平台降低管理 UI 和终端开发成本,用 Rust 原生 helper 承载所有默认桌面视频、RDP、精确 GPU、Raw Input 和驱动恢复能力。