mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-11 01:55:43 +00:00
34ef01e17d
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)
223 lines
12 KiB
Markdown
223 lines
12 KiB
Markdown
# BetterDesk RdClient Desktop
|
||
|
||
Tauri v2 desktop shell for the RdClient operator UI. The app loads your panel’s **`/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 Fedora’s toolchain)
|
||
- **Linux:** `webkit2gtk-4.1`, `libayatana-appindicator3`, `librsvg2`, `patchelf` (Tauri [Linux deps](https://v2.tauri.app/start/prerequisites/))
|
||
|
||
## 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):
|
||
|
||
```bash
|
||
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:
|
||
|
||
```text
|
||
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:
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
cd rdclient-desktop
|
||
./scripts/rebuild-clean.sh
|
||
```
|
||
|
||
Production bundle instead of dev:
|
||
|
||
```bash
|
||
./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 panel’s 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 multi‑GB 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 ~200 MB** 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
|
||
|
||
```bash
|
||
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/`:
|
||
|
||
```bash
|
||
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`).
|