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
328 lines
16 KiB
Markdown
328 lines
16 KiB
Markdown
# RemoteDesk 技术栈规划
|
||
|
||
> 当前决策:所有发布运行时使用 Rust;Agent 是服务端;原生客户端使用
|
||
> winit、wgpu 和 egui;实时传输使用 str0m WebRTC。Go、Hysteria2、
|
||
> Tauri 和 WebView 不属于目标组件。详见
|
||
> [ADR 0001](adr/0001-all-rust-runtime.md)。下文保留的旧技术栈内容仅用于
|
||
> 迁移对照,后续随代码迁移删除。
|
||
|
||
## 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. 进程模型
|
||
|
||
```text
|
||
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 目标渲染路径
|
||
|
||
渲染路径:
|
||
|
||
```text
|
||
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=false`、`zero_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`。
|
||
|
||
管线:
|
||
|
||
```text
|
||
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
|
||
|
||
```text
|
||
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/libei;Xorg 默认使用 XComposite/DRI3/XDamage/XRandR/XTest,XShm 仅兼容模式;终端使用 PAM/PTY。unsafe FFI 必须隔离在专用 crate。
|
||
|
||
Linux 视频内存策略:
|
||
|
||
```text
|
||
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 端使用 prost,Web 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 TURN;Windows RDP 使用透明 L4 Connector。
|
||
|
||
TURN credentials、SDP 和 Session Ticket 通过受限 Named Pipe 交给 native-video helper;长期设备私钥、Agent 授权和短期会话材料均不进入 JavaScript。
|
||
|
||
## 10. GPU 选择
|
||
|
||
配置模式:
|
||
|
||
```text
|
||
inherit global
|
||
default
|
||
manual(adapter identity)
|
||
```
|
||
|
||
- RDP helper:默认和手动均可精确控制 DXGI Adapter。
|
||
- Linux native default:选择窗口显示器所属 Adapter,使 decoder、sink 和输出保持同 GPU。
|
||
- Linux manual:native-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. 仓库结构
|
||
|
||
```text
|
||
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.lock`、`rust-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 限时并在窗口关闭后销毁。
|
||
- 依赖生成 SBOM,npm 与 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 和驱动恢复能力。
|