Files
holesail-browser/docs/REMOTE-DESKTOP.md
T
Raven Scott f5c349a4a3
CI / Build & Test (push) Successful in 2m56s
docs
2026-02-28 22:29:56 -05:00

7.1 KiB

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:

{
  "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:

{
  "ok": true,
  "sessionId": "rdp-session-1",
  "wsPort": 23000,
  "tunnelPort": 22000
}

stopRdpSession

Stop a running session.

Request payload:

{ "sessionId": "rdp-session-1" }

Response payload:

{ "ok": true }

getRdpSessions

List active sessions.

Response payload:

{
  "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:

{
  "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):

{ "type": "connected", "width": 1280, "height": 720 }

Native host → browser (bitmap updates):

{
  "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):

{ "type": "close" }
{ "type": "error", "message": "Authentication failed" }

Browser → native host (mouse input):

{ "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):

{ "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:

{
  "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.