Files
BetterDesk/rdclient-desktop
UNITRONIX 34ef01e17d fix(security): harden TLS defaults, device auth, and update privileges
Require stricter client/server TLS options, centralize device auth on agent APIs, constrain Linux privileged update helpers, and harden filebrowser path checks with supporting audit notes.

Thanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
2026-08-15 23:28:03 +02:00
..

BetterDesk RdClient Desktop

Tauri v2 desktop shell for the RdClient operator UI. The app loads your panels /remote dashboard in the main window and opens each remote session in a separate window (/remote/:deviceId), similar to RustDesk.

This is production-ready Phase C+ of the RdClient roadmap: server validation, LAN discovery, settings/reset, encrypted peer passwords, full panel i18n, codec fallbacks on Linux, and generator-built installers with embedded panel URL.

Prerequisites

  • Node.js 18+ (for @tauri-apps/cli)
  • Rust stable (1.77+; tested with Fedoras toolchain)
  • Linux: webkit2gtk-4.1, libayatana-appindicator3, librsvg2, patchelf (Tauri Linux deps)

Platforms

OS Support
Windows Native Tauri / WebView2
Linux X11 Native GTK + WebKitGTK
Linux Wayland Native Wayland with automatic WebKit workarounds (KDE, GNOME, NVIDIA)

Session detection and GDK/WebKit env setup run in src-tauri/src/linux_display.rs before GTK starts (also available via scripts/rdclient-launcher.sh for packaged installs).

TLS (HTTP / self-signed / Let's Encrypt)

RdClient targets self-hosted operator panels. HTTPS certificate validation is enabled by default and uses the operating system trust store. Plain HTTP remains available for explicit local/development deployments but must not be used for production or hostile networks.

Platform Mechanism
Linux System certificate validation by default; the bundled WebKit policy is only relaxed when strict TLS is explicitly disabled
Windows WebView2 certificate validation by default; --ignore-certificate-errors is only used when strict TLS is explicitly disabled

Strict validation (system trust store only):

BETTERDESK_TLS_STRICT=1 npm run dev

Disable strict mode only for a deliberate development/self-signed exception, and never use that exception for production credentials or WAN access.

Linux troubleshooting (Gdk error 71 / Wayland)

If the window fails to open on Wayland with:

Gdk-Message: Error 71 (Protocol error) dispatching to Wayland display.

the app already applies common fixes automatically. You can override:

Variable Effect
BETTERDESK_UI_BACKEND=x11 Force XWayland / X11 (GDK_BACKEND=x11)
BETTERDESK_UI_BACKEND=wayland Force native Wayland
BETTERDESK_WEBKIT_DISABLE_DMABUF=1 Set WEBKIT_DISABLE_DMABUF_RENDERER=1 (disables GPU compositing — use if blank window / flicker)
BETTERDESK_WEBKIT_NO_MEDIA_ACCEL=1 Skip GStreamer VA-API hints for WebKit media decode
BETTERDESK_WEBKIT_NO_WORKAROUND=1 Skip all automatic WebKit/GDK tweaks
BETTERDESK_WEBKIT_NO_COMPOSITING=1 Set WEBKIT_DISABLE_COMPOSITING_MODE=1 (last resort)

NVIDIA + Wayland: the binary sets __NV_DISABLE_EXPLICIT_SYNC=1 when /proc/driver/nvidia/version exists and keeps DMA-BUF enabled for GPU compositing. NVIDIA + X11: DMA-BUF is disabled automatically (blank-window workaround).

GPU / codecs: Wayland keeps WebKit DMA-BUF/GPU compositing enabled by default. Vendor-specific VA-API drivers are auto-selected (Intel iHD, AMD radeonsi, NVIDIA when nvidia-vaapi-driver is installed). GStreamer ranks hardware decoders (vaav1dec, vah264dec, …) above software. WebView2 on Windows enables GPU rasterization, AV1/HEVC HW decode, and WebCodecs.

Linux packages (recommended for AV1 / smooth remote):

GPU Packages (Fedora example)
Intel libva-intel-media-driver, gstreamer1-vaapi, gstreamer1-plugins-bad-free
AMD mesa-va-drivers, gstreamer1-vaapi, gstreamer1-plugins-bad-free
NVIDIA nvidia-vaapi-driver (optional VA-API), gstreamer1-plugins-bad-free

Verify VA-API: vainfo (should list AV1/H264/VP9 profiles). WebKitGTK 2.44+ and GStreamer 1.24+ are required for WebCodecs + DMA-BUF zero-copy.

If the window is blank or flickers, set BETTERDESK_WEBKIT_DISABLE_DMABUF=1.

Examples:

# Force X11 session (works on XWayland)
BETTERDESK_UI_BACKEND=x11 npm run dev

# Wayland with extra-safe WebKit flags
BETTERDESK_WEBKIT_DISABLE_DMABUF=1 npm run dev

Quick start

cd rdclient-desktop
npm install
npm run dev

Clean rebuild (no Cargo cache)

If Connect still does nothing after pulling changes, force a full rebuild:

cd rdclient-desktop
./scripts/rebuild-clean.sh

Production bundle instead of dev:

./scripts/rebuild-clean.sh build

Important: the dashboard HTML/JS is loaded from your panel URL (/remote). Updating only the desktop binary is not enough for UI tweaks — run Settings → Updates on the panel (or deploy web-nodejs) so /js/remote-dashboard.js is current. The desktop shell also injects a Connect bridge, so Connect works even before the panel JS update once you run a freshly built binary.

  1. On first launch, enter your panel base URL (e.g. https://desk.example.com) or pick a server from LAN discovery.
  2. Sign in at /remote/login when prompted (same as the web RdClient).
  3. Use Connect on a device — the desktop opens a new window instead of a browser tab.
  4. Open Settings (gear icon in the dashboard header) to change URL, TLS mode, language, sign out, or Reset client (clears config, cookies, and saved passwords).

File transfer (desktop)

Remote sessions use the panels RustDesk FILE_TRANSFER channel (toolbar File transfer button). Prefer this channel for large files and folder trees — it uses a dedicated relay so the desktop video session stays interactive. Cliprdr copy/paste remains convenient for small Explorer transfers, not multiGB bulk moves.

Action Desktop behaviour
Upload files Drag files onto the modal or remote pane; click the drop zone for a native multi-file picker; optional Choose folder to browse local directories
Upload folders Double-click / context menu Upload folder, or drop a folder path onto the modal — expands the tree, creates remote dirs, uploads files sequentially under one queue job
Download files Streaming Save as — blocks are appended to disk (no full-file buffer in the WebView)
Download folders Context menu / double-click Download folder — pick (or use) a local destination, walk remote read_dir, mkdir, stream each file
Queue Folder jobs show overall % + current file name; Cancel stops the active child and remaining items. One folder job runs at a time (others queue)
Performance Chunk IPC uses base64 (not JSON number arrays); download writes are batched; queue UI is throttled. Rebuild desktop and update panel together after FT changes
Protocol Same RDFileTransfer / dedicated file relay as the web RdClient — browse, upload, download, overwrite prompts

Rebuild the desktop binary after pulling rdclient-desktop changes. Deploy or update the panel so /js/rdclient/local-files.js, filetransfer.js, and file-modal.js are current on your server.

Cliprdr file paste (desktop, Windows)

Explorer Copy / Ctrl+C on either side → focus the other → Ctrl+V uses RustDesk Cliprdr (same path as the native RustDesk client), separate from the file-transfer modal. Dragging files onto the session window also registers them for remote paste (native Tauri drop paths).

Direction Behaviour
Local → remote (copy/paste) Windows CF_HDROP paths read natively; file tree expanded into FILEGROUPDESCRIPTORW PDUs; peer Ctrl+V pulls chunks
Local → remote (drag-drop) OS drop onto the session window → Cliprdr FormatList → click under the cursor → synthetic Ctrl+V (not shell DnD into a folder HWND). Open the File transfer modal first to upload into a chosen remote folder instead
Remote → local (drag-out) Drag a file on the remote toward the edge of the RdClient window (keep the button down). RdClient cancels the remote Explorer drag, sends Ctrl+C, downloads via Cliprdr, then starts a local OLE drag so you can drop on Desktop/Explorer. Plain Copy → Paste also works
Remote → local (copy/paste) Peer FormatList → RdClient requests descriptor + file bytes into a temp dir → CF_HDROP on the local clipboard for Explorer paste
Cliprdr performance File bytes move in chunks over the shared session relay (same as RustDesk). RdClient uses base64 IPC + UI yields so video/heartbeat keep running during modest copy/paste. Selections over ~300 entries or ~200MB are refused for Cliprdr (toast steers you to File transfer) so remote Explorer does not freeze on Paste. For large trees always use toolbar File transfer — dedicated FILE_TRANSFER connection
Drag-drop plumbing Native tauri://drag-drop paths (do not use disable_drag_drop_handler — HTML5 drops lack paths in WebView2)
Sync trigger Window focus / click in the viewer, ~1.5s poll while streaming, or native file drop
Text race guard When CF_HDROP is present, focus sync skips text clipboard push so path-as-text cannot wipe file formats on the peer
File transfer modal Open modal → drop files on the remote pane or drop zone → uploads via desktop_open_paths

Requires a rebuilt desktop binary and panel JS (cliprdr.js, updated client.js / protocol.js / remote.js / desktop-dnd.js). Linux/macOS Cliprdr is not implemented yet.

Environment & embedded URL

Source Purpose
BETTERDESK_SERVER_URL Auto-configure panel URL before setup UI
betterdesk-rdclient.json next to the binary Installer-embedded { "server_url": "https://…" } from Generator
UDP / mDNS LAN discovery Setup UI lists panels on the local network

Password storage

  • Operator login (“Remember me”): RdClientSecureStore in IndexedDB (AES-GCM).
  • Device password (“Remember device password”): same vault, per deviceId (peer:{id} keys). Passwords never leave the device.
  • Reset client: clears config.json, WebView cookies/storage, and the vault.

LAN discovery

The panel publishes itself via UDP (port 21119, always on) and optionally mDNS _betterdesk._tcp when bonjour-service is installed and PANEL_MDNS is not off.

Build

npm run build

Installers/binaries are under src-tauri/target/release/bundle/.

How it works

Piece Role
src/setup.html First-run: LAN discovery list + manual panel URL
src/settings.html Local settings: URL, TLS, language, sign out, reset
src-tauri/src/lib.rs Tauri commands: probe, discover, settings, sign out, reset, sessions
src-tauri/src/config.rs Persists extended config + embedded/env URL helpers
src-tauri/src/server_probe.rs Validates panel via /api/bd/server-info
src-tauri/src/discovery.rs UDP LAN browse (BetterDesk announce protocol)
src-tauri/src/linux_display.rs Linux X11/Wayland session + WebKitGTK workarounds
src-tauri/src/tls_policy.rs Windows WebView2 + strict-mode env
scripts/rdclient-launcher.sh Optional wrapper for release binaries
vendor/wry/ Linux WebKit TLS policy patch (see vendor/README.md)
web-nodejs/public/js/remote-dashboard.js Detects window.__TAURI__ and calls open_session

Config file: config.json in the OS app config directory (com.betterdesk.rdclient).

Dependency note (brotli / alloc-no-stdlib)

Cargo.lock pins a git patch for alloc-no-stdlib so brotli (via Tauri) compiles with a single allocator version. If a fresh cargo update reintroduces alloc-no-stdlib 2.x and the build fails, run once from src-tauri/:

cargo update -p alloc-no-stdlib@2.0.4 --precise 3.0.0

Verify matrix (manual)

Platform Check
Linux Wayland Setup discovery, login, VP9/H.264 session (no AV1 loop), settings reset
Linux X11 Same as Wayland; test BETTERDESK_UI_BACKEND=x11 if needed
Windows x64 WebView2 present, Connect, remember passwords, MSI/portable from Generator
Fedora deb/rpm Installed bundle from Generator when build host has toolchain

Roadmap (not in this release)

  • Operator JWT + OS keychain login (POST /api/bd/operator/login)
  • WebSocket relay Bearer auth for long-lived desktop sessions
  • Native Rust video decoder (if WebCodecs insufficient on Linux)

License

AGPL-3.0 (same as BetterDesk on dev).