Files
remote-control-support-webapp/public/viewer.html
T
rmancinasandClaude Opus 5 999717f77b feat: self-hosted remote support over VNC
Browser-based remote control (noVNC) with invite links, per-user access
control, garagedoor SSO and a persisted client list.

The hub proxies RFB rather than pointing the browser at a VNC server. That
is what lets it authenticate upstream with a stored password the browser
never sees, and enforce view-only by dropping input messages on the
client->server stream instead of hiding buttons.

Machines are reachable two ways: direct TCP for LAN hosts, or an outbound
agent tunnel for anything behind NAT. Node 22's global WebSocket keeps the
agent dependency-free, and node:sqlite keeps the image free of native
builds.

Ships with an end-to-end suite that boots the real server against a fake
VNC server and a fake auth service (72 assertions), plus Gitea Actions
CI/CD to Portainer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 23:37:35 -07:00

41 lines
1.6 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<title>Remote session</title>
<link rel="stylesheet" href="/styles.css">
</head>
<!-- No particle field here: every frame belongs to the remote desktop. -->
<body class="viewer-body">
<div class="viewer-bar">
<span class="dot" id="conn-dot"></span>
<strong id="client-name">Connecting…</strong>
<span class="tag" id="role-tag" hidden></span>
<span class="viewer-sep"></span>
<span class="faint" id="conn-status"></span>
<div class="spacer"></div>
<button class="small" id="btn-cad" disabled title="Send Ctrl-Alt-Delete">Ctrl-Alt-Del</button>
<button class="small" id="btn-clipboard" disabled title="Send clipboard text to the remote machine">Paste</button>
<button class="small" id="btn-fit" disabled title="Toggle between fit-to-window and actual size">Fit</button>
<button class="small" id="btn-fullscreen" disabled>Fullscreen</button>
<span class="viewer-sep"></span>
<button class="small danger" id="btn-disconnect" disabled>Disconnect</button>
</div>
<div id="screen" style="position:relative">
<div class="viewer-status" id="status-overlay">
<div class="box">
<h2 id="status-title">Connecting…</h2>
<p class="faint" id="status-text" style="margin-bottom:0">Setting up the session.</p>
<div id="status-actions" class="row" style="justify-content:center;margin-top:18px"></div>
</div>
</div>
</div>
<script type="module" src="/viewer.js"></script>
</body>
</html>