288 lines
7.1 KiB
Markdown
288 lines
7.1 KiB
Markdown
# Remote Desktop
|
|
|
|
Holesail Browser supports VNC and RDP connections in the browser, tunneled over Holesail. VNC uses the noVNC RFB client; RDP uses `node-rdpjs-2`. Both are accessed from the dashboard's **Remote Desktop** tab.
|
|
|
|
## VNC vs RDP
|
|
|
|
| Feature | VNC | RDP |
|
|
|---------|-----|-----|
|
|
| Protocol | RFB (VNC) | RDP (Microsoft) |
|
|
| Browser client | noVNC (WebSocket → RFB) | node-rdpjs-2 (JSON bridge) |
|
|
| Performance | Good for most use cases | Moderate (JSON serialization overhead) |
|
|
| Compatibility | Any VNC server | Windows Remote Desktop, FreeRDP |
|
|
| Default port | 5900 | 3389 |
|
|
|
|
## Architecture
|
|
|
|
### VNC
|
|
|
|
```
|
|
Dashboard (noVNC / canvas)
|
|
│ WebSocket ws://127.0.0.1:23000+ (binary frames)
|
|
▼
|
|
rdp-manager.js (transparent byte pipe)
|
|
│ TCP
|
|
▼
|
|
127.0.0.1:22000+ (Holesail client tunnel)
|
|
│ P2P / Noise protocol
|
|
▼
|
|
Remote VNC server (port 5900)
|
|
```
|
|
|
|
For VNC, the native host is a transparent pipe — it does not interpret the RFB protocol. noVNC handles all VNC protocol logic in the browser.
|
|
|
|
### RDP
|
|
|
|
```
|
|
Dashboard (canvas renderer)
|
|
│ WebSocket ws://127.0.0.1:23000+ (JSON frames)
|
|
▼
|
|
rdp-manager.js (node-rdpjs-2 RDP client)
|
|
│ TCP
|
|
▼
|
|
127.0.0.1:22000+ (Holesail client tunnel)
|
|
│ P2P / Noise protocol
|
|
▼
|
|
Remote RDP server (port 3389)
|
|
```
|
|
|
|
For RDP, `node-rdpjs-2` handles the RDP protocol in the native host. Bitmap updates are serialized to JSON and sent to the browser, which renders them on a canvas. Mouse and keyboard input is sent as JSON from the browser to the native host.
|
|
|
|
## Usage
|
|
|
|
### Saving a connection
|
|
|
|
1. Open the dashboard → **Remote Desktop**
|
|
2. Click **Add Connection**
|
|
3. Enter:
|
|
- **Label** — a name for this connection
|
|
- **Type** — VNC or RDP
|
|
- **hs:// key** — the Holesail key for the remote peer
|
|
- **Port** — remote port (default: 5900 for VNC, 3389 for RDP)
|
|
- **Width / Height** — initial display resolution
|
|
- **Username** — (RDP only) login username
|
|
- **Password** — optional; saved as base64 in `state.json` and used automatically on reconnect
|
|
4. Click **Save**
|
|
|
|
### Connecting
|
|
|
|
1. Click **Connect** next to a saved connection
|
|
2. A viewer window opens in the dashboard
|
|
3. The native host starts a Holesail tunnel to the remote peer and begins the session
|
|
|
|
### Disconnecting
|
|
|
|
Click **Disconnect** or close the viewer. The tunnel and WebSocket server are cleaned up.
|
|
|
|
---
|
|
|
|
## Native host commands
|
|
|
|
### `startRdpSession`
|
|
|
|
Start a new VNC or RDP session.
|
|
|
|
**Request payload:**
|
|
```json
|
|
{
|
|
"connectionId": "rdp-abc123",
|
|
"hsUrl": "hs://abc123...",
|
|
"type": "vnc",
|
|
"port": 5900,
|
|
"width": 1280,
|
|
"height": 720,
|
|
"username": "",
|
|
"passwordB64": "cGFzc3dvcmQ="
|
|
}
|
|
```
|
|
|
|
`passwordB64` is optional. When present it is a base64-encoded UTF-8 password string used for RDP authentication.
|
|
|
|
**Response payload:**
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"sessionId": "rdp-session-1",
|
|
"wsPort": 23000,
|
|
"tunnelPort": 22000
|
|
}
|
|
```
|
|
|
|
### `stopRdpSession`
|
|
|
|
Stop a running session.
|
|
|
|
**Request payload:**
|
|
```json
|
|
{ "sessionId": "rdp-session-1" }
|
|
```
|
|
|
|
**Response payload:**
|
|
```json
|
|
{ "ok": true }
|
|
```
|
|
|
|
### `getRdpSessions`
|
|
|
|
List active sessions.
|
|
|
|
**Response payload:**
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"sessions": [
|
|
{ "sessionId": "rdp-session-1", "connectionId": "rdp-abc123", "type": "vnc", "wsPort": 23000 }
|
|
]
|
|
}
|
|
```
|
|
|
|
### `setRdpConnections`
|
|
|
|
Save RDP/VNC connection definitions to `state.json`.
|
|
|
|
**Request payload:**
|
|
```json
|
|
{
|
|
"connections": [
|
|
{
|
|
"id": "rdp-abc123",
|
|
"label": "Work PC",
|
|
"hsUrl": "hs://abc...",
|
|
"type": "vnc",
|
|
"port": 5900,
|
|
"width": 1280,
|
|
"height": 720,
|
|
"username": "",
|
|
"passwordB64": "cGFzc3dvcmQ="
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
`passwordB64` is optional. When present it is a base64-encoded UTF-8 password string (`btoa(unescape(encodeURIComponent(password)))`). It is persisted in `state.json` and used automatically when starting a session.
|
|
|
|
---
|
|
|
|
## WebSocket protocol
|
|
|
|
### VNC
|
|
|
|
The dashboard connects to `ws://127.0.0.1:<wsPort>`. All frames are binary — raw RFB bytes passed transparently between noVNC and the VNC server. The native host does not interpret the protocol.
|
|
|
|
### RDP
|
|
|
|
**Native host → browser (connection established):**
|
|
```json
|
|
{ "type": "connected", "width": 1280, "height": 720 }
|
|
```
|
|
|
|
**Native host → browser (bitmap updates):**
|
|
```json
|
|
{
|
|
"type": "bitmap",
|
|
"destLeft": 100,
|
|
"destTop": 50,
|
|
"destRight": 300,
|
|
"destBottom": 150,
|
|
"width": 200,
|
|
"height": 100,
|
|
"bitsPerPixel": 32,
|
|
"isCompress": false,
|
|
"data": "<base64-encoded bitmap>"
|
|
}
|
|
```
|
|
|
|
**Native host → browser (session events):**
|
|
```json
|
|
{ "type": "close" }
|
|
{ "type": "error", "message": "Authentication failed" }
|
|
```
|
|
|
|
**Browser → native host (mouse input):**
|
|
```json
|
|
{ "type": "mouseMove", "x": 150, "y": 75 }
|
|
{ "type": "mouseButton", "x": 150, "y": 75, "button": 1, "isDown": true }
|
|
{ "type": "mouseButton", "x": 150, "y": 75, "button": 1, "isDown": false }
|
|
```
|
|
|
|
**Browser → native host (keyboard input):**
|
|
```json
|
|
{ "type": "keyEvent", "code": 65, "isDown": true }
|
|
{ "type": "keyEvent", "code": 65, "isDown": false }
|
|
{ "type": "keyUnicode", "code": 65, "isDown": true }
|
|
```
|
|
|
|
> **Note:** `keyEvent` sends a scancode via `sendKeyEventScancode`; `keyUnicode` sends a Unicode code point via `sendKeyEventUnicode`. Use `keyEvent` for special keys (arrows, function keys, modifiers) and `keyUnicode` for printable characters.
|
|
|
|
---
|
|
|
|
## Port allocation
|
|
|
|
| Range | Usage |
|
|
|-------|-------|
|
|
| 22000+ | Holesail client tunnel (one per session) |
|
|
| 23000+ | WebSocket server (one per session) |
|
|
|
|
---
|
|
|
|
## Saved connections
|
|
|
|
RDP/VNC connections are stored in `state.json` under `rdpConnections`:
|
|
|
|
```json
|
|
{
|
|
"rdpConnections": [
|
|
{
|
|
"id": "rdp-abc123",
|
|
"label": "Work PC",
|
|
"hsUrl": "hs://abc123...",
|
|
"type": "vnc",
|
|
"port": 5900,
|
|
"width": 1280,
|
|
"height": 720,
|
|
"username": "",
|
|
"passwordB64": "cGFzc3dvcmQ="
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
`passwordB64` is omitted when no password was saved. Connections are restored on startup. Active sessions are not persisted — they must be reconnected after a native host restart.
|
|
|
|
---
|
|
|
|
## Requirements
|
|
|
|
### VNC
|
|
|
|
- A VNC server on the remote machine (e.g. TigerVNC, RealVNC, macOS Screen Sharing)
|
|
- The VNC server must be accessible via a Holesail `hs://` key
|
|
|
|
### RDP
|
|
|
|
- Windows Remote Desktop enabled on the remote machine, or a compatible RDP server (e.g. xrdp on Linux)
|
|
- The RDP server must be accessible via a Holesail `hs://` key
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
**Connection times out**
|
|
|
|
- The remote peer may be offline or the `hs://` key may be wrong
|
|
- Check `~/.holesail-browser/holesail-browser.log` for tunnel errors
|
|
|
|
**VNC: black screen or no updates**
|
|
|
|
- Verify the VNC server is running and listening on the expected port
|
|
- Try disconnecting and reconnecting
|
|
|
|
**RDP: authentication fails**
|
|
|
|
- Verify the username and password on the remote machine
|
|
- Ensure the remote machine has Remote Desktop enabled and the user has permission to connect
|
|
|
|
**RDP: slow or choppy display**
|
|
|
|
RDP performance depends on the latency of the Holesail tunnel and the speed of JSON serialization. For better performance, use VNC if the remote machine supports it.
|