# ๐Ÿš€ BetterDesk v3 โ€” Ultimate Remote Desktop & CDAP Solution
BetterDesk Logo

![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg) ![Go](https://img.shields.io/badge/Go-1.21+-00ADD8.svg) ![Node.js](https://img.shields.io/badge/Node.js-18+-339933.svg) ![Version](https://img.shields.io/badge/version-3.5.4-brightgreen.svg) ![Security](https://img.shields.io/badge/Security-TLS%20%2B%20NaCl%20%2B%20TOTP%20%2B%20E2EE-green.svg) ![Database](https://img.shields.io/badge/DB-SQLite%20%2B%20PostgreSQL-blue.svg) ![CDAP](https://img.shields.io/badge/CDAP-v1.0-orange.svg) ![i18n](https://img.shields.io/badge/i18n-25%2B%20languages-purple.svg) [![Sponsor on GitHub](https://img.shields.io/badge/GitHub-Sponsor-181717?logo=github&logoColor=white&style=flat)](https://github.com/sponsors/UNITRONIX) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-%23FFDD00?logo=buy-me-a-coffee&logoColor=black&style=flat)](https://buymeacoffee.com/unitronix) **A clean-room RustDesk-compatible server written in Go โ€” single binary replacing hbbs + hbbr โ€” with full protocol support, TLS everywhere, PostgreSQL backend, CDAP (Custom Device API Protocol) for IoT/SCADA/network devices, and a modern Node.js web management console.** [Architecture](#-architecture) โ€ข [Installation](#-installation) โ€ข [Configuration](#-configuration) โ€ข [Security](#-security-architecture) โ€ข [API](#-api-reference) โ€ข [Troubleshooting](#-troubleshooting)
### ๐Ÿ† Honorary Supporter INSOLVE โ€” Honorary Supporter BetterDesk is proudly supported by **[INSOLVE](https://insolve.pl)** โ€” recognized as an *Honorary Supporter* of the project. See [SPONSORS.md](SPONSORS.md) for the full list of supporters and how to become one.
--- ## โœ… End-to-End Encryption โ€” Fully Working > **E2E encryption between RustDesk clients is fully functional.** Both P2P (punch-hole) and relay sessions establish NaCl-encrypted channels with proper `SignedId` + `PublicKey` handshake. The green lock indicator appears in the RustDesk client for all connection modes. > > Additionally, you can enable **TLS on relay ports** (`--tls-relay` / `TLS_RELAY=Y`) for an extra transport-level encryption layer on top of the E2E channel. --- ## ๐Ÿ“‹ Table of Contents - [Overview](#-overview) - [Architecture](#-architecture) - [BetterDesk Go Server](#-betterdesk-go-server) - [Protocol Implementation](#protocol-implementation) - [Cryptography](#cryptography) - [Database Backends](#database-backends) - [TLS Support](#tls-support) - [Rate Limiting & DDoS Protection](#rate-limiting--ddos-protection) - [Enrollment & Device Tokens](#enrollment--device-tokens) - [Web Console (Node.js)](#-web-console-nodejs) - [Security Architecture](#-security-architecture) - [Installation](#-installation) - [Linux](#linux) - [Windows](#windows) - [Docker](#docker) - [RustDesk Client Configuration](#-rustdesk-client-configuration) - [Desktop Client Login](#desktop-client-login) - [Enabling Pro Features](#enabling-pro-features) - [TLS / SSL Certificates](#-tls--ssl-certificates) - [Configuration Reference](#-configuration-reference) - [API Reference](#-api-reference) - [Migration Guide](#-migration-guide) - [Monitoring & Metrics](#-monitoring--metrics) - [Troubleshooting](#-troubleshooting) - [E2E Encryption](#-e2e-encryption) - [Chat E2E Encryption](#-chat-e2e-encryption) - [Unattended Access & Wake-on-LAN](#-unattended-access--wake-on-lan) - [Technology Stack](#-technology-stack) - [Contributing](#-contributing) - [License](#-license) --- ## ๐ŸŒŸ Overview **BetterDesk** is a complete RustDesk infrastructure solution consisting of two main components: 1. **BetterDesk Server** โ€” A clean-room Go implementation that replaces both `hbbs` (signal) and `hbbr` (relay) with a **single binary**. It implements the full RustDesk wire protocol, including UDP/TCP/WebSocket signal, TCP/WebSocket relay, NaCl secure handshake, and a comprehensive HTTP REST API. 2. **BetterDesk Console** โ€” A Node.js (Express.js) web management panel with device monitoring, TOTP 2FA, RBAC, address book sync, and RustDesk Client API. ### Why BetterDesk? | Feature | Original RustDesk Server | BetterDesk Server | |---------|------------------------|-------------------| | **Binaries** | 2 (hbbs + hbbr) | **1 single binary** | | **Language** | Rust | Go (pure Go, no CGO) | | **Database** | SQLite only | **SQLite + PostgreSQL** | | **TLS** | Not built-in | **TLS everywhere** (auto-detect plain/TLS on same port) | | **API** | Minimal | **Full REST API** with JWT + API keys | | **Status tracking** | Binary (online/offline) | **4-tier** (Online/Degraded/Critical/Offline) | | **Rate limiting** | None | **IP, bandwidth, connection** limits | | **Metrics** | None | **Prometheus** exposition format | | **Admin console** | None | **TCP admin** (telnet/netcat) | | **Device enrollment** | Open only | **Open/Managed/Locked** modes | | **Audit trail** | None | **Ring-buffer + JSON file** audit logging | | **Hot reload** | Restart required | **SIGHUP** config reload | | **Multi-instance** | File-based only | **PostgreSQL LISTEN/NOTIFY** for real-time sync | | **Web panel** | None | **Full Node.js console** with TOTP 2FA | | **User roles** | Single admin | **4-tier RBAC** (Admin, Operator, Viewer, Pro) | | **Chat** | None | **E2E encrypted** operatorโ†”device chat | | **Wake-on-LAN** | None | **Built-in WOL** via magic packet | | **Access policies** | None | **Unattended access** schedules + operator restrictions | > **๐Ÿค– AI-Assisted Development**: This project was developed with significant assistance from AI coding tools (Claude/GitHub Copilot). All code has been reviewed, tested, and validated for production use. --- ## ๐Ÿ—๏ธ Architecture ``` RustDesk Desktop/Mobile Clients โ”‚ โ”œโ”€โ”€ UDP (:21116) โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Signal Server โ”€โ”€โ–บ RegisterPeer, PunchHole, RequestRelay โ”œโ”€โ”€ TCP (:21116) โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Signal Server โ”€โ”€โ–บ NaCl Secure Handshake โ†’ encrypted channel โ”œโ”€โ”€ TCP (:21115) โ”€โ”€โ”€โ”€โ”€โ”€โ–บ NAT Test โ”€โ”€โ–บ TestNatRequest, OnlineRequest, ConfigUpdate โ”œโ”€โ”€ WS (:21118) โ”€โ”€โ”€โ”€โ”€โ”€โ–บ WS Signal โ”€โ”€โ–บ WebSocket-based signal (web clients) โ”‚ โ”œโ”€โ”€ TCP (:21117) โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Relay Server โ”€โ”€โ–บ UUID pairing โ†’ bidirectional io.Copy pipe โ”œโ”€โ”€ WS (:21119) โ”€โ”€โ”€โ”€โ”€โ”€โ–บ WS Relay โ”€โ”€โ–บ WebSocket relay (web clients) โ”‚ โ”œโ”€โ”€ HTTP (:21114) โ”€โ”€โ”€โ”€โ”€โ–บ Go HTTP API โ”€โ”€โ–บ Login, AB, heartbeat, REST (default) โ””โ”€โ”€ HTTP (:21121) โ”€โ”€โ”€โ”€โ”€โ–บ Node proxy โ”€โ”€โ–บ same handlers on Go (backward compat) Admin / Web Console โ”‚ โ”œโ”€โ”€ (internal) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Go :21114 โ”€โ”€โ–บ Panel via BETTERDESK_API_URL โ”œโ”€โ”€ WS via panel โ”€โ”€โ”€โ”€โ”€โ”€โ–บ Event stream โ”€โ”€โ–บ Real-time peer status โ”œโ”€โ”€ TCP (admin) โ”€โ”€โ”€โ”€โ–บ Admin Console โ”€โ”€โ–บ telnet/netcat management (127.0.0.1 only) โ””โ”€โ”€ HTTP (:5000) โ”€โ”€โ”€โ”€โ–บ Web Console โ”€โ”€โ–บ Node.js + Express + EJS (LAN) TOTP 2FA, RBAC, device management Database Layer โ”‚ โ”œโ”€โ”€ SQLite โ”€โ”€โ–บ Pure Go (modernc.org/sqlite), WAL mode, single binary โ””โ”€โ”€ PostgreSQL โ”€โ”€โ–บ pgx/v5 + pgxpool, LISTEN/NOTIFY, row-level locking ``` ### Port Map | Port | Protocol | Service | Direction | |------|----------|---------|-----------| | **21115** | TCP | NAT type test + OnlineRequest | WAN | | **21116** | TCP + UDP | Signal Server (registration, punch hole) | WAN | | **21117** | TCP | Relay Server (bidirectional stream) | WAN | | **21118** | WS(S) | WebSocket Signal | WAN | | **21119** | WS(S) | WebSocket Relay | WAN | | **21114** | HTTP | Go server API โ€” default (`-api-port`), direct RustDesk + REST | WAN | | **21121** | HTTP | Node backward-compat proxy โ†’ Go :21114 | WAN | | **5000** | HTTP | Web Console (admin panel) | LAN | > All TCP/WS ports support **dual-mode TLS** โ€” plain and TLS on the same port with automatic detection. --- ## ๐Ÿ”ง BetterDesk Go Server The Go server (`betterdesk-server/`) is a ~20,000 LOC clean-room implementation of the RustDesk signal and relay protocol. It compiles to a **single static binary** with no external dependencies (pure Go, no CGO required). ### Server Modes ```bash # Default: run everything (signal + relay + API + admin) ./betterdesk-server -mode all # Signal only (no relay) ./betterdesk-server -mode signal # Relay only ./betterdesk-server -mode relay ``` ### Protocol Implementation The server implements the complete RustDesk rendezvous/relay protocol: #### Signal Protocol (UDP/TCP/WS) | Message | Direction | Description | |---------|-----------|-------------| | `RegisterPeer` | Client โ†’ Server | Heartbeat registration (~12s interval), updates in-memory peer map | | `RegisterPeerResponse` | Server โ†’ Client | Suggested heartbeat interval, request online status | | `RegisterPk` | Client โ†’ Server | Public key registration with UUID consistency check | | `RegisterPkResponse` | Server โ†’ Client | Registration result | | `PunchHoleRequest` | Client A โ†’ Server | Request hole-punch to Client B | | `PunchHole` | Server โ†’ Client B | Forward punch request with Client A's address | | `PunchHoleSent` | Client B โ†’ Server | Confirm punch sent | | `PunchHoleResponse` | Server โ†’ Client A | Relay response with Client B's NAT info | | `RequestRelay` | Client โ†’ Server | Request relay session | | `RelayResponse` | Server โ†’ Clients | Relay server address + UUID for pairing | | `FetchLocalAddr` / `LocalAddr` | Bidirectional | LAN address exchange for direct connections | | `TestNatRequest` | Client โ†’ Server (:21115) | NAT type detection | | `TestNatResponse` | Server โ†’ Client | NAT type + `ConfigUpdate` (relay/rendezvous servers) | | `OnlineRequest` | Client โ†’ Server (:21115) | Bulk online status query (bitmask response) | #### Relay Protocol (TCP/WS) The relay is a **pure opaque byte pipe**: 1. Client A connects, sends `RequestRelay{uuid: "..."}` โ†’ registered as pending 2. Client B connects with the same UUID โ†’ pair found 3. Bidirectional `io.Copy` begins immediately (relay is a pure opaque byte pipe) 4. Timeouts: 30s pairing, 30s idle (extended on activity via `idleTimeoutConn`) The relay does **not** parse, inspect, or modify traffic between paired clients. E2E encryption is entirely between the two RustDesk clients. #### Wire Protocol (codec/) Matches `hbb_common::bytes_codec::BytesCodec`: - **TCP**: Variable-length framing โ€” bottom 2 bits of header = header length - 1, remaining bits = payload length (little-endian). Supports 1-4 byte headers (max frame: 64 KB) - **UDP**: Raw protobuf (no framing) - **WebSocket**: Raw protobuf per binary WS frame ### Cryptography #### Server-Client Secure Handshake (NaCl) ``` Server Client โ”‚ โ”‚ โ”‚ KeyExchange{keys: [Ed25519_sign(Cv_pub)]} โ”‚ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ โ”‚ โ”‚ โ”‚ KeyExchange{keys: [client_cv_pub, โ”‚ โ”‚ nacl_box(symmetric_key)]} โ”‚ โ”‚ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ โ”‚ โ”‚ โ”‚ โ•โ•โ• All traffic encrypted with NaCl secretbox โ•โ•โ• ``` 1. Server generates Ed25519 keypair โ†’ derives Curve25519 public key โ†’ signs with Ed25519 2. Client verifies signature โ†’ generates its own Curve25519 keypair 3. Client encrypts a random symmetric key using NaCl box (Curve25519 DH) 4. All subsequent messages use NaCl secretbox with sequential nonces 5. **Backward compatible**: Auto-detects old (plain) vs new (encrypted) clients #### Key Storage - **Private key**: `id_ed25519` file (auto-generated if missing) - **Public key**: `id_ed25519.pub` (base64-encoded, same format as original RustDesk) - **Key format**: Standard Ed25519 (32-byte seed), compatible with existing RustDesk clients #### Password Hashing - **Algorithm**: PBKDF2-HMAC-SHA256 - **Iterations**: 100,000 - **Salt**: 16-byte cryptographically random - **Comparison**: Constant-time (`subtle.ConstantTimeCompare`) ### Database Backends #### SQLite (Default) ```bash ./betterdesk-server -db ./db_v2.sqlite3 ``` - **Driver**: `modernc.org/sqlite` โ€” pure Go, no CGO required - **Journal mode**: WAL (Write-Ahead Logging) for concurrent reads - **Foreign keys**: Enabled by default - **Connection limit**: 1 (SQLite single-writer constraint) - **Write serialization**: `sync.RWMutex` around all write operations - **Tables**: `peers`, `server_config`, `id_change_history`, `users`, `api_keys`, `device_tokens` #### PostgreSQL ```bash ./betterdesk-server -db "postgres://user:password@localhost:5432/betterdesk?sslmode=disable" ``` - **Driver**: `pgx/v5` with `pgxpool` connection pooling - **Pool size**: Configurable via `pool_max_conns` DSN parameter (default: 10) - **Native types**: `BOOLEAN`, `BYTEA`, `TIMESTAMPTZ`, `BIGSERIAL` - **Row-level locking**: `SELECT ... FOR UPDATE` in `ChangePeerID` (replaces SQLite global mutex) - **LISTEN/NOTIFY**: Real-time cross-instance event push for multi-server deployments - **Partial indexes**: `WHERE banned = TRUE`, `WHERE peer_id != ''` for performance - **Auto-detection**: DSN starting with `postgres://` or `postgresql://` automatically selects PostgreSQL #### Schema Overview ```sql -- Core tables (both backends) peers -- Device records (21 fields: id, uuid, pk, ip, note, status, etc.) server_config -- Key-value configuration store id_change_history -- Device ID change audit trail users -- Admin/operator/viewer accounts (PBKDF2 + TOTP) api_keys -- API key management (SHA256 hash storage) device_tokens -- Enrollment tokens (Dual Key System) ``` ### TLS Support BetterDesk supports TLS on all transport layers with a unique **dual-mode auto-detection** system: ```bash # Enable TLS on signal ports (21116 TCP + 21115 + 21118 WSS) ./betterdesk-server -tls-signal -tls-cert server.crt -tls-key server.key # Enable TLS on relay ports (21117 TCP + 21119 WSS) ./betterdesk-server -tls-relay -tls-cert server.crt -tls-key server.key # Enable TLS on everything ./betterdesk-server -tls-signal -tls-relay -tls-cert server.crt -tls-key server.key # HTTPS on API ./betterdesk-server -tls-cert server.crt -tls-key server.key -force-https ``` #### DualModeListener (config/tls.go) Accepts **both plain and TLS connections on the same port**: 1. Peeks first byte of incoming connection 2. If `0x16` (TLS ClientHello) โ†’ upgrades to `tls.Server()` 3. Otherwise โ†’ passes through as plain TCP via `peekedConn` This means existing RustDesk clients (no TLS) continue to work alongside TLS-enabled clients without port changes. Minimum TLS version: **TLS 1.2**. ### Rate Limiting & DDoS Protection | Layer | Type | Default | Configurable | |-------|------|---------|-------------| | **IP rate limiter** | Sliding window per-IP | 20 registrations/min | Via constants | | **Login rate limiter** | Per-IP | 5 attempts / 5 min | Via constants | | **Bandwidth limiter** | Token bucket | 1 GB/s global, 16 MB/s per-session | Via constants | | **Connection limiter** | Per-IP concurrent | 20 relay connections/IP | `-relay-max-conns-ip` | | **TCP punch cache** | TTL + max size | 2 min TTL, 10K max entries | Via constants | | **WebSocket origins** | Whitelist | Accept all | `WS_ALLOWED_ORIGINS` env | | **Relay idle timeout** | Per-session | 30s (extended on activity) | Via constants | | **Relay pair timeout** | Pending sessions | 30s | Via constants | ### Enrollment & Device Tokens The **Dual Key System** controls which devices can register with the server: | Mode | Behavior | |------|----------| | `open` (default) | Accept all device registrations | | `managed` | New devices need admin approval OR a valid enrollment token | | `locked` | Only devices with pre-issued valid enrollment tokens can register | ```bash # Set enrollment mode ./betterdesk-server -mode all # Then via API: PUT /api/enrollment/mode {"mode": "managed"} ``` Device tokens have statuses: `pending`, `active`, `revoked`, `expired`. Bulk generation is supported via `POST /api/tokens/generate-bulk`. ### 4-Tier Device Status System ``` ONLINE โ†’ Last heartbeat < 15s ago DEGRADED โ†’ 2 consecutive missed heartbeats (15-30s) CRITICAL โ†’ 4+ missed heartbeats (30-60s) OFFLINE โ†’ Beyond RegTimeout (30s) with no heartbeat ``` - **Heartbeat interval**: Clients send `RegisterPeer` every ~12s - **Cleaner goroutine**: Runs every 3s, checks all peers, publishes status transition events - **Debounced DB sync**: Memory โ†’ database sync every 60s (avoids write storms) ### Admin TCP Console A lightweight management interface accessible via `telnet` or `netcat`: ```bash ./betterdesk-server -admin-port 9090 -admin-password "secret" # Then: telnet 127.0.0.1 9090 ``` **Commands**: `status`, `peers [count|info ]`, `ban/unban/kick `, `blocklist [add|rm]`, `config [get|set]`, `reload`, `quit` > Admin console binds to `127.0.0.1` only โ€” never exposed to the network. --- ## ๐Ÿ–ฅ๏ธ Web Console (Node.js) The web console (`web-nodejs/`) is an Express.js application providing a full-featured management UI. ### Features - **Dashboard** โ€” Real-time statistics cards (total, active, inactive, banned devices) - **Device management** โ€” Responsive devices page with horizontal folder chips, unified toolbar with segmented filters, slim table, and kebab context menu (โ‹ฎ). Four breakpoints: desktop, tablet (โ‰ค768px), phone card layout (โ‰ค600px), small phone (โ‰ค400px) - **Device details** โ€” Hardware tab (sysinfo), metrics tab (live CPU/RAM/disk bars + history charts) - **TOTP 2FA** โ€” Two-factor authentication with `otplib` - **RBAC** โ€” Admin, Operator, Viewer, and Pro (API-only) roles with permission enforcement - **Address book sync** โ€” Full AB storage with `address_books` table - **RustDesk Client API** โ€” Go on :21114 (default WAN), :21121 backward-compat proxy with 7-layer security - **Desktop connect** โ€” One-click connect via `rustdesk://` URI handler - **i18n** โ€” JSON-based translations, 25+ languages (auto-discovery from `lang/` directory) - **Desktop widget dashboard** โ€” Drag-and-drop dashboard mode with 20+ widget types (weather, calendar, system monitor, disk usage, log viewer, alert feed, speed test, Docker containers, world clock, bookmarks, device map, and more), Windows 11-style snap layouts with edge snapping, draggable zone borders, Aero Shake, widget groups/stacking, glassmorphism theme, presets/templates - **Fleet management** โ€” Device groups, tags, batch operations, scaling policies - **Chat 2.0** โ€” Operatorโ†”device messaging with E2E encryption (ECDH P-256 + AES-256-GCM), read receipts, file sharing, typing indicators - **Web remote client** โ€” Browser-based remote desktop (H.264 via WebCodecs on HTTPS, JMuxer fallback on HTTP), session recording (WebM VP9+Opus), monitor switching, quality presets (Speed/Balanced/Quality/Best) - **File transfer** โ€” Browser-based file transfer with progress tracking - **Security audit** โ€” Built-in security scanner with compliance reporting - **CDAP panel** โ€” Connected Device Automation Protocol widget rendering + commands - **Resource control** โ€” USB, optical drive, monitor, disk, quota policy management - **Unattended access** โ€” Access policies with day/time schedules, operator restrictions, device passwords - **Wake-on-LAN** โ€” Remote power-on via magic packet (UDP broadcast) - **Pro-only accounts** โ€” API-only user role that activates RustDesk Pro features without web panel access - **OS-style login screen** โ€” Windows 11-style login page with frosted glass, clock overlay, multi-user selector, TOTP flow - **Toast notifications** โ€” Slide-in notifications with progress bar auto-dismiss - **Light/dark/auto theme** โ€” Full theme system with `prefers-color-scheme` auto-detection - **Skeleton loading** โ€” Shimmer pulse placeholders during data loading - **CSRF protection** โ€” Double-submit cookie pattern with `csrf-csrf` - **WebSocket** โ€” Real-time status updates from Go server event bus ### RustDesk Client API (Go :21114, compat :21121) **Handlers** run in **betterdesk-server** on **21114** (default API port). **`http://:21114`** for new configs; **`http://:21121`** still works via Node proxy for existing deployments. Allows RustDesk desktop clients to: - Login/logout with username and password - Sync address books across devices - Send heartbeats and system information - Query device groups and user information **Security middleware stack (7 layers)**: | Layer | Protection | |-------|-----------| | 1 | Request timeout (10s request / 15s headers) | | 2 | Security headers (no server fingerprinting) | | 3 | Request logger (full audit trail) | | 4 | Path whitelist (only known RustDesk endpoints) | | 5 | Global rate limit (100 req/15min per IP) | | 6 | Login rate limit (5 attempts/15min per IP) | | 7 | Body size limit (1KB default, 64KB for address book) | --- ## ๐Ÿ›ก๏ธ Security Architecture ### Server-Level Security | Component | Implementation | |-----------|---------------| | **Transport encryption** | NaCl secretbox (signal TCP), TLS 1.2+ (all ports), WSS (WebSocket) | | **Password storage** | PBKDF2-HMAC-SHA256, 100K iterations, 16-byte random salt | | **JWT tokens** | HS256 with HMAC-equal verification (constant-time), configurable expiry, unique JTI | | **2FA partial tokens** | 5-minute TTL (prevents long-lived intermediate auth states) | | **TOTP 2FA** | RFC 6238 compliant, ยฑ1 time step tolerance, constant-time code comparison | | **API authentication** | JWT Bearer token OR API key (`X-API-Key` header, SHA256 hash lookup) | | **Input validation** | Peer ID: `^[A-Za-z0-9_-]{6,16}$`, config key: `^[A-Za-z0-9_.\-]{1,64}$` | | **SQL injection** | Parameterized queries only, LIKE patterns escape `%` and `_` | | **IP blocklist** | IP, CIDR, device ID blocking with hot-reload (SIGHUP) | | **Rate limiting** | Per-IP sliding window (registrations, login, TCP connections) | | **Bandwidth limiting** | Token bucket (global: 1 GB/s, per-session: 16 MB/s) | | **Audit logging** | Ring buffer (10K events) + optional JSON-lines file output | | **Error handling** | Never exposes internal details to clients | | **Credentials file** | Admin password file written with mode `0600` | | **Proxy trust** | `X-Forwarded-For` only when `TRUST_PROXY=Y` and (Go) `TRUSTED_PROXIES` lists the proxy | ### Console-Level Security | Component | Implementation | |-----------|---------------| | **CSRF** | Double-submit cookie pattern (`csrf-csrf`) | | **Session fixation** | Session regeneration after login | | **Timing-safe auth** | Pre-computed dummy bcrypt hash for non-existent users | | **WebSocket auth** | Session cookie required for WS upgrade | | **Helmet** | Security headers (CSP, HSTS, X-Frame-Options) | | **Rate limiting** | `express-rate-limit` on all endpoints | | **XSS prevention** | HTML sanitization, `textContent` instead of `innerHTML` | | **Sort injection** | Whitelisted sort fields and directions | --- ## ๏ฟฝ๏ธ Platform Support | Platform | Tier | Status | Notes | |----------|------|--------|-------| | **Linux x86_64** (bare-metal) | Tier 1 | โœ… Primary | Full support, all features, recommended | | **Linux ARM64** (bare-metal) | Tier 2 | โœ… Supported | Raspberry Pi 4+, Oracle ARM | | **Docker** (single-container) | Tier 2 | โœ… Supported | All-in-one image with supervisord | | **PostgreSQL** backend | Tier 2 | โœ… Supported | Enterprise deployments | | **Windows** x86_64 | Tier 3 | โš ๏ธ Experimental | Community-tested, limited support | | **Synology DSM** (Docker) | Tier 3 | โš ๏ธ Experimental | Community-tested | | **FreeBSD** x86_64 | Tier 3 | โš ๏ธ Experimental | Community build + example `rc.d` in `contrib/freebsd/` โ€” no official installer or CI binaries | > **Tier 1** = Fully tested by maintainers, highest priority for bug fixes. > **Tier 2** = Supported and tested, fixes provided. > **Tier 3** = Community-tested, best-effort support. Contributions welcome! --- ## ๏ฟฝ๐Ÿ“ฆ Installation ### Prerequisites | Requirement | Version | Notes | |------------|---------|-------| | **Go** | 1.21+ | Auto-installed by scripts if missing (downloads Go 1.22.1) | | **Node.js** | 18+ | Auto-installed by scripts if missing | | **Git** | Any | For cloning the repository | | **OS** | Linux (Ubuntu 20.04+, Debian 11+, CentOS 8+), Windows 10+, Docker | | | **PostgreSQL** | 14+ | Optional โ€” SQLite is the default | ### Linux **One-line install:** ```bash # Docker (official all-in-one image, auto-configured) curl -fsSL https://raw.githubusercontent.com/UNITRONIX/BetterDesk/main/install.sh | sudo bash # Native (git clone + betterdesk.sh --auto) curl -fsSL https://raw.githubusercontent.com/UNITRONIX/BetterDesk/main/install.sh | sudo bash -s -- --native ``` **Manual install:** ```bash git clone https://github.com/UNITRONIX/Rustdesk-FreeConsole.git cd Rustdesk-FreeConsole chmod +x betterdesk.sh # Interactive mode (recommended for first install) sudo ./betterdesk.sh # Automatic mode (non-interactive) sudo ./betterdesk.sh --auto # Skip Go binary SHA256 verification sudo ./betterdesk.sh --skip-verify # Custom API port API_PORT=21120 sudo ./betterdesk.sh --auto ``` The script will: 1. Install Go toolchain if not present 2. Compile `betterdesk-server` from source (single binary) 3. Install Node.js and the web console 4. Create systemd services (`betterdesk-server.service` + `betterdesk-console.service`) 5. Generate Ed25519 keys (or preserve existing ones) 6. Create initial admin user (credentials saved to `.admin_credentials`) 7. Start all services ### Windows ```powershell # Run PowerShell as Administrator git clone https://github.com/UNITRONIX/Rustdesk-FreeConsole.git cd Rustdesk-FreeConsole # Interactive mode .etterdesk.ps1 # Automatic mode .etterdesk.ps1 -Auto # Skip verification .etterdesk.ps1 -SkipVerify ``` The script installs Go, compiles the server, sets up NSSM services (`BetterDeskServer` + `BetterDeskConsole`) or scheduled tasks as fallback. ### Docker **๐Ÿš€ One-line install (recommended โ€” auto-configures Docker, relay, firewall):** ```bash curl -fsSL https://raw.githubusercontent.com/UNITRONIX/BetterDesk/main/install.sh | sudo bash ``` The installer downloads the **official all-in-one** GHCR image (`ghcr.io/unitronix/betterdesk`), detects your relay address, opens firewall ports (UFW/firewalld when active), starts the container, and prints admin credentials plus RustDesk client settings. **Options:** ```bash # Legacy two-container layout (server + console images) curl -fsSL .../install.sh | sudo bash -s -- --split # LAN-only server (use host LAN IP for relay) curl -fsSL .../install.sh | sudo bash -s -- --relay-mode local # Fixed public IP / custom admin password curl -fsSL .../install.sh | sudo bash -s -- --relay-servers 203.0.113.10:21117 --admin-password 'YourSecurePass' # Native install (git clone + betterdesk.sh --auto) instead of Docker curl -fsSL .../install.sh | sudo bash -s -- --native ``` Install files land in `/opt/betterdesk/docker` (Docker) or `/opt/betterdesk/source` (native). See `install.sh --help` for all flags. **๐Ÿš€ Quick Start (no build required โ€” official single image):** ```bash curl -fsSL https://raw.githubusercontent.com/UNITRONIX/BetterDesk/main/docker-compose.quick.single.yml -o docker-compose.yml docker compose pull && docker compose up -d docker compose exec betterdesk betterdesk-show-admin-credentials ``` Open http://localhost:5000 โ€” API on port **21121**. See [DOCKER_QUICKSTART.md](docs/docker/DOCKER_QUICKSTART.md) for legacy split layout and more options. **Build from source (advanced):** ```bash git clone https://github.com/UNITRONIX/Rustdesk-FreeConsole.git cd Rustdesk-FreeConsole # Build and start (all-in-one: Go server + Node.js console in one container) docker compose -f docker-compose.single.yml build docker compose -f docker-compose.single.yml up -d ``` **With PostgreSQL:** ```bash docker compose -f docker-compose.single.yml --profile postgres up -d ``` **Legacy multi-container** (via interactive script): ```bash chmod +x betterdesk-docker.sh ./betterdesk-docker.sh ``` ### Menu Options All scripts provide an interactive menu: | Option | Description | |--------|-------------| | **1** | ๐Ÿš€ New installation (compile Go server + install Node.js console) | | **2** | โฌ†๏ธ Update existing installation | | **3** | ๐Ÿ”ง Repair installation | | **4** | โœ… Validate installation | | **5** | ๐Ÿ’พ Create backup | | **6** | ๐Ÿ” Reset admin password | | **7** | ๐Ÿ”จ Build/rebuild Go server binary | | **8** | ๐Ÿ“Š Diagnostics | | **9** | ๐Ÿ—‘๏ธ Uninstall | | **C** | ๐Ÿ”’ Configure SSL/TLS certificates | | **M** | ๐Ÿ”„ Migrate from existing RustDesk (Docker) | | **P** | ๐Ÿ”€ Database migration (SQLite โ†” PostgreSQL) | | **S** | โš™๏ธ Settings (path configuration) | ### What Gets Installed ``` /opt/rustdesk/ # (Linux) or C:\BetterDesk\ (Windows) โ”œโ”€โ”€ betterdesk-server # Single Go binary (signal + relay + API) โ”œโ”€โ”€ id_ed25519 # Ed25519 private key (mode 0600) โ”œโ”€โ”€ id_ed25519.pub # Ed25519 public key (base64) โ”œโ”€โ”€ db_v2.sqlite3 # SQLite database (if using SQLite) โ”œโ”€โ”€ .admin_credentials # Initial admin password (mode 0600) โ”œโ”€โ”€ .api_key # API key for console โ†” server auth โ””โ”€โ”€ web-console/ # Node.js web console โ”œโ”€โ”€ server.js โ”œโ”€โ”€ package.json โ”œโ”€โ”€ .env # Console configuration โ””โ”€โ”€ ... ``` --- ## ๐Ÿ–ฅ๏ธ RustDesk Client Configuration After installing BetterDesk server, configure your RustDesk desktop clients to connect to it. ### Basic Setup 1. Open the RustDesk client 2. Click the **menu (โ‰ก)** button โ†’ **Network** โ†’ **ID/Relay Server** 3. Fill in: | Field | Value | Example | |-------|-------|---------| | **ID Server** | Your server IP or domain | `betterdesk.example.com` | | **Relay Server** | Same as ID Server (or leave empty) | `betterdesk.example.com` | | **API Server** | `http://:21114` or `:21121` (compat) | `http://betterdesk.example.com:21114` | | **Key** | Contents of `id_ed25519.pub` on the server | (base64 public key string) | > **Tip:** The public key can be found in the Web Console under **Dashboard โ†’ Server Keys**, or by reading the file `/opt/rustdesk/id_ed25519.pub` (Linux) / `C:\BetterDesk\id_ed25519.pub` (Windows) on the server. ### Mass Deployment BetterDesk supports three automated configuration paths. Full Windows / Intune / PSADT steps: **[docs/setup/RUSTDESK_CLIENT_DEPLOYMENT.md](docs/setup/RUSTDESK_CLIENT_DEPLOYMENT.md)**. The web console **Dashboard โ†’ RustDesk Client Configuration** card provides manual fields, a QR code, **Copy deploy string** (for scripts), and an **Intune / PSADT script** snippet. Set **Client server address** if the panel URL differs from the IP/DNS clients use, set `PANEL_PUBLIC_HOST` in the console `.env` for a single shared host, or configure **Settings โ†’ Public client endpoints** (or `PUBLIC_SERVER_ID` / `PUBLIC_RELAY_SERVER` / `PUBLIC_API_URL` in `.env`) when ID server, relay, and API use different public hostnames. #### 1. QR / deep link ``` rustdesk://config/ ``` #### 2. CLI / Import (`rustdesk.exe --config`) Uses a **reversed** base64 string (no `=` padding) โ€” the same format as **Settings โ†’ Network โ†’ Import Server Config** in the RustDesk client. **Do not** pass raw JSON. JSON payload (before encoding): ```json { "host": "betterdesk.example.com", "relay": "betterdesk.example.com", "api": "http://betterdesk.example.com:21114", "key": "" } ``` PowerShell (generate deploy string): ```powershell $json = @{ host='betterdesk.example.com'; relay='betterdesk.example.com'; api='http://betterdesk.example.com:21114'; key='' } | ConvertTo-Json -Compress $b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($json)).TrimEnd('=') $CfgString = -join ($b64[-1..-($b64.Length)] -join '') & "${env:ProgramFiles}\RustDesk\rustdesk.exe" --config $CfgString ``` Example template script: [`scripts/deploy/rustdesk-apply-config.ps1.example`](scripts/deploy/rustdesk-apply-config.ps1.example). ### Desktop Client Login RustDesk desktop clients can **log in** to the BetterDesk server using their user account. This enables address book synchronization, device grouping, and audit trail per user. #### How to Log In 1. Open the RustDesk client 2. Click the **account icon** (person silhouette) in the bottom-left corner 3. Enter your **username** and **password** โ€” the same credentials as the Web Console (local accounts or **LDAP/Active Directory** when LDAP is enabled in Settings) 4. Click **Login** > **Note:** Login is handled by **Go on 21114**. Use **`http://:21114`** (default) or **`http://:21121`** (backward compatibility). The web panel (5000) is management UI only. With LDAP enabled, directory users sign in here with their AD password; accounts are auto-provisioned on first successful login (same as the web console). #### What Login Enables | Feature | Description | |---------|-------------| | **Address Book Sync** | Your address book is stored server-side and synced across all devices where you're logged in | | **User Identity** | Connections are associated with your user account in audit logs | | **Device Groups** | Access to pre-configured device groups and strategies | | **Heartbeat + Sysinfo** | The client periodically sends system information (CPU, RAM, OS) to the server | #### User Roles and API Access | Role | Web Panel | RustDesk Client Login | Address Book | Device Groups | Device List | |------|:---------:|:--------------------:|:------------:|:-------------:|:-----------:| | **Admin** | โœ… Full access | โœ… | โœ… | โœ… | โœ… | | **Operator** | โœ… Device management | โœ… | โœ… | โœ… | โœ… | | **Viewer** | โœ… Read-only | โœ… | โœ… | โœ… | โœ… | | **Pro** | โŒ Blocked | โœ… | โœ… | โŒ Empty | โŒ Empty | > **Pro accounts** are designed for end-users who need RustDesk Pro features (address book sync, heartbeat, sysinfo) without access to the web management panel or visibility into other devices/groups. Create Pro users via the Web Console โ†’ Users page. #### Troubleshooting Login | Problem | Solution | |---------|----------| | "Login failed" | Verify API URL `http://:21114` or `:21121`; test `curl http://127.0.0.1:21114/api/login-options` | | "Connection refused" on :21114 | Go running with `-api-port 21114`, firewall **21114/tcp** | | "Connection refused" on :21121 | `API_ENABLED=true`, console running, firewall **21121/tcp** | | "Failed to parse response" with `https://` | Use **plain HTTP** on :21114 or :21121 | | TOTP 2FA prompt | Enter the 6-digit code from your authenticator app (if 2FA is enabled for your account) | | "Invalid credentials" | Reset password via Web Console โ†’ Users, or run the installer with option 6 | | LDAP/AD "Invalid credentials" in client but web console works | Update to latest release (Fixes #218); confirm LDAP test in Settings โ†’ LDAP; use API URL `:21114` or `:21121`, not the panel port `:5000` | ### Enabling Pro Features Connecting the RustDesk desktop client to a BetterDesk server with the API Server field configured **automatically activates Pro-level features** โ€” no license key required. These features are built into the standard RustDesk client but remain dormant until a compatible API server is detected. #### How to Activate 1. Open the RustDesk client 2. Go to **Settings (โš™) โ†’ Network โ†’ ID/Relay Server** (or **โ‰ก โ†’ Network**) 3. Fill in all four fields: | Field | Value | |-------|-------| | **ID Server** | `betterdesk.example.com` | | **Relay Server** | `betterdesk.example.com` (or leave empty to auto-detect) | | **API Server** | `http://betterdesk.example.com:21114` | | **Key** | Contents of `id_ed25519.pub` from the server | 4. Click the **account icon** (bottom-left) and **log in** with your BetterDesk credentials 5. Pro features activate immediately upon successful login > **Important:** The **API Server** field must point to the **Go** HTTP API (default port **21114**), e.g. `http://your-server:21114`. #### Features Enabled After Login | Feature | Description | Without API | With API + Login | |---------|-------------|:-----------:|:----------------:| | **Address Book Sync** | Address book stored server-side and synced across all logged-in devices | Local only | โœ… Cloud sync | | **Device Groups** | Organize devices into groups with access control strategies | โ€” | โœ… | | **User Groups** | Group-based access policies and permissions | โ€” | โœ… | | **Audit Trail** | Connection events, file transfers, and alarms logged per user | โ€” | โœ… | | **Heartbeat + Sysinfo** | Client reports CPU, RAM, OS, hostname periodically to the server | โ€” | โœ… | | **Device Metrics** | Live CPU/RAM/disk usage visible in the Web Console device panel | โ€” | โœ… | | **Connection Audit** | Each remote session logged with start/end time, peer IDs, user identity | โ€” | โœ… | | **File Transfer Audit** | Every file sent/received is recorded in the audit log | โ€” | โœ… | | **Security Alarms** | Brute-force attempts, unauthorized access, and suspicious activity alerts | โ€” | โœ… | | **Access Strategies** | Admin-defined rules controlling who can connect to which devices | โ€” | โœ… | | **Multi-Device Login** | Same account logged in on multiple machines, all sharing one address book | โ€” | โœ… | | **TOTP 2FA** | Two-factor authentication prompt when logging in (if enabled for the account) | โ€” | โœ… | #### Verifying Pro Mode Is Active After logging in, you can confirm Pro features are working: - **Address book** โ€” Open the address book panel; entries sync from the server. Adding a device on one machine appears on others. - **Account icon** โ€” The bottom-left icon shows your username instead of a generic silhouette. - **Web Console** โ€” Navigate to **Devices** in the Web Console; your client should appear with live status, sysinfo (Hardware tab), and real-time metrics (Metrics tab). #### Mass Deployment with Pro Enabled Use the dashboard **Copy deploy string** or generate the reversed base64 string (see [Mass Deployment](#mass-deployment) above). After deployment, users log in individually to activate per-user Pro features. ```powershell # After MSI โ€” elevated PowerShell & "${env:ProgramFiles}\RustDesk\rustdesk.exe" --config '' ``` > **Note:** Users still need to log in individually after initial configuration to activate per-user features (address book sync, audit trail, etc.). ### Ports Required on Client Side Ensure the following **outbound** ports are accessible from clients to the server: | Port | Protocol | Purpose | |------|----------|---------| | 21115 | TCP | NAT type detection (automatic) | | 21116 | TCP + UDP | Signal / ID registration | | 21117 | TCP | Relay (connection forwarding) | | 21118 | TCP (WS) | WebSocket signal (optional, web clients) | | 21119 | TCP (WS) | WebSocket relay (optional, web clients) | | 21114 | TCP | Go API โ€” default (RustDesk client + REST) | | 21121 | TCP | Backward-compat proxy to Go :21114 | --- ## ๐Ÿ”’ TLS / SSL Certificates BetterDesk supports TLS on all layers: Go server transport (signal + relay), Go server HTTPS API, and the Node.js web console. **External reverse proxy (Caddy/Nginx on :443):** terminate TLS at the proxy; BetterDesk panel stays HTTP on `127.0.0.1:5000`. See [docs/setup/REVERSE_PROXY.md](docs/setup/REVERSE_PROXY.md) and `sudo betterdesk.sh` โ†’ SSL Configuration โ†’ **External reverse proxy**. ### Self-Signed Certificate (Quick Start) For testing or internal networks, generate a self-signed certificate: ```bash # Create certificate directory mkdir -p /opt/rustdesk/ssl # Generate a self-signed certificate (valid for 3 years) openssl req -x509 -nodes -days 1095 -newkey rsa:2048 \ -keyout /opt/rustdesk/ssl/betterdesk.key \ -out /opt/rustdesk/ssl/betterdesk.crt \ -subj "/CN=$(hostname -f)/O=BetterDesk/C=US" \ -addext "subjectAltName=DNS:$(hostname -f),DNS:localhost,IP:$(curl -s ifconfig.me),IP:127.0.0.1" # Secure the private key chmod 600 /opt/rustdesk/ssl/betterdesk.key ``` > **Windows (PowerShell)**: > ```powershell > New-Item -ItemType Directory -Path "C:\BetterDesk\ssl" -Force > openssl req -x509 -nodes -days 1095 -newkey rsa:2048 ` > -keyout "C:\BetterDesk\ssl\betterdesk.key" ` > -out "C:\BetterDesk\ssl\betterdesk.crt" ` > -subj "/CN=localhost/O=BetterDesk" > ``` ### Applying TLS to Go Server Once you have certificate files, configure the Go server: ```bash # TLS on signal ports (21116 TCP + 21115 + 21118 WSS) ./betterdesk-server -tls-signal -tls-cert /opt/rustdesk/ssl/betterdesk.crt -tls-key /opt/rustdesk/ssl/betterdesk.key # TLS on relay ports (21117 TCP + 21119 WSS) ./betterdesk-server -tls-relay -tls-cert /opt/rustdesk/ssl/betterdesk.crt -tls-key /opt/rustdesk/ssl/betterdesk.key # TLS everywhere + force HTTPS on API ./betterdesk-server -tls-signal -tls-relay -force-https \ -tls-cert /opt/rustdesk/ssl/betterdesk.crt \ -tls-key /opt/rustdesk/ssl/betterdesk.key ``` For systemd, add the flags to `ExecStart` in `/etc/systemd/system/betterdesk-server.service`: ```ini ExecStart=/opt/rustdesk/betterdesk-server -mode all ... \ -tls-signal -tls-relay \ -tls-cert /opt/rustdesk/ssl/betterdesk.crt \ -tls-key /opt/rustdesk/ssl/betterdesk.key ``` > **Dual-mode**: BetterDesk auto-detects plain vs TLS on the **same port** (first-byte `0x16` detection). Existing non-TLS clients continue to work without changes. ### Applying TLS to Web Console (Node.js) Edit the console `.env` file: ```bash HTTPS_ENABLED=true SSL_CERT_PATH=/opt/rustdesk/ssl/betterdesk.crt SSL_KEY_PATH=/opt/rustdesk/ssl/betterdesk.key HTTP_REDIRECT_HTTPS=true ``` Or use the ALL-IN-ONE script menu option **C** (SSL Configuration) which supports: - **Let's Encrypt** โ€” automatic certificate via certbot (requires public domain + port 80) - **Custom certificate** โ€” provide your own PEM cert + key - **Self-signed** โ€” auto-generated for testing ### Let's Encrypt (Production) For production servers with a public domain: ```bash # Install certbot sudo apt install certbot # Debian/Ubuntu # Obtain certificate (standalone mode, port 80 must be open) sudo certbot certonly --standalone -d betterdesk.example.com # Certificate files: # /etc/letsencrypt/live/betterdesk.example.com/fullchain.pem # /etc/letsencrypt/live/betterdesk.example.com/privkey.pem ``` Then configure both the Go server and Node.js console to use these paths. > **Auto-renewal**: The ALL-IN-ONE script automatically adds a certbot renewal cron job when you use the Let's Encrypt option. ### Automatic TLS During Installation The ALL-IN-ONE scripts (`betterdesk.sh` / `betterdesk.ps1`) **automatically generate a self-signed certificate** during every fresh installation: 1. **Certificate location**: `/ssl/betterdesk.crt` + `betterdesk.key` 2. **Validity**: 3 years, RSA 2048-bit, with SAN (server IP + localhost) 3. **Go server flags**: `-tls-cert`, `-tls-key`, `-tls-signal`, `-tls-relay`, `-force-https` added automatically 4. **Node.js console**: `.env` pre-populated with `SSL_CERT_PATH` / `SSL_KEY_PATH` and `BETTERDESK_API_URL` set to `https://` 5. **Dual-mode**: Existing non-TLS clients continue to work (auto-detection on same port) You can **upgrade to Let's Encrypt** or a custom certificate at any time using menu option **C**. If certificate generation fails during install (e.g., openssl missing), the server runs without TLS and you can generate certificates later. --- ## โš™๏ธ Configuration Reference ### CLI Flags | Flag | Default | Env Var | Description | |------|---------|---------|-------------| | `-port` | `21116` | `PORT` | Signal port (UDP + TCP) | | `-relay-port` | `21117` | `RELAY_PORT` | Relay port (TCP) | | `-api-port` | `21114` | `API_PORT` | HTTP REST API port | | `-mode` | `all` | `MODE` | Server mode: `all`, `signal`, `relay` | | `-db` | `./db_v2.sqlite3` | `DB_URL` | Database DSN โ€” file path (SQLite) or `postgres://...` (PostgreSQL) | | `-key-file` | `id_ed25519` | `KEY_FILE` | Ed25519 key file path (without extension) | | `-relay-servers` | *(empty)* | `RELAY_SERVERS` | Comma-separated relay addresses for `ConfigUpdate` | | `-rendezvous-servers` | *(empty)* | `RENDEZVOUS_SERVERS` | Comma-separated rendezvous addresses | | `-mask` | *(empty)* | `MASK` | LAN mask (e.g. `192.168.0.0/24`) | | `-always-relay` | `false` | `ALWAYS_USE_RELAY=Y` | Force relay for all connections (no P2P) | | `-blocklist` | *(empty)* | `BLOCKLIST_FILE` | Path to IP/ID/CIDR blocklist file | | `-audit-log` | *(empty)* | `AUDIT_LOG_FILE` | Path to audit log file (JSON lines) | | `-tls-cert` | *(empty)* | `TLS_CERT` | TLS certificate file (PEM) | | `-tls-key` | *(empty)* | `TLS_KEY` | TLS private key file (PEM) | | `-tls-signal` | `false` | `TLS_SIGNAL=Y` | Enable TLS on signal ports | | `-tls-relay` | `false` | `TLS_RELAY=Y` | Enable TLS on relay ports | | `-log-format` | `text` | `LOG_FORMAT` | Log format: `text` or `json` | | `-admin-port` | `0` | `ADMIN_PORT` | TCP admin console port (0 = disabled) | | `-admin-password` | *(empty)* | `ADMIN_PASSWORD` | Admin TCP console password | | `-jwt-secret` | *(auto)* | `JWT_SECRET` | JWT signing secret (auto-generated if omitted) | | `-jwt-expiry` | `24` | `JWT_EXPIRY_HOURS` | JWT token expiry in hours | | `-force-https` | `false` | `FORCE_HTTPS=Y` | Reject non-TLS API requests | | `-trust-proxy` | `false` | `TRUST_PROXY=Y` | Trust `X-Forwarded-For` / `X-Real-IP` when peer is in `--trusted-proxies` | | `-trusted-proxies` | (empty) | `TRUSTED_PROXIES` | Comma-separated CIDR/IP allowlist of reverse proxies | | `-relay-max-conns-ip` | `20` | `RELAY_MAX_CONNS_PER_IP` | Max relay connections per IP | | `-signal-rate-limit-per-ip` | `20` | `SIGNAL_RATE_LIMIT_PER_IP` | Max signal registrations per proxy/client bucket per minute (`0` = disabled) | | `-init-admin-user` | `admin` | `INIT_ADMIN_USER` | Initial admin username | | `-init-admin-pass` | *(auto)* | `INIT_ADMIN_PASS` | Initial admin password (auto-generated if omitted) | | `-version` | โ€” | โ€” | Show version and exit | > Signal proxy note: UDP/TCP signal traffic on port `21116` cannot use HTTP headers such as `X-Forwarded-For`. `TRUST_PROXY` + `TRUSTED_PROXIES` apply to HTTP/API traffic and to signal **WebSocket** (`/ws/id`) client address headers. For NGINX stream or Docker proxy deployments, set `SIGNAL_RATE_LIMIT_PER_IP` higher for very large fleets, or `0` only on trusted private networks. Current builds scope registration buckets by proxy/client address plus peer ID to avoid false positives when multiple devices share one proxy address. ### Environment-Only Variables | Env Var | Description | |---------|-------------| | `WS_ALLOWED_ORIGINS` | Comma-separated allowed WebSocket origins | | `ENROLLMENT_MODE` | Device enrollment: `open`, `managed`, or `locked` | ### Protocol Constants | Constant | Value | Description | |----------|-------|-------------| | `RegTimeout` | 30s | Time after last heartbeat to mark peer offline | | `HeartbeatCheck` | 3s | Status cleaner goroutine interval | | `HeartbeatExpected` | 15s | Expected heartbeat interval from clients | | `DegradedThreshold` | 2 missed | Transitions to DEGRADED status | | `CriticalThreshold` | 4 missed | Transitions to CRITICAL status | | `HeartbeatSuggestion` | 12s | Suggested heartbeat interval sent to clients | | `TCPConnTimeout` | 20s | TCP connection establishment timeout | | `RelayPairTimeout` | 30s | Time to wait for relay pair match | | `RelayIdleTimeout` | 30s | Close idle relay sessions (extended on activity) | | `DefaultTotalBandwidth` | 1 GB/s | Global bandwidth limit | | `DefaultSingleBandwidth` | 16 MB/s | Per-session bandwidth limit | | `IPRateLimitRegistrations` | 20/min | Registration rate limit per signal bucket | | `IDChangeCooldown` | 5 min | Minimum interval between ID changes | | `MaxFrameSize` | 64 KB | Maximum wire protocol frame size | ### Console Configuration (.env) ```bash PORT=5000 # Web console port HOST=0.0.0.0 # Web console bind address (LAN) GO_API_PORT=21114 # Go -api-port (handlers) API_PORT=21121 # Backward-compat proxy port (optional listener) API_ENABLED=true RUSTDESK_API_PROXY=true HTTPS_ENABLED=false # Enable HTTPS on console SSL_CERT_PATH= # SSL certificate path SSL_KEY_PATH= # SSL key path TRUST_PROXY=false # Trust X-Forwarded-For (Go also needs TRUSTED_PROXIES) TRUSTED_PROXIES= # e.g. 127.0.0.1/32,::1/128 when TRUST_PROXY=Y DB_PATH= # Path to SQLite database BETTERDESK_API_URL= # Go server API URL (http://localhost:21114/api) BETTERDESK_API_KEY= # API key for Go server (env: BETTERDESK_API_KEY or HBBS_API_KEY) ``` > Keep port `5000` LAN-only via firewall. Do not expose the Web Console directly to the internet. --- ## ๐Ÿ“ก API Reference ### Authentication All API requests (except public endpoints) require authentication: ```bash # Option 1: JWT Bearer token (from POST /api/auth/login) curl -H "Authorization: Bearer " http://localhost:21114/api/peers # Option 2: API key curl -H "X-API-Key: " http://localhost:21114/api/peers ``` ### Public Endpoints (No Auth) | Method | Path | Description | |--------|------|-------------| | `GET` | `/api/health` | Health check + peer counts | | `GET` | `/api/server/stats` | Server stats (memory, goroutines, peer breakdown) | | `GET` | `/api/server/pubkey` | Ed25519 public key (base64 + hex) | | `GET` | `/metrics` | Prometheus metrics | | `POST` | `/api/auth/login` | Login โ†’ JWT token | | `POST` | `/api/auth/login/2fa` | Complete TOTP 2FA flow | ### RustDesk Client Endpoints (Go Server, Port 21114) These endpoints are served on the Go server's management API port because the RustDesk desktop client calculates the API port as `signal_port - 2` (21116 - 2 = 21114): | Method | Path | Description | |--------|------|-------------| | `POST` | `/api/login` | RustDesk-compatible login (username/password + optional TOTP) | | `GET` | `/api/login-options` | Available authentication methods | | `POST` | `/api/logout` | Invalidate session (no-op for stateless JWT) | | `GET/POST` | `/api/currentUser` | Get current user info (Bearer token) | | `GET/POST` | `/api/ab` | Get/update address book | | `POST` | `/api/heartbeat` | Client heartbeat (updates peer status to ONLINE) | | `POST` | `/api/sysinfo` | Client system info (hostname, OS, version) | | `POST` | `/api/sysinfo_ver` | Sysinfo version check (SHA256 of stored fields) | ### Viewer+ Endpoints | Method | Path | Description | |--------|------|-------------| | `GET` | `/api/peers` | List all peers (`?include_deleted=true`) | | `GET` | `/api/peers/{id}` | Get single peer | | `GET` | `/api/peers/status/summary` | Aggregate status counts | | `GET` | `/api/peers/online` | All online peer snapshots | | `GET` | `/api/peers/{id}/status` | Detailed live status (missed beats, transport) | | `GET` | `/api/blocklist` | List blocklist entries | | `PUT` | `/api/peers/{id}/tags` | Set peer tags | | `GET` | `/api/tags/{tag}/peers` | Peers by tag (LIKE pattern, `%_` escaped) | | `GET` | `/api/audit/events` | Recent events (`?limit=50&action=peer_banned`) | | `GET` | `/api/peers/{id}/metrics` | Historical CPU/RAM/disk metrics (`?limit=100`, max 1000) | | `GET` | `/api/ws/events` | WebSocket real-time events (`?filter=peer_online`) | | `GET` | `/api/auth/me` | Current user info | ### Admin-Only Endpoints | Method | Path | Description | |--------|------|-------------| | `DELETE` | `/api/peers/{id}` | Delete peer (`?hard=true` for permanent) | | `POST` | `/api/peers/{id}/ban` | Ban peer (with reason) | | `POST` | `/api/peers/{id}/unban` | Unban peer | | `POST` | `/api/peers/{id}/change-id` | Change device ID (validated: 6-16 chars, `[A-Za-z0-9_-]`) | | `POST` | `/api/blocklist` | Add IP/CIDR/ID to blocklist | | `DELETE` | `/api/blocklist/{entry}` | Remove blocklist entry | | `GET/PUT` | `/api/config/{key}` | Server configuration (key validated: 1-64 chars) | | `GET/POST/PUT/DELETE` | `/api/users`, `/api/users/{id}` | User CRUD | | `POST` | `/api/users/{id}/totp/setup` | Generate TOTP secret + QR URI | | `POST` | `/api/users/{id}/totp/confirm` | Confirm TOTP enrollment | | `DELETE` | `/api/users/{id}/totp` | Disable TOTP | | `GET/POST/DELETE` | `/api/keys`, `/api/keys/{id}` | API key management | | `GET/POST/PUT/DELETE` | `/api/tokens`, `/api/tokens/{id}` | Device token CRUD | | `POST` | `/api/tokens/generate-bulk` | Bulk generate enrollment tokens | | `POST` | `/api/tokens/{id}/bind` | Bind token to specific peer ID | | `GET/PUT` | `/api/enrollment/mode` | Enrollment mode management | | `POST` | `/api/peers/{id}/wol` | Wake-on-LAN magic packet (UDP broadcast) | | `GET/POST/PUT/DELETE` | `/api/access-policies`, `/api/access-policies/{id}` | Unattended access policies (schedules, operator restrictions) | ### Example: Login Flow ```bash # Step 1: Login (returns JWT or requires 2FA) curl -X POST http://localhost:21114/api/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"your_password"}' # Response (no 2FA): # {"token":"eyJhbG...","user":{"id":1,"username":"admin","role":"admin"}} # Response (2FA required): # {"requires_2fa":true,"partial_token":"eyJhbG..."} # Step 2 (if 2FA): Complete with TOTP code curl -X POST http://localhost:21114/api/auth/login/2fa -H "Content-Type: application/json" -d '{"partial_token":"eyJhbG...","code":"123456"}' ``` ### WebSocket Events ```javascript // Connect to real-time event stream const ws = new WebSocket('ws://localhost:21114/api/ws/events?filter=peer_online,peer_offline'); ws.onmessage = (event) => { const data = JSON.parse(event.data); // { "type": "peer_online", "peer_id": "ABC123", "timestamp": "..." } }; ``` **Event types**: `peer_online`, `peer_degraded`, `peer_critical`, `peer_offline`, `peer_banned`, `peer_unbanned`, `peer_deleted`, `peer_id_changed`, `blocklist_updated`, `server_stats` --- ## ๐Ÿ”„ Migration Guide ### From Original RustDesk (Rust) to BetterDesk (Go) The migration tool (`betterdesk-server/tools/migrate/`) supports multiple migration paths: **Linux/macOS:** ```bash # Compile migration tool cd betterdesk-server/tools/migrate go build -o migrate . # Mode 1: Rust hbbs โ†’ BetterDesk Go (preserves peer table โ†’ peers) ./migrate -mode rust2go -src /opt/rustdesk/db_v2.sqlite3 -dst /opt/betterdesk/db_v2.sqlite3 # Mode 2: SQLite โ†’ PostgreSQL ./migrate -mode sqlite2pg -src /opt/betterdesk/db_v2.sqlite3 -dst "postgres://user:pass@localhost:5432/betterdesk" # Mode 3: PostgreSQL โ†’ SQLite (reverse) ./migrate -mode pg2sqlite -src "postgres://..." -dst ./backup.sqlite3 # Mode 4: Node.js console โ†’ Go server ./migrate -mode nodejs2go -src /opt/betterdesk/web-console/betterdesk.db -dst /opt/betterdesk/db_v2.sqlite3 # Mode 5: Backup ./migrate -mode backup -src /opt/betterdesk/db_v2.sqlite3 ``` **Windows (PowerShell):** ```powershell # Compile migration tool (requires Go installed) cd betterdesk-server\tools\migrate go build -o migrate.exe . # Usage (same modes as Linux) .\migrate.exe -mode rust2go -src C:\BetterDesk\db_v2.sqlite3 -dst C:\BetterDesk\db_v2_new.sqlite3 .\migrate.exe -mode sqlite2pg -src C:\BetterDesk\db_v2.sqlite3 -dst "postgres://user:pass@localhost:5432/betterdesk" ``` > **Note:** Windows users need [Go](https://go.dev/dl/) installed to compile the migration tool. Pre-built binaries are available in [GitHub Releases](https://github.com/UNITRONIX/BetterDesk/releases) (when available). The migration tool auto-detects the source schema (original RustDesk `peer` table vs BetterDesk `peers` table) and maps columns accordingly. Ed25519 keys, UUIDs, ID history, bans, and tags are fully preserved. ### Using ALL-IN-ONE Scripts Both `betterdesk.sh` and `betterdesk.ps1` include built-in migration options: - **Option M** โ€” Migrate from existing RustDesk Docker installation - **Option P** โ€” Database migration (SQLite โ†” PostgreSQL) ### From Existing Docker RustDesk ```bash ./betterdesk-docker.sh # Select: M (Migrate from existing RustDesk) ``` The wizard auto-detects existing containers, creates a backup, and migrates data to BetterDesk. --- ## ๐Ÿ“Š Monitoring & Metrics ### Prometheus Metrics Available at `GET /metrics` (no authentication required): ``` # Counters (monotonic) betterdesk_registrations_total betterdesk_expired_total betterdesk_relay_sessions_total betterdesk_relay_bytes_total betterdesk_bandwidth_throttle_hits_total betterdesk_audit_events_total # Gauges (current values) betterdesk_uptime_seconds betterdesk_peers_total betterdesk_peers_online betterdesk_peers_degraded betterdesk_peers_critical betterdesk_peers_offline betterdesk_peers_banned betterdesk_peers_udp betterdesk_peers_tcp betterdesk_peers_ws betterdesk_relay_active_sessions betterdesk_blocklist_entries betterdesk_event_subscribers betterdesk_goroutines betterdesk_memory_alloc_bytes betterdesk_memory_sys_bytes ``` ### Grafana Integration Point a Prometheus scraper at `http://your-server:21114/metrics` and import the metrics above. No additional exporter needed. ### Audit Logging ```bash # Enable file-based audit log ./betterdesk-server -audit-log /var/log/betterdesk/audit.jsonl ``` Events logged: login, failed auth, peer banned/unbanned, config changes, ID changes, blocklist modifications. Each event includes timestamp, action, actor, target, IP, and details. --- ## ๐Ÿ”ง Troubleshooting ### Common Issues | Symptom | Cause | Solution | |---------|-------|----------| | **Key mismatch** errors | Keys changed during install | Restore from backup: `cp /opt/rustdesk-backup-*/id_ed25519* /opt/rustdesk/` | | **All devices offline** | Database missing `last_online` column | Run repair: `sudo ./betterdesk.sh` โ†’ option 3 | | **API not responding** | Wrong binary or port | Check: `curl http://localhost:21114/api/health` | | **E2E encryption** | Not showing green lock | Verify server is v2.4.0+; enable `--tls-relay` for extra layer | | **Docker: "pull access denied"** | Images not on Docker Hub | Build locally: `docker compose build` | | **Docker: "no such table"** | Wrong binary in container | Rebuild: `docker compose build --no-cache` | | **Services won't start** | Permission issues | `sudo chmod 600 /opt/rustdesk/id_ed25519` | | **Go compilation fails** | Missing Go toolchain | Script auto-installs, or: `sudo apt install golang-go` | | **PostgreSQL connection** | Wrong DSN format | Must start with `postgres://` or `postgresql://` | | **Connect button** won't work | Custom RustDesk scheme | Set via browser console: `setCustomScheme('your-scheme')` | ### Diagnostics ```bash # Linux โ€” run built-in diagnostics sudo ./betterdesk.sh # Select option 8 # Check service status sudo systemctl status betterdesk-server sudo systemctl status betterdesk-console # Check logs sudo journalctl -u betterdesk-server -n 100 --no-pager sudo journalctl -u betterdesk-console -n 100 --no-pager # Test API curl http://localhost:21114/api/health curl http://localhost:21114/api/server/stats # Check metrics curl http://localhost:21114/metrics # Windows โ€” check services Get-Service BetterDeskServer Get-Service BetterDeskConsole ``` ### Key Management ```bash # Backup keys (CRITICAL โ€” losing keys disconnects ALL clients) cp /opt/rustdesk/id_ed25519 /opt/rustdesk/id_ed25519.backup cp /opt/rustdesk/id_ed25519.pub /opt/rustdesk/id_ed25519.pub.backup # Restore keys from automatic backup BACKUP=$(ls -d /opt/rustdesk-backup-* | sort | tail -1) sudo cp $BACKUP/id_ed25519* /opt/rustdesk/ sudo chmod 600 /opt/rustdesk/id_ed25519 sudo systemctl restart betterdesk-server ``` ### Firewall Configuration ```bash # RustDesk protocol ports (WAN) sudo ufw allow 21115/tcp # NAT test sudo ufw allow 21116/tcp # Signal (TCP) sudo ufw allow 21116/udp # Signal (UDP) sudo ufw allow 21117/tcp # Relay sudo ufw allow 21114/tcp # Go API (default, direct) sudo ufw allow 21121/tcp # RustDesk client API (backward-compat proxy) # Admin ports (LAN only) sudo ufw allow from 192.168.0.0/16 to any port 21114 proto tcp # REST API sudo ufw allow from 192.168.0.0/16 to any port 5000 proto tcp # Web Console ``` --- ## ๐Ÿ” E2E Encryption ### Overview RustDesk clients support end-to-end encryption for remote desktop sessions. BetterDesk Server fully supports this โ€” both P2P (hole-punch) and relay-mode connections establish an encrypted E2E channel with NaCl key exchange. ### How It Works 1. Client A requests connection to Client B via the signal server 2. Signal server facilitates hole-punching (sends `PunchHoleResponse` with Client B's public key) or relay setup (sends `RelayResponse` with relay UUID and `SignIdPk` credential) 3. Once connected (P2P or relay), clients perform an **asymmetric key exchange** using NaCl (`Message.SignedId` + `Message.PublicKey`) 4. All subsequent session traffic (video, audio, clipboard, files) is encrypted end-to-end ### Key Implementation Details | Component | Implementation | |-----------|---------------| | **PunchHoleResponse** | Includes `pk` field (responder's public key) for E2E handshake initiation | | **RelayResponse** | Uses `SignIdPk()` NaCl combined format (64-byte Ed25519 signature + IdPk protobuf) | | **Relay server** | Pure opaque byte pipe โ€” does **not** send its own `RelayResponse` confirmation, allowing clients to exchange `Message.SignedId` / `Message.PublicKey` directly | | **Transport layer** | Optional TLS via `--tls-relay` for defense-in-depth on top of E2E | ### Verified Behavior - Green lock indicator appears in the RustDesk client for all connection modes - `Message.SignedId` + `Message.PublicKey` handshake confirmed between peers via debug logging - Compatible with RustDesk clients v1.1.9+ (standard and custom builds) --- ## ๏ฟฝ Chat E2E Encryption BetterDesk includes a built-in chat system between operators and end-user devices with full end-to-end encryption. ### Protocol | Component | Implementation | |-----------|---------------| | **Key exchange** | P-256 ECDH via WebCrypto API | | **Key derivation** | HKDF-SHA256 | | **Message encryption** | AES-256-GCM with per-message IV | | **File encryption** | AES-256-GCM with encrypted metadata (filename, size, timestamp) | | **Key persistence** | localStorage (auto-generated key pair per device) | | **Key rotation** | Every 24 hours or 1000 messages (whichever comes first) | ### Features - **1:1 chat** โ€” Operator โ†” end-user device messaging - **Read receipts** โ€” Message ID arrays for read confirmation - **Typing indicators** โ€” Real-time typing status - **Presence** โ€” Online/away/busy status updates - **File sharing** โ€” Encrypted file transfer up to 50 MB per file - **Fallback contacts** โ€” If Go server is unavailable, operator + connected agents are returned as contacts ### Message Types | Type | Direction | Description | |------|-----------|-------------| | `key_exchange` | Bidirectional | Public key relay for ECDH | | `read_receipt` | Bidirectional | Message ID arrays marking read | | `presence_update` | Bidirectional | Online/away/busy status | | `file_share` | Bidirectional | Encrypted file metadata relay | | `chat_message` | Bidirectional | E2E encrypted text message | --- ## ๐Ÿ”“ Unattended Access & Wake-on-LAN ### Unattended Access Management Administrators can configure unattended access policies per device, controlling who can connect and when. ```bash # Create an access policy curl -X POST http://localhost:21114/api/access-policies \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "peer_id": "ABC123", "password": "secret", "enabled": true, "allowed_operators": ["operator1", "operator2"], "schedule": { "days": ["mon", "tue", "wed", "thu", "fri"], "start_time": "08:00", "end_time": "18:00", "timezone": "Europe/Warsaw" } }' ``` **Features:** - Set/change device passwords (bcrypt hashed) - Enable/disable unattended access per device - Configure access schedules (day/time/timezone) - Restrict allowed operators - Web Console UI: modal on device detail page with full schedule editor ### Wake-on-LAN Power on offline devices remotely via magic packet: ```bash # Wake device by peer ID (MAC address provided in request body) curl -X POST http://localhost:21114/api/peers/ABC123/wol \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"mac": "AA:BB:CC:DD:EE:FF"}' ``` The Go server sends a UDP broadcast magic packet (6ร— `0xFF` + 16ร— MAC address) to `255.255.255.255:9`. Available from the MGMT desktop client and Web Console (offline devices only). --- ## ๏ฟฝ๐Ÿ› ๏ธ Technology Stack ### BetterDesk Go Server | Component | Technology | |-----------|-----------| | **Language** | Go 1.21+ (pure Go, no CGO) | | **Protocol** | RustDesk rendezvous + relay (protobuf) | | **Crypto** | NaCl (Curve25519 + secretbox), Ed25519 (`golang.org/x/crypto`) | | **Database** | SQLite (`modernc.org/sqlite`), PostgreSQL (`pgx/v5` + `pgxpool`) | | **WebSocket** | `coder/websocket` | | **Protobuf** | `google.golang.org/protobuf` | | **TLS** | Standard library `crypto/tls` (min TLS 1.2) | ### Web Console (Node.js) | Component | Technology | |-----------|-----------| | **Runtime** | Node.js 22+ | | **Framework** | Express.js | | **Templates** | EJS | | **Database** | better-sqlite3 | | **Auth** | bcrypt + TOTP (`otplib`) | | **Security** | csrf-csrf, Helmet, express-rate-limit | | **Client API** | Dedicated port with 7-layer security | ### Frontend | Component | Technology | |-----------|-----------| | **UI** | HTML5, CSS3 (glassmorphism, responsive breakpoints), JavaScript ES6+ | | **Icons** | Material Icons (offline) | | **Charts** | Live metric bars + history charts | | **i18n** | JSON-based translations | ### DevOps | Component | Technology | |-----------|-----------| | **Linux services** | systemd (`betterdesk-server.service` + `betterdesk-console.service`) | | **Windows services** | NSSM (`BetterDeskServer` + `BetterDeskConsole`) | | **Docker** | Docker Compose with local image builds | | **Installation** | Bash (`betterdesk.sh`) + PowerShell (`betterdesk.ps1`) ALL-IN-ONE | | **CI/CD** | GitHub Actions (multi-platform build) | --- ## ๐Ÿ—๏ธ Building from Source ### Go Server ```bash cd betterdesk-server # Linux (amd64) CGO_ENABLED=0 go build -ldflags="-s -w" -o betterdesk-server . # Linux (arm64) CGO_ENABLED=0 GOARCH=arm64 go build -ldflags="-s -w" -o betterdesk-server-arm64 . # Windows CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o betterdesk-server.exe . ``` ### Migration Tool ```bash cd betterdesk-server/tools/migrate go build -o migrate . ``` ### Node.js Console ```bash cd web-nodejs npm install npm start ``` --- ## ๐ŸŒ Internationalization (i18n) BetterDesk Console supports 25+ languages through JSON-based translations with auto-discovery. **Included languages**: ๐Ÿ‡ฌ๐Ÿ‡ง English, ๐Ÿ‡ต๐Ÿ‡ฑ Polish, ๐Ÿ‡ฉ๐Ÿ‡ช German, ๐Ÿ‡ซ๐Ÿ‡ท French, ๐Ÿ‡ช๐Ÿ‡ธ Spanish, ๐Ÿ‡ฎ๐Ÿ‡น Italian, ๐Ÿ‡ต๐Ÿ‡น Portuguese, ๐Ÿ‡ณ๐Ÿ‡ฑ Dutch, ๐Ÿ‡จ๐Ÿ‡ณ Chinese (Simplified), ๐Ÿ‡ฏ๐Ÿ‡ต Japanese, ๐Ÿ‡ฐ๐Ÿ‡ท Korean, ๐Ÿ‡ธ๐Ÿ‡ฆ Arabic, ๐Ÿ‡ฎ๐Ÿ‡ฑ Hebrew, ๐Ÿ‡บ๐Ÿ‡ฆ Ukrainian, ๐Ÿ‡น๐Ÿ‡ท Turkish, ๐Ÿ‡ฎ๐Ÿ‡ณ Hindi, ๐Ÿ‡ธ๐Ÿ‡ช Swedish, ๐Ÿ‡ณ๐Ÿ‡ด Norwegian, ๐Ÿ‡ฉ๐Ÿ‡ฐ Danish, ๐Ÿ‡ซ๐Ÿ‡ฎ Finnish, ๐Ÿ‡จ๐Ÿ‡ฟ Czech, ๐Ÿ‡ญ๐Ÿ‡บ Hungarian, ๐Ÿ‡ท๐Ÿ‡ด Romanian, ๐Ÿ‡น๐Ÿ‡ญ Thai, ๐Ÿ‡ป๐Ÿ‡ณ Vietnamese, ๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesian Languages are auto-discovered from the `lang/` directory โ€” no code changes needed. **Adding a new language**: 1. Copy `web-nodejs/lang/en.json` โ†’ `web-nodejs/lang/{code}.json` 2. Translate all string values 3. Update the `_meta` section with language info 4. Run `npm run i18n:check --fix` to validate 100% key coverage See [Contributing Translations](docs/development/CONTRIBUTING_TRANSLATIONS.md) for details. --- ## ๐Ÿค– CDAP โ€” Connected Device Automation Protocol CDAP enables managing IoT, ICS, and custom devices alongside standard RustDesk desktops. ### Architecture ``` CDAP Device (Agent/Bridge) โ”€โ”€WebSocketโ”€โ”€โ–บ Go Server (:21122/cdap) โ”‚ REST API + WS proxy โ”‚ Node.js Console โ”€โ”€โ–บ Browser ``` ### Features - **Widget rendering** โ€” 8 widget types (gauge, toggle, button, LED, text, slider, select, chart) - **Real-time telemetry** โ€” Heartbeat with CPU/memory/disk metrics, bulk state updates - **Remote commands** โ€” Send commands to devices with confirmation and cooldown - **Terminal** โ€” Browser-based terminal sessions to CDAP agents - **File browser** โ€” Browse and transfer files from CDAP agents - **Clipboard sync** โ€” Bidirectional clipboard between browser and device - **Audio streaming** โ€” Two-way audio via WebSocket (PCM + Opus) - **Multi-monitor** โ€” Remote display selection and switching ### SDKs & Bridges | SDK | Language | Status | |-----|----------|--------| | [Python SDK](sdks/python/) | Python 3.9+ | โœ… Stable | | [Node.js SDK](sdks/nodejs/) | Node.js 22+ | โœ… Stable | | Bridge | Protocol | Status | |--------|----------|--------| | [Modbus](bridges/modbus/) | Modbus TCP/RTU | โœ… Reference | | [SNMP](bridges/snmp/) | SNMP v2c/v3 | โœ… Reference | | [REST/Webhook](bridges/rest-webhook/) | HTTP polling + webhooks | โœ… Reference | See [CDAP Documentation](docs/cdap/OVERVIEW.md) and [SDK Documentation](docs/sdk/OVERVIEW.md) for details. --- ## ๐Ÿ–ฅ๏ธ Desktop Clients ### BetterDesk MGMT Client (Operator/Admin) A Tauri v2 + SolidJS desktop application for operators and administrators. - Operator login with TOTP 2FA and JWT token management - Device list with live status, search, filter, group chips, and CPU/RAM metrics - One-click remote connect to any device (H.264/VP9 video pipeline) - Full remote desktop with multi-monitor selection, clipboard sync, file transfer, session recording - Help request management inbox with accept & auto-connect - Session history dashboard with audit trail - Server management panel (admin) โ€” clients, operators, audit, API keys, config - Notification center with real-time push (30s polling) - Unattended access management โ€” set passwords, schedules, operator restrictions - Wake-on-LAN for offline devices - Device actions โ€” restart, shutdown, lock screen, log off, send message - File transfer panel with local file browsing - Input injection via `enigo` crate (keyboard, mouse, text) - Single-instance enforcement (Windows mutex) - Full i18n (English + Polish, ~60 keys) **Build**: `cd betterdesk-mgmt && pnpm install && pnpm tauri build` ### BetterDesk Agent Client (Endpoint Device) A lightweight Tauri v2 agent installed on end-user devices. - 5-step setup wizard with sequential server validation (availability โ†’ protocol โ†’ registration โ†’ certificate โ†’ complete) - Device registration with machine UID-based device ID (`BD-{hash}`) and OS keyring token storage - System info collection (hostname, OS, CPU, RAM, disk, username) - Help request submission (4-state flow: idle โ†’ composing โ†’ sending โ†’ sent) - Operator chat with E2E encryption - Settings panel (connection, privacy, general, about) - Tray icon with autostart, single-instance enforcement - Minimal UI (480ร—520 single window) - Full i18n (English + Polish, ~120 keys) **Build**: `cd betterdesk-agent-client && pnpm install && pnpm tauri build` ### Native CDAP Agent (Go) A headless Go binary for servers and IoT devices. - 9 system widgets (CPU, memory, disk, hostname, uptime) - Terminal, file browser, clipboard, screenshot capabilities - Systemd / NSSM service installers **Build**: `cd betterdesk-agent && go build -o betterdesk-agent .` --- ## ๐ŸŒ Web Remote Desktop BetterDesk includes a browser-based remote desktop client accessible from the Web Console. ### Features | Feature | HTTPS | HTTP | |---------|:-----:|:----:| | **Video codec** | WebCodecs (H.264, VP9, AV1, VP8) | JMuxer (H.264 only) | | **Target FPS** | 60 fps | 30 fps | | **Image quality** | Best (configurable) | Best (configurable) | | **Session recording** | โœ… WebM VP9+Opus at 15fps | โœ… | | **Monitor switching** | โœ… Display list with resolutions | โœ… | | **Quality presets** | Speed / Balanced / Quality / Best | Same | | **Keyboard/mouse** | โœ… Full input (modifiers, F-keys, wheel) | โœ… | | **Clipboard sync** | โœ… Bidirectional via navigator.clipboard | โŒ (HTTPS required) | | **Special keys** | Ctrl+Alt+Del, Win, PrintScreen, Alt+Tab | Same | | **Fullscreen** | โœ… F11 / button toggle | โœ… | | **Scale modes** | Fit / Fill / Original (1:1) | Same | ### Session Recording ```javascript // Start recording during a remote session client.startRecording(); // Canvas capture stream + audio at 15fps, WebM VP9+Opus // Stop and auto-download client.stopRecording(); // Produces .webm file download ``` ### Quality Presets | Preset | Image Quality | FPS | Use Case | |--------|:------------:|:---:|----------| | **Speed** | Low | 30 | Slow connections, text work | | **Balanced** | Balanced | 30 | General use | | **Quality** | Quality | 60 | Design work, presentations | | **Best** | Best | 60 | LAN, maximum fidelity | --- ## ๐Ÿ“‚ Project Structure ``` Rustdesk-FreeConsole/ โ”œโ”€โ”€ betterdesk-server/ # Go server (~20K LOC) โ”‚ โ”œโ”€โ”€ main.go # Entry point, flags, boot sequence โ”‚ โ”œโ”€โ”€ signal/ # Signal server (UDP/TCP/WS) โ”‚ โ”œโ”€โ”€ relay/ # Relay server (TCP/WS) โ”‚ โ”œโ”€โ”€ api/ # HTTP REST API + auth handlers โ”‚ โ”œโ”€โ”€ crypto/ # Ed25519, NaCl, Curve25519, address codec โ”‚ โ”œโ”€โ”€ db/ # Database interface + SQLite + PostgreSQL โ”‚ โ”œโ”€โ”€ config/ # Configuration, constants, TLS, DualModeListener โ”‚ โ”œโ”€โ”€ codec/ # Wire protocol framing (TCP + WS) โ”‚ โ”œโ”€โ”€ peer/ # Concurrent in-memory peer map (4-tier status) โ”‚ โ”œโ”€โ”€ security/ # IP/ID/CIDR blocklist โ”‚ โ”œโ”€โ”€ auth/ # JWT, PBKDF2, RBAC roles, TOTP 2FA โ”‚ โ”œโ”€โ”€ ratelimit/ # IP limiter, bandwidth limiter, conn limiter โ”‚ โ”œโ”€โ”€ metrics/ # Prometheus text exposition โ”‚ โ”œโ”€โ”€ audit/ # Ring-buffer audit log โ”‚ โ”œโ”€โ”€ events/ # Pub/sub event bus (11 event types) โ”‚ โ”œโ”€โ”€ logging/ # Text/JSON structured logging โ”‚ โ”œโ”€โ”€ admin/ # TCP management console โ”‚ โ”œโ”€โ”€ reload/ # Hot-reload (SIGHUP / manual) โ”‚ โ”œโ”€โ”€ proto/ # Generated protobuf (rendezvous + message) โ”‚ โ””โ”€โ”€ tools/migrate/ # Migration tool (5 modes) โ”œโ”€โ”€ web-nodejs/ # Node.js web console โ”‚ โ”œโ”€โ”€ server.js # Express app (dual port: 5000 + 21121) โ”‚ โ”œโ”€โ”€ routes/ # API routes (panel + Client API) โ”‚ โ”œโ”€โ”€ middleware/ # Security, CSRF, WAN, i18n, rate limiting โ”‚ โ”œโ”€โ”€ services/ # Auth, DB, WebSocket relay โ”‚ โ”œโ”€โ”€ views/ # EJS templates โ”‚ โ”œโ”€โ”€ public/ # Static assets (CSS, JS) โ”‚ โ””โ”€โ”€ lang/ # i18n translations (EN, PL) โ”œโ”€โ”€ betterdesk-mgmt/ # MGMT Desktop Client (Tauri v2 + SolidJS) โ”œโ”€โ”€ betterdesk-agent-client/ # Agent Client (Tauri v2 + SolidJS) โ”œโ”€โ”€ betterdesk-agent/ # Native CDAP Agent (Go) โ”œโ”€โ”€ sdks/ # CDAP Bridge SDKs (Python + Node.js) โ”œโ”€โ”€ bridges/ # Reference CDAP Bridges (Modbus, SNMP, REST) โ”œโ”€โ”€ betterdesk.sh # Linux ALL-IN-ONE installer (v2.4.0) โ”œโ”€โ”€ betterdesk.ps1 # Windows ALL-IN-ONE installer (v2.4.0) โ”œโ”€โ”€ betterdesk-docker.sh # Docker installer (v2.4.0) โ”œโ”€โ”€ docker-compose.yml # Docker orchestration โ”œโ”€โ”€ docs/ # Documentation (architecture, CDAP, SDK, security) โ”œโ”€โ”€ dev_modules/ # Development & testing utilities โ””โ”€โ”€ archive/ # Archived: Rust binaries, Flask console ``` --- ## ๐Ÿค Contributing Contributions are welcome! See [CONTRIBUTING.md](docs/development/CONTRIBUTING.md) for guidelines. ### Reporting Issues 1. Run diagnostics: `sudo ./betterdesk.sh` โ†’ option 8 2. Collect logs: `journalctl -u betterdesk-server -n 100` 3. Open a [GitHub Issue](https://github.com/UNITRONIX/Rustdesk-FreeConsole/issues) with system info, logs, and reproduction steps ### Pull Requests 1. Fork โ†’ branch (`feature/your-feature`) โ†’ commit โ†’ push โ†’ PR 2. Follow existing code style and conventions 3. Update documentation and i18n keys for new features 4. Test with real RustDesk clients on both Linux and Windows --- ## ๐Ÿ“„ License BetterDesk is licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0)** โ€” see [LICENSE](LICENSE). **Development status:** AGPL-3.0 applies to the active development branch (`dev`). Stable release **v3.4.0** will be the first version published under **AGPL-3.0**. Releases **v3.3.x and earlier** remain under **Apache 2.0** as originally published. ### Clean-Room Implementation BetterDesk Server (`betterdesk-server/`) is a **clean-room implementation** of the RustDesk rendezvous and relay protocol. Like how any HTTP server implements the HTTP protocol without being "derived from" Apache or Nginx, BetterDesk implements published protocol specifications for compatibility with RustDesk clients โ€” but contains **no RustDesk source code**. - **Go imports**: No `github.com/rustdesk/*` dependencies - **Code review**: No RustDesk copyright headers or attribution - **Protocol**: Uses independently authored `.proto` specifications BetterDesk Console (`web-nodejs/`) is an entirely original Node.js/Express application. ### Archive Directory The `archive/` directory (excluded from distribution via `.gitignore`) contains deprecated code from earlier development phases. Those files retain their original licensing and are never included in releases, packages, or Docker images. ### Trademark Notice "RustDesk" is a trademark of the RustDesk Team. BetterDesk is an independent project that implements the RustDesk protocol for client compatibility. Use of the name "RustDesk" in this project is purely descriptive (indicating protocol compatibility) and does not imply affiliation with or endorsement by the RustDesk Team. --- ## ๐Ÿ™ Credits - **[RustDesk](https://github.com/rustdesk/rustdesk)** โ€” The open-source remote desktop solution - **[RustDesk Server](https://github.com/rustdesk/rustdesk-server)** โ€” Original HBBS/HBBR reference implementation - **[pgx](https://github.com/jackc/pgx)** โ€” PostgreSQL driver for Go - **[coder/websocket](https://github.com/coder/websocket)** โ€” WebSocket library for Go - **[modernc.org/sqlite](https://modernc.org/sqlite)** โ€” Pure Go SQLite driver - **[Material Icons](https://fonts.google.com/icons)** โ€” Google Material Design icons --- ## ๐Ÿ“ž Support - **Documentation**: [docs/](docs/) - **Issues**: [GitHub Issues](https://github.com/UNITRONIX/Rustdesk-FreeConsole/issues) - **Discussions**: [GitHub Discussions](https://github.com/UNITRONIX/Rustdesk-FreeConsole/discussions) ---
**Made with โค๏ธ by UNITRONIX & the community** If you find this project useful, please consider giving it a โญ on GitHub! [โฌ† Back to Top](#-betterdesk--rustdesk-compatible-server--web-console)