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>
41 lines
1.6 KiB
HTML
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>
|