Files
rustdesk/docs/CLIENT_VS_SERVER_GAPS.md
T
mike 47f0d0fff2
build-linux / build-linux-x64 (push) Successful in 5m23s
build-macos / build-macos-x64 (push) Successful in 9m4s
build-windows / build-windows-x64 (push) Successful in 10m13s
Implement CI workflow for Gitea. Include provision scripts for Gitea runners.
2026-05-07 09:39:23 +02:00

19 KiB

RustDesk Client vs OSS Server — Feature Gap Analysis

This document compares the RustDesk client (/Users/sn0/Desktop/rustdesk) against the OSS server (/Users/sn0/Desktop/rustdesk-server) and lists every feature the client implements or expects, but that the OSS server (hbbs + hbbr + rustdesk-utils) does not provide.

The OSS server is, by upstream's own description in its README, deliberately minimal:

Self-host your own RustDesk server, it is free and open source. If you want extra features, RustDesk Server Pro might suit you better.

Almost every gap below is filled by RustDesk Server Pro (closed source). What follows is the concrete list.


TL;DR

Area OSS server Client expects
Rendezvous protocol (UDP/TCP/WS) implemented
Relay protocol (hbbr) implemented
RegisterPk over TCP NOT_SUPPORT Uses UDP — non-issue if UDP reachable
HttpProxyRequest / HttpProxyResponse (HTTP-via-rendezvous tunnel) no handler Used when USE_RAW_TCP_FOR_API=Y
Entire /api/* HTTP surface (35 endpoints in CONSOLE_API.md) no HTTP server All login, AB, group, audit, sysinfo, etc.
User / password / token authentication Bearer-token model
Address book (legacy or shared) Personal AB, shared AB, tags, peers
Device groups, users, accessible peers list "Group" tab in UI
Audit logging (conn, file, alarm, note) Background fire-and-forget
Session recording upload Chunked uploader
Sysinfo / heartbeat-based device tracking Every 15 s (3 s when active)
Strategy/policy push via heartbeat config_options / disconnect / sysinfo flag
2FA, email-code, SMS-code, OIDC/SSO Login challenge variants
Plugin signing (/lic/web/api/plugin-sign) Optional, only if signed plugins shipped
CLI bulk device assignment (rustdesk --assign) POST /api/devices/cli
Per-tenant licensing / Pro status flag Inferred from sysinfo success

1. Rendezvous-protocol gaps

The OSS server does speak the rendezvous protocol — it accepts and responds to all of the variants the client sends in the normal connection path. There are exactly two variant-level gaps:

1.1 RegisterPk over TCP — explicitly rejected

src/rendezvous_server.rs:556-563 returns RegisterPkResponse { result: NOT_SUPPORT } for any TCP-arriving RegisterPk. UDP works fine. The client has UDP RegisterPk as the primary path (src/rendezvous_mediator.rs:685-693), so this is only an issue in fully UDP-blocked environments where the client falls back to TCP for everything.

1.2 HttpProxyRequest / HttpProxyResponse — no handler

When USE_RAW_TCP_FOR_API is enabled (and WebSocket is off), the client tunnels its HTTP API calls as protobuf messages over the rendezvous server's TCP socket — see src/common.rs:1188-1250 (tcp_proxy_request).

  • Wire format: an encrypted KeyExchange handshake, then a RendezvousMessage::set_http_proxy_request(HttpProxyRequest { method, path, headers, body }), expecting HttpProxyResponse { status_code, headers, body } back.
  • OSS handling: there is no match arm for HttpProxyRequest in /Users/sn0/Desktop/rustdesk-server/src/rendezvous_server.rs; it falls into the _ => {} catch-all and is silently dropped.
  • Impact: any client that has been pushed OPTION_USE_RAW_TCP_FOR_API=Y (typical for restricted networks) cannot reach the HTTP API at all on an OSS-only deployment, even if the API itself were implemented.

1.3 What the OSS server does handle (not gaps, for completeness)

  • UDP: RegisterPeer, RegisterPk, PunchHoleRequest, PunchHoleSent, LocalAddr, ConfigureUpdate (loopback only), SoftwareUpdate.
  • TCP: PunchHoleRequest, RequestRelay, RelayResponse, PunchHoleSent, LocalAddr, TestNatRequest.
  • TCP listener2 (port - 1): TestNatRequest, OnlineRequest, plus a loopback admin CLI.
  • WebSocket on port + 2 (default 21118): same handler set as TCP.
  • The relay (hbbr) listens on port (default 21117) and port + 2 (WebSocket, default 21119), and handles RequestRelay for tunnelling.

So the rendezvous + relay surface for plain peer-to-peer use is complete in the OSS server. All the actual gaps are above the rendezvous layer.


2. HTTP API — none of it exists in OSS

/Users/sn0/Desktop/rustdesk-server/Cargo.toml pulls in axum and tower-http, but they are unused — no Router::new, no route definitions, no HTTP listener bound. All 35 endpoints documented in CONSOLE_API.md are gaps.

Grouped by feature area:

2.1 Authentication & session (/api/login, /api/login-options, /api/logout, /api/currentUser)

  • No user table in the OSS schema (which is a single peer table — see §4 below). No password storage, hashing, salting.
  • No bearer-token issuance or validation.
  • No email_check / tfa_check challenge types (no email sender, no TOTP store).
  • No login-options registry — client always gets an empty oidc/... list.
  • No OIDC / SSO device flow (/api/oidc/auth, /api/oidc/auth-query).

2.2 Address book — both modes missing

The client supports two AB modes (CONSOLE_API.md §4) and the OSS server implements neither:

  • Legacy single-blob mode (GET/POST /api/ab) — needs a per-user blob store + gzip handling.
  • Shared mode (/api/ab/settings, /api/ab/personal, /api/ab/shared/profiles, /api/ab/peers, /api/ab/tags/{guid}, plus the per-peer and per-tag CRUD on {guid}) — needs a normalized AB / peer / tag / share-rule schema with read | read/write | full control ACLs.

Without this, the client falls back to its local-only "Recents" / "Favorites" lists; nothing syncs across devices.

2.3 Device groups, users, accessible peers (/api/device-group/accessible, /api/users, /api/peers)

The "Group" tab in the desktop UI populates from these three endpoints. With OSS the tab is empty; the client logs get accessible device groups: <error> and silently swallows it.

2.4 Heartbeat / sysinfo / strategy push (/api/heartbeat, /api/sysinfo, /api/sysinfo_ver)

This is the agent-management heartbeat loop (CONSOLE_API.md §6).

OSS gaps:

  • No device-tracking table (last-seen, OS, hostname, version, IP, online state).
  • No sysinfo cache or version string.
  • No mechanism to push back in the heartbeat response:
    • sysinfo: <truthy> to force an immediate sysinfo re-upload.
    • disconnect: [conn_id, ...] to force a remote session to close.
    • modified_at / strategy.config_options to push policy.
  • Without strategy push, the operator cannot remotely set:
    • whitelist, relay-server, rendezvous-servers, direct-access-port, stop-service, OPTION_DISABLE_UDP, OPTION_ENABLE_UDP_PUNCH, OPTION_ENABLE_IPV6_PUNCH, OPTION_USE_RAW_TCP_FOR_API, OPTION_DIRECT_SERVER, etc.
    • Or any of the OPTION_PRESET_* keys that pre-fill the address book / username / device group / strategy on a freshly enrolled client.
  • is_pro() is set to true only when /api/sysinfo returns SYSINFO_UPDATED (src/hbbs_http/sync.rs:219) — with OSS, is_pro() is permanently false, so any client behavior gated on it is disabled.

2.5 Audit (/api/audit/conn, /api/audit/file, /api/audit/alarm, PUT /api/audit)

The client emits these fire-and-forget on every:

Even though the client doesn't block on these, OSS silently swallows all of them, so:

  • No central session log.
  • No file-transfer log (incl. the top-10-by-size summary the client computes).
  • No alarm notifications for IP-whitelist hits or brute-force attempts.
  • No audit-row GUID exists, so the "leave a note when the session ends" dialog has nothing to attach to.

2.6 Session recording upload (POST /api/record)

Chunked uploader (src/hbbs_http/record_upload.rs) with ?type=new|part|tail|remove. OSS has no /api/record route and no on-disk recording store, so server-side recording is impossible.

(Local-only recording on the controlling side still works — that's not a server feature.)

2.7 OIDC / SSO device flow (/api/oidc/auth, /api/oidc/auth-query)

The polled device-code flow (src/hbbs_http/account.rs) requires an OIDC client implementation, browser-flow URL generation, and a poll-for-token side. None of this is in OSS.

2.8 CLI bulk assign (POST /api/devices/cli)

src/core_main.rs:519-616. Used by rustdesk --assign --token ... for mass-deploy scripts to register a freshly-installed agent into a tenant, optionally setting user_name, strategy_name, address_book_*, device_group_name, device_username, device_name, note. Requires user/group/AB tables, none of which exist in OSS.

2.9 Plugin signature service (POST /lic/web/api/plugin-sign)

src/plugin/callback_msg.rs:282-296. Required only if the deployment ships signed plugins. OSS has no plugin infrastructure of any kind.

2.10 Generic file downloader

HEAD then GET against an arbitrary URL with a required Content-Length (src/hbbs_http/downloader.rs). Works against any static file server — OSS doesn't need to serve this, but a complete Pro-replacement backend usually exposes installer/plugin/recording downloads via this.


3. Schema gaps

The OSS database is a single SQLite table called peer:

guid (PK), id (UNIQUE), uuid, pk, created_at, user (unused),
status (unused), note (unused), info (JSON: { ip })

/Users/sn0/Desktop/rustdesk-server/src/database.rs:71-144.

To support the client's HTTP surface a backend needs at minimum:

  • users (id, name, display_name, avatar, email, note, password_hash, status, is_admin, totp_secret, oidc_subject, …)
  • tokens (token_hash, user_id, expires_at)
  • oidc_sessions (poll_code, state, created_at, access_token, …)
  • address_books (guid, owner_user_id, name, note, kind=personal|shared)
  • address_book_shares (ab_guid, user_or_group_id, rule={1,2,3})
  • address_book_peers (ab_guid, peer_id, alias, tags[], note, password|hash, username, hostname, platform)
  • address_book_tags (ab_guid, name, color)
  • device_groups (id, name)
  • device_group_members (device_group_id, user_or_group_id)
  • peers_extended (peer_id, user_id, device_group_id, last_seen, version, sysinfo_blob, sysinfo_hash, sysinfo_ver, online, …)
  • audit_conn (guid, peer_id, conn_id, session_id, action, ip, started_at, ended_at, note)
  • audit_file (peer_id, peer_remote, type, path, is_file, info_json)
  • audit_alarm (peer_id, typ, info_json)
  • recordings (filename, peer_id, size, header_blob, started_at, finished_at)
  • strategies (id, name, modified_at, config_options_json, extra_json)
  • peer_strategy_assignment / device_group_strategy_assignment

The OSS schema covers exactly one row of one of those tables (peers_extended.peer_id plus pk/uuid). Everything else is a gap.


4. Authentication / authorization gaps

  • No user/password. The OSS server identifies a peer entirely by (id, uuid, pk). There is no concept of a logged-in human user, no password, no session, no role.
  • No bearer tokens. The client adds Authorization: Bearer <access_token> to every authenticated HTTP call (flutter/lib/common.dart:2691-2695). With no HTTP API and no user store, OSS has nothing to validate against.
  • No 2FA. The client supports TOTP challenge (type: tfa_check, tfa_type, secret) and per-device 2FA-trust (src/ui_session_interface.rs). Not present in OSS.
  • No email/SMS verification. The email_check challenge type and verificationCode field have no sender on the OSS side.
  • No SSO / OIDC. No identity-provider integration.
  • No admin/role concept. The UserPayload.is_admin flag (used to gate the user-management UI) has no source.
  • No per-AB ACL. AbProfile.rule (read / read-write / full control) has no enforcement layer.
  • No IP allowlisting / per-IP rate-limiting on HTTP endpoints. OSS rate-limits RegisterPk per source IP (src/rendezvous_server.rs:891-919) but that's at the rendezvous layer only.

What the OSS server does offer in this space:

  • Optional symmetric server key (-k) checked against licence_key in PunchHoleRequest and RequestRelay. This is shared-secret deployment lockdown, not user auth.
  • Ed25519 signing of RelayResponse payloads using the server's private key.

5. Operations / fleet management gaps

These are conveniences a Pro server offers via the strategy/heartbeat channel; OSS has no equivalent because heartbeat itself is not implemented.

  • Force-disconnect a remote session from the admin console (heartbeat returns disconnect: [conn_id]src/hbbs_http/sync.rs:251-254).
  • Force-refresh sysinfo (sysinfo truthy in heartbeat).
  • Push global config to all enrolled agents (the strategy.config_options map). Without this, every option must be set per-machine.
  • Pre-seed an agent at install time with an address-book entry, alias, password, note, strategy, device group, custom hostname/username (OPTION_PRESET_ADDRESS_BOOK_*, OPTION_PRESET_USERNAME, OPTION_PRESET_STRATEGY_NAME, OPTION_PRESET_DEVICE_GROUP_NAME, …). Client emits these preset values on every sysinfo, but OSS discards them.
  • Operator end-of-session notes (PUT /api/audit).
  • rustdesk --assign --token … for mass deployment.
  • Brute-force / IP-whitelist alarms to a central log.

What OSS does offer for ops:

  • A loopback-only TCP admin CLI on port - 1 (default 21115) for hbbs and on port for hbbr (src/rendezvous_server.rs:1102-1116, src/relay_server.rs:152-323) — relay-servers, ip-blocker, ip-changes, punch-requests, always-use-relay, test-geo, blacklist-add, blocklist-add, total-bandwidth, usage, etc.
  • A ConfigureUpdate push only from loopback — the operator can update the rendezvous-server list pushed to clients, but only by nc 127.0.0.1 21115 on the server box itself.

6. Client-side features that work fine against OSS

For balance — these features in the client need no Pro-server support at all:

  • All in-session protocol (after the relay/direct connection is established): screen sharing, file transfer, terminal, RDP / VNC tunnel, port forward, voice call, view-only mode, whiteboard, printer, clipboard, multi-monitor, mouse/keyboard injection. These are negotiated on the session stream itself and never touch the management server.
  • LAN discovery (when both ends are reachable on the same LAN, no rendezvous server needed at all).
  • The client's local 2FA on the controlled side ("ask the operator for a one-time code"). That's a peer-to-peer protocol negotiation, not a server feature.
  • IP-whitelist enforcement on the controlled side (src/server/connection.rs:1202-1228) — done locally against the whitelist config option. (But the operator UX of pushing that whitelist to a fleet is missing — see §5.)
  • Self-update — the client checks a hardcoded URL on the public update server, not the configured rendezvous/API server.
  • Custom-server bootstrap via filename (rustdesk-host=…,key=…,api=…,relay=….exe, src/custom_server.rs) — works against OSS as long as the api= field is left empty / public.

7. What you'd need to build to fully replace Pro

Given the analysis above, a full Pro-replacement backend on top of OSS would need:

  1. Add an HTTP server (axum is already in the Cargo.toml of OSS, unused). Implement the 35 routes in CONSOLE_API.md.
  2. Add a HttpProxyRequest handler in rendezvous_server.rs so that locked-down clients can reach the HTTP API through the rendezvous TCP port (decode the protobuf, replay the request internally, wrap the response).
  3. Extend the schema along the lines of §3.
  4. Add user / token / OIDC / 2FA layers, plus an email sender for email_check.
  5. Implement the strategy / push-config side of /api/heartbeat and the sysinfo cache for /api/sysinfo*.
  6. Add audit + recording stores with retention and access-control.
  7. (Optional) Plugin signing service if you're shipping signed plugins.

The rendezvous + relay protocol itself does not need to change — OSS is correct and complete there.


Source-of-truth references