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>
52 lines
1.7 KiB
JavaScript
52 lines
1.7 KiB
JavaScript
'use strict';
|
|
|
|
const path = require('path');
|
|
|
|
function bool(v, dflt) {
|
|
if (v === undefined || v === '') return dflt;
|
|
return /^(1|true|yes|on)$/i.test(String(v));
|
|
}
|
|
|
|
function list(v) {
|
|
return String(v || '')
|
|
.split(',')
|
|
.map((s) => s.trim().toLowerCase())
|
|
.filter(Boolean);
|
|
}
|
|
|
|
const config = {
|
|
port: Number(process.env.PORT || 8080),
|
|
host: process.env.HOST || '0.0.0.0',
|
|
|
|
// garagedoor-node-ws central auth
|
|
authUrl: (process.env.AUTH_URL || 'http://192.168.4.208:8000').replace(/\/$/, ''),
|
|
|
|
// Admins: username allowlist, or garagedoor `level` at/above this threshold.
|
|
adminUsers: list(process.env.ADMIN_USERS),
|
|
adminLevel: process.env.ADMIN_LEVEL === '' || process.env.ADMIN_LEVEL === undefined
|
|
? null
|
|
: Number(process.env.ADMIN_LEVEL),
|
|
|
|
dbPath: process.env.DB_PATH || path.join(__dirname, '..', 'data', 'rcs.db'),
|
|
|
|
// Key material for AES-256-GCM at-rest encryption of VNC passwords / agent keys.
|
|
encryptionKey: process.env.ENCRYPTION_KEY || '',
|
|
|
|
// Public base URL, used when rendering invite links. Falls back to the request host.
|
|
publicUrl: (process.env.PUBLIC_URL || '').replace(/\/$/, ''),
|
|
|
|
ticketTtlMs: Number(process.env.TICKET_TTL_MS || 30_000),
|
|
inviteDefaultTtlMs: Number(process.env.INVITE_TTL_MS || 24 * 60 * 60 * 1000),
|
|
agentOfflineAfterMs: Number(process.env.AGENT_OFFLINE_AFTER_MS || 90_000),
|
|
consentTimeoutMs: Number(process.env.CONSENT_TIMEOUT_MS || 45_000),
|
|
|
|
// Session invites let unauthenticated people connect. Off by default is safer,
|
|
// but the whole point of this app is handing a link to someone, so: on.
|
|
allowSessionInvites: bool(process.env.ALLOW_SESSION_INVITES, true),
|
|
|
|
trustProxy: bool(process.env.TRUST_PROXY, true),
|
|
logLevel: process.env.LOG_LEVEL || 'info',
|
|
};
|
|
|
|
module.exports = config;
|