SynVirt Web Console v2 (VNC)
Synced read-only from
/home/synnet/mirrors/synvirt-product/docs/WEB_CONSOLE_V2.md. Edit at the source, not here.
SynVirt Web Console v2 (VNC)
Section titled “SynVirt Web Console v2 (VNC)”Document version: 0.9.0-alpha.2 · Module: daemon console router +
crates/console-gateway (VNC bridge) + crates/console-ticket.
Supersedes the multi-protocol design captured in earlier revisions of this document (RDP / VNC / SPICE / Serial recommendation engine). SPICE was removed in 0.9.0-alpha.2; this document now describes the single VNC path.
1. What changed in 0.9.0-alpha.2
Section titled “1. What changed in 0.9.0-alpha.2”The 0.8.x console module shipped SPICE everywhere: per-VM Unix
sockets under /run/synvirt/spice/, a vendored spice-html5 client
in the dashboard, spicevmc channels in every domain XML, QXL as the
default video device, and a console-gateway whose only job was to
bridge a WebSocket to a SPICE Unix socket.
All of that is gone. The web console is now VNC-only:
console-gatewayis a WebSocket↔TCP-socket bridge for VNC.- The dashboard ships noVNC (BSD-2-Clause) — no
spice-html5. - Domain XML carries
<graphics type='vnc' listen='127.0.0.1' port='-1'>and<video><model type='virtio'/>(orstd-vgafor legacy guests).<channel type='spicevmc'>and<redirdev>blocks are removed. The QXL VideoModel variant is gone. - Guest-tools ISO no longer carries
spice-guest-tools/spice-vdagent. It ships virtio-win drivers and SynVirt agents only.
There is no recommendation engine. There is no protocol negotiation.
The browser hits /api/console/{uuid} to mint a ticket, then opens
a WebSocket to /console/vnc/{uuid}?ticket=…. One path.
WebRTC remains a post-1.0 evaluation candidate — as a possible successor to VNC, not as an additional coexistence path.
2. Architecture diagram
Section titled “2. Architecture diagram”┌──────────────────────┐ ┌────────────────────────┐ ┌──────────────────┐│ Browser │ │ synvirt-daemon │ │ qemu-kvm (per-VM)││ noVNC (vendored) │ │ console-gateway │ │ ││ │ │ ├─ ticket store │ │ VNC listener ││ WSS /console/vnc/ │ │ ├─ axum router │ │ on 127.0.0.1: ││ {uuid} │◄──►│ │ POST /api/ │◄──►│ 5900+N ││ ?ticket=… │ │ │ console/ │ │ ││ │ │ │ GET /console/ │ │ (port chosen by││ │ │ │ vnc/ │ │ libvirt at ││ │ │ └─ bridge pump │ │ domain start) │└──────────────────────┘ └────────────────────────┘ └──────────────────┘The VNC listener address is resolved per VM via libvirt’s
vncdisplay query. It is host-local, never network-exposed.
3. REST surface
Section titled “3. REST surface”POST /api/console/{vm_uuid} → 200 { ticket, expires_in: 30 }GET /console/vnc/{vm_uuid} → 101 Switching Protocols (WebSocket) query: ticket=<JWT>Tickets are HS256, single-use, 30-second TTL, with a 24-hour HMAC
key rotation and 30-minute grace window. The ticket store is
extracted into crates/console-ticket/ so any future protocol
gateway can reuse it; the only aud claim today is console.
The legacy /api/v1/vms/:name/console/recommended-backend endpoint
that earlier drafts of this document specified is not shipped.
There is one backend; recommendation is a no-op.
3.1 Legacy ticket + VNC paths
Section titled “3.1 Legacy ticket + VNC paths”Two legacy endpoints predate the noVNC bridge above:
POST /api/v1/auth/ticket— issues a short-lived dashboard ticket against PAM Basic Auth.GET /api/v1/vms/:name/console/vnc— name-keyed WebSocket bridge to the VM’s VNC display.
Both are name-keyed and apply a strict valid_name() check on the
:name segment: it must start with an ASCII lowercase letter and
contain only ASCII lowercase letters, digits, and hyphens, length
1-63. Any other character returns a 400 before reaching libvirt —
the restriction matches synvirt_libvirt::vm::validate_name so the
name is safe to splice into a subprocess argv without quoting.
The new /console/* paths above (POST /api/console/{vm_uuid} and
GET /console/vnc/{vm_uuid}) are UUID-keyed and have no
valid_name() restriction — UUIDs are validated by their format,
not their character set.
4. Frontend
Section titled “4. Frontend”crates/web-ux-v2/src/components/console/ carries:
- A noVNC wrapper that opens the WebSocket and renders to a canvas.
- A pop-out window for multi-monitor setups.
- Connection-status badges (connecting / connected / disconnected).
The legacy crates/web-ux/public/spice/ tree, the
ConsoleModal.js SPICE client, and the spice-html5 vendored
sources have all been removed.
5. Crates
Section titled “5. Crates”| Crate | Role |
|---|---|
console-gateway |
WebSocket ↔ TCP pump, axum routes, libvirt vncdisplay resolution. |
console-ticket |
Shared ticket store (HS256 + dashmap nonce + key rotation). |
rdp-gateway, vnc-gateway (separate crate), serial-gateway, and
the recommendation router that earlier drafts proposed are not in
scope. The single VNC path lives in console-gateway.
6. Out of scope
Section titled “6. Out of scope”- Multi-protocol coexistence (RDP, SPICE, WebRTC, serial) on the same gateway. WebRTC is a successor candidate, not an addition.
- Per-VM
preferred_backendpolicy. There’s only one backend. - Recording console sessions. Audit logs name the operator + VM + start/stop times only.
- Cross-host migration of an open session. The session drops and re-establishes against the new host.
7. Migration from 0.8.x
Section titled “7. Migration from 0.8.x”Existing VMs whose domain XML still carries <graphics type='spice'>
continue to “work” in the libvirt sense — libvirt accepts the XML,
QEMU starts, the VM runs. But the dashboard no longer offers a SPICE
console: the only console button mints a VNC ticket and opens
/console/vnc/{uuid}. If the domain doesn’t also carry a VNC
listener, the bridge fails fast with vncdisplay returned no display.
The migrator’s video_migration pass rewrites legacy SPICE +
QXL domains in place: it strips the <graphics type='spice'> /
<channel type='spicevmc'> / <redirdev> blocks, swaps QXL to
virtio-vga (or std-vga for Win9x / NT / 2000), and adds a
<graphics type='vnc' listen='127.0.0.1' port='-1'> listener.
Operators can also re-define a VM by hand to refresh its XML — see
docs/UPGRADE_NOTES.md § 0.9.0-alpha.2.
End of WEB_CONSOLE_V2.md.