Access your computers from any browser — no app needed.
rclient is an open-source web client for RustDesk that works with your self-hosted RustDesk relay server. Type a machine ID and password in your browser and you're in — keyboard, mouse, and live video, all without installing anything.
🌐 Live demo: rclient.linkzy.dev
(Demo connects to a real relay server in São Paulo, Brazil. You can test with your own RustDesk machine ID and password.)
RustDesk is a great open-source remote desktop tool, and running your own relay server gives you full control and low latency. But the native apps must be installed on every device you want to control from.
rclient fills that gap: once it's running on your VPS, you can connect to any of your machines from any browser — a friend's computer, a phone, a work machine where you can't install software.
Your Browser
│
│ HTTPS/WSS (Cloudflare Tunnel or your reverse proxy)
▼
rclient Web UI ──► rclient Gateway
(React + WebCodecs) (Node.js)
│
RustDesk protocol
(WebSocket, encrypted)
│
┌──────▼──────┐
│ Your VPS │
│ hbbs :21118│ ← rendezvous
│ hbbr :21119│ ← relay
└──────┬──────┘
│
Remote Machine
(RustDesk host app)
- Gateway (Node.js) — speaks the RustDesk binary protocol, handles encryption, and bridges the browser to your relay server
- Web UI (React) — renders the remote screen using the browser's built-in WebCodecs API, captures keyboard and mouse input
- A Linux VPS (1 GB RAM minimum — Oracle Cloud free tier works great)
- Docker and Docker Compose installed
- A domain name (or a free Cloudflare Tunnel for HTTPS without a static IP)
| Browser | Support |
|---|---|
| Chrome / Edge 94+ | ✅ Full |
| Firefox 130+ | ✅ Full |
| Safari 16.4+ | |
| Mobile browsers |
This guide takes you from zero to a working browser-based remote desktop. There are three parts:
The host is the computer you want to control remotely (e.g. your home PC).
- Download and install RustDesk on it
- Open RustDesk. Note your Machine ID and set a Password (the one you'll type in rclient later)
- Go to Settings → Network and configure your relay server:
- ID/Relay Server: your VPS IP or domain (e.g.
relay.yourdomain.com) - Key: the server public key (you'll get this in Step 2)
- ID/Relay Server: your VPS IP or domain (e.g.
- Make sure RustDesk is running (it can run as a background service)
💡 The host machine just needs to be on the internet and reachable via your relay server — no port forwarding required.
Skip this step if you already have a relay server running. If you're using public RustDesk servers, rclient will still work — but running your own gives you lower latency and full privacy.
On your VPS, create a working directory and compose file:
mkdir -p ~/rustdesk/data
cd ~/rustdeskCreate docker-compose.infra.yml:
version: "3"
networks:
rustdesk-net:
name: rustdesk-net
services:
hbbs:
image: rustdesk/rustdesk-server:latest
container_name: hbbs
command: hbbs
ports:
- "21115:21115"
- "21116:21116"
- "21116:21116/udp"
- "21118:21118"
volumes:
- ./data:/root
networks:
- rustdesk-net
restart: unless-stopped
depends_on:
- hbbr
hbbr:
image: rustdesk/rustdesk-server:latest
container_name: hbbr
command: hbbr
ports:
- "21117:21117"
- "21119:21119"
volumes:
- ./data:/root
networks:
- rustdesk-net
restart: unless-stoppedStart it and grab the server key:
docker compose -f docker-compose.infra.yml up -d
docker logs hbbs 2>&1 | grep "Key:"The key looks like: zJDp2iyK8zXDFZYUQxyftU+C9254fdIb3Xmngpj9DfI=
Go back to Step 1 and paste this key into RustDesk's Network → Key field on your host machine.
On your VPS (same machine as the relay, or a different one), create docker-compose.app.yml:
version: "3"
networks:
rustdesk-net:
external: true
name: rustdesk-net
services:
gateway:
image: ghcr.io/linkzy/rclient-gateway:latest
container_name: rclient-gateway
environment:
- HBBS_HOST=${HBBS_HOST:-hbbs}
- HBBS_WS_PORT=${HBBS_WS_PORT:-21118}
- HBBR_HOST=${HBBR_HOST:-hbbr}
- HBBR_WS_PORT=${HBBR_WS_PORT:-21119}
- SERVER_KEY=${SERVER_KEY}
networks:
- rustdesk-net
restart: unless-stopped
web:
image: ghcr.io/linkzy/rclient-web:latest
container_name: rclient-web
ports:
- "127.0.0.1:8080:80"
networks:
- rustdesk-net
restart: unless-stopped
depends_on:
- gatewayCreate a .env file next to it:
SERVER_KEY=your-server-key-hereStart it:
docker compose -f docker-compose.app.yml up -dThe web UI is now running on http://localhost:8080.
Browsers require HTTPS for WebCodecs to work. Two options:
Install cloudflared:
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.debLog in and create a tunnel:
cloudflared tunnel login
cloudflared tunnel create rclientCreate /etc/cloudflared/config.yml:
tunnel: <your-tunnel-id>
credentials-file: /root/.cloudflared/<your-tunnel-id>.json
ingress:
- hostname: rclient.yourdomain.com
service: http://localhost:8080
- service: http_status:404Add the DNS record and start:
cloudflared tunnel route dns rclient rclient.yourdomain.com
sudo cloudflared service install
sudo systemctl enable --now cloudflaredExample with Caddy:
rclient.yourdomain.com {
reverse_proxy localhost:8080
}
- Open
https://rclient.yourdomain.comin your browser - Enter the Machine ID from RustDesk on the host machine
- Enter the Password you set in RustDesk
- Click Connect
You should see the remote screen within a few seconds.
| Variable | Description | Default |
|---|---|---|
SERVER_KEY |
Server public key (base64, from hbbs logs) | required |
HBBS_HOST |
Rendezvous server hostname | hbbs |
HBBS_WS_PORT |
hbbs WebSocket port | 21118 |
HBBR_HOST |
Relay server hostname | hbbr |
HBBR_WS_PORT |
hbbr WebSocket port | 21119 |
rclient is functional and usable. On a well-located relay server (e.g. same region as your host machine):
- Keyboard/mouse input: near-instant (< 10ms perceived latency)
- Video: 3–16 FPS depending on screen activity (typing, browsing = fine; fast video = choppy)
- Latency: determined mainly by your relay server location — co-locating relay and host in the same region helps most
The video frame rate is not yet at the native app's level (30 FPS). This is a known area of active improvement. See the Roadmap.
| Issue | Status |
|---|---|
| Video capped at ~16 FPS | Under improvement |
| Keyboard layout / Caps Lock mismatch (KI-005) | Known bug, fix pending |
| Mouse scroll wheel inverted (KI-004) | Known bug, fix pending |
| No audio | Planned (see Roadmap) |
| No file transfer | Planned |
| Single monitor only | Planned |
| Mobile touch input | Partial — basic mouse works |
Features not yet implemented but planned:
- Audio streaming — receive and play remote audio in the browser
- File transfer — upload/download files to/from the remote machine
- Multi-monitor support — switch between displays
- Keyboard layout fix (KI-005) — correct character mapping for non-US layouts
- Scroll direction fix (KI-004) — invert mouse wheel delta
- Browser-level authentication — protect the rclient UI with a login page (currently anyone with the URL can attempt connections)
- Mobile touch support — proper touch-to-mouse mapping for phones and tablets
- Clipboard sync — paste text into the remote machine
- Session persistence — reconnect automatically after network drop
- 30 FPS — further protocol tuning to match native app frame rate
- Never expose the gateway port (4000) publicly — only the
webcontainer should be public-facing. The gateway is automatically internal-only in the provided compose setup. - Use HTTPS — required for WebCodecs and important for protecting your connection password in transit.
- Add browser-level authentication — currently the rclient UI is open to anyone who knows the URL. Until a login page is added (see Roadmap), consider restricting access at the reverse proxy level (e.g. HTTP Basic Auth in nginx/Caddy).
- Passwords are never stored — rclient only uses the password during the RustDesk handshake and never logs or persists it.
rclient/
├── gateway/ ← Node.js/TypeScript backend (RustDesk protocol bridge)
│ └── src/
│ ├── proto/ ← .proto files
│ ├── rendezvous.ts ← hbbs WebSocket client
│ ├── relay.ts ← hbbr WebSocket client
│ ├── session.ts ← per-connection session (encryption, video, input)
│ └── server.ts ← WebSocket server facing the browser
├── web/ ← React/TypeScript frontend
│ └── src/
│ ├── components/ ← ConnectForm, RemoteScreen
│ ├── hooks/ ← useGateway (WebSocket connection)
│ └── video/ ← WebCodecs decoder
├── docker-compose.yml
├── docker-compose.prod.yml
└── docs/ ← Technical documentation
For contributors and AI agents, the full technical docs are in docs/:
| File | Contents |
|---|---|
docs/AI_GUIDELINES.md |
Architecture overview, coding conventions, common pitfalls |
docs/ARCHITECTURE.md |
System design, component diagram, gateway WebSocket API |
docs/PROTOCOL.md |
RustDesk protocol reference (protobuf, encryption, codecs) |
docs/KNOWN_ISSUES.md |
Confirmed bugs with root cause analysis |
- Read
docs/AI_GUIDELINES.mdfirst - Check
docs/KNOWN_ISSUES.mdbefore touchingsession.ts - Pick something from the Roadmap or Known Issues
- Open a PR
MIT