JVNC Developer & Protocol Reference

For anyone maintaining JVNC or writing a compatible client. Start with Architecture & Code Map for the hot paths and the feature framework; this page is the wire-level reference.


1. Solution layout

Project Contents
JVNC.Core Protocol shared by both apps: Rfb.cs (constants, big-endian helpers), RfbPixelFormat.cs, FileTransfer.cs, Security.cs (pre-handshake, TOTP, certificates, allow list, lockout, address classes), Dpapi.cs, JsonSettings.cs, AppLog.cs. No UI, no Win32 except DPAPI.
JVNC.Server WPF tray app. RfbServer.cs (listener, per-client reader task + update task, security handshake), ScreenCapture.cs (GDI BitBlt + cursor compositing), InputInjector.cs (SetCursorPos + SendInput), MainWindow, SecurityWindow.
JVNC.Client WPF viewer. RfbClient.cs (handshake, receive loop decoding into a client-side framebuffer, senders), MainWindow (painting, input coalescing, full screen), WinKeyHook.cs, KeySym.cs, SecurityWindow.
Installer/JVNC.Installer Self-contained WPF setup program; the app files are embedded at build time.
docs/ These manuals (Markdown source; render.py produces HTML).

Build: dotnet build JVNC.sln -c Release. Package: package.ps1. Installer: Installer\build-installer.ps1. Target net8.0-windows, no NuGet packages. Version in Directory.Build.props.


2. Wire protocol

2.1 Plain mode (all security off)

Standard RFB 3.8: RFB 003.008\n exchange, security type None (1), ClientInit/ServerInit, then normal messages. Encodings offered by the client: JVNC JPEG (private), CopyRect (1), Zlib (6) and Raw (0). Any third-party viewer works (it gets CopyRect too if it advertises it).

2.2 Security negotiation (1.0.5+)

The server always performs the standard RFB 003.008 version exchange, then offers security types: 250 (JVNC, private) always; 1 (None) when no layer is on; otherwise 19 (VeNCrypt) when TLS is on or 2 (VNC Authentication) when it is off — the latter two only if Allow standard VNC viewers is on and neither client certificates nor TOTP are required. After the chosen sub-handshake the server sends the normal u32 SecurityResult, then ClientInit/ServerInit proceed.

Failures at any step count towards the per-address lockout (5 → 15 min ban). Before any of this the server applies the IP allow list and the public-address rule.

2.3 JVNC extension messages

Message types 250–253, used only between JVNC client and server, both directions unless noted.

Type Name Body
250 FileHeader pad[3], u32 nameLen, name (UTF-8), u64 size
251 FileData pad[3], u32 len, data (≤ 256 KB per message)
252 FileRequest (client → server) pad[3] — ask the server user to pick a file to send
253 FileStatus (server → client) pad[3], u32 len, UTF-8 text
240 Capabilities (both) pad[3], u8 count, u8 types[] — extension types the sender can receive; client first, server replies
241 MonitorsRequest (client → server) pad[3]
245 Monitors (server → client) pad[3], u8 count, per monitor: i32 x, i32 y, u16 w, u16 h, u8 primary, u8 nameLen, name (framebuffer coordinates)
242 AudioEnable (client → server) u8 enable, u8 quality (0 stereo 48 kHz, 1 mono 24 kHz), pad[1]
246 AudioFormat (server → client) pad[3], u32 sampleRate, u8 channels, u8 codec (1 = IMA ADPCM), pad[2]
243 Privacy (both) u8 on/state, pad[2] — client requests, server echoes the actual state
244 Chat (both) pad[3], u32 len, UTF-8 text (≤ 64 KB)
249 HostInfo (server → client) pad[3], u8 macCount, 6-byte MACs, u32 nameLen, name — for Wake-on-LAN
248 Clipboard (both) pad[3], u32 len, UTF-8 text (≤ 1 MB). Server also accepts RFB ClientCutText from stock viewers.
200 PointerEx (client → server) u8 kind (1 touch, 2 pen), u8 flags (1 down, 2 up, 4 barrel, 8 eraser, 16 in-range/hover), pad[1], u16 x, u16 y, u16 pressure (0..1024), u32 id. Injected with InjectTouchInput / InjectSyntheticPointerInput; mouse fallback.
201 PointerRel (client → server) u8 buttons (RFB mask, wheel bits 3/4 pulse), i16 dx, i16 dy — relative mouse mode (MOUSEEVENTF_MOVE).
202 Focus (server → client) pad[3], i32 x, i32 y, i32 w, i32 h — foreground window rect in framebuffer coordinates; sent on change and once after capabilities.
203 Ping (both) pad[3], u32 seq, u64 stamp — the server echoes the message verbatim; the client computes RTT.
204 LinkCap (client → server) pad[3], u32 bytesPerMs (0 = measured link) — per-session bandwidth limit used by the frame budget.
254 TransferCtl (both) u8 sub, pad[2], u32 len, body. Sub 1 Meta (u8 kind 0 file/1 clipboard file/2 clipboard image (zlib CF_DIB)/3 folder entry, u8 last, pad[2], u64 offset, u32 batch, u32 relLen, relPath) describes the NEXT FileHeader; 2 Probe (u64 size, u32 nameLen, name) asks for a partial; 3 Have (u64 offset, name) answers; 4 Cancel (receiver → sender, empty); 5 Abort (sender → receiver, empty; partial kept as <name>.<size>.jvncpart); 6 Saved (receiver → sender: u32 len, UTF-8 local path of a kind-4 HotFolder capture). Kind 4 HotFolder = capture: saved with a stable name (overwritten) in the receiver's hot-folder save folder, path put on its clipboard. Peers without 254 get plain FileHeader/FileData only.
247 AudioData (server → client) pad[3], u32 len, u32 sampleFrames, ADPCM nibbles (channels interleaved per frame, low nibble first; encoder/decoder state continues across messages)

A file is a header followed by data messages until size bytes have arrived. Chunks are separate messages so input and screen updates interleave with transfers.

2.4 Screen updates

2.5 Input


3. Concurrency model (server)

Per client: a reader task parses messages and injects input immediately; an update task waits for a pending FramebufferUpdateRequest and does capture/diff/encode/send; all socket writes go through Session.WriteLock. File sends run as background tasks taking the write lock per chunk. Dialogs on either side are shown asynchronously (Dispatcher.InvokeAsync); incoming file data streams to a temp file until the user decides.


4. Testing

A headless stress client (see the session notes / Installer folder history) connects, sweeps the pointer, optionally opens a self-dragging "churn" window and a fragmenting TCP proxy, and reports fps and any receive-loop failure. Security layers are exercised by editing server.json (TLS, client cert, TOTP secret, allow list) and running the harness with JVNC_TOTP / JVNC_TRUST environment variables. Run the churn test for 60 s after any change to encoding or the receive loop.


5. Extending