From cfda6b19b73d74c7557ce1d11470087faa9dafaf Mon Sep 17 00:00:00 2001
From: Ricardo Mancinas
Date: Wed, 12 Aug 2026 11:37:12 -0700
Subject: [PATCH] feat(install): one-command device registration for macOS,
Windows and Linux
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Registering a machine meant reading a multi-step page, installing Node, and
running three commands in the right order. Now it is one line per platform.
The hub serves scripts/install.sh and scripts/install.ps1 with its own address
and the enrolment token substituted in, so the published command carries
everything and there is nothing to fill in:
curl -fsSL https://support.freakma.com/install.sh?token=TOKEN | sh
irm https://support.freakma.com/install.ps1?token=TOKEN | iex
That output is piped straight into a shell, so the token — the only untrusted
value in either file — is refused unless it matches the base64url shape that
randomToken produces.
Each script checks for a usable runtime and stops with instructions rather than
guessing, warns when nothing is serving RFB on the loopback, installs per-user
with no root or administrator, and registers a login-scoped service: launchd on
macOS, a lingering systemd user service on Linux, a logon task on Windows. The
Windows script uses Node 22 when it is present and falls back to the bundled
executable otherwise, which is what lets one command cover both Windows 11 and
Server 2008 R2.
It registers a logon task rather than a service on purpose: services run in
session 0 and cannot draw on the interactive desktop, so the "ask first" consent
prompt would never appear.
Also adds /docs — a per-OS setup guide with service management and a
troubleshooting table — and reworks the enrolment page into OS tabs that open on
whichever platform the reader is sitting at.
Co-Authored-By: Claude Opus 5
---
Dockerfile | 1 +
README.md | 59 ++++++-
public/docs.html | 339 +++++++++++++++++++++++++++++++++++++++
public/enroll.html | 120 ++++++++++----
public/index.html | 1 +
public/styles.css | 30 ++++
scripts/install.ps1 | 106 ++++++++++++
scripts/install.sh | 167 +++++++++++++++++++
server/config.js | 13 ++
server/index.js | 36 +++++
server/routes/invites.js | 6 +-
11 files changed, 838 insertions(+), 40 deletions(-)
create mode 100644 public/docs.html
create mode 100644 scripts/install.ps1
create mode 100644 scripts/install.sh
diff --git a/Dockerfile b/Dockerfile
index 96caf40..3f3efc2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -28,6 +28,7 @@ RUN pnpm install --prod --frozen-lockfile
COPY server ./server
COPY public ./public
COPY agent ./agent
+COPY scripts ./scripts
COPY --from=agent-exe /build/dist/rcs-agent.exe ./dist/rcs-agent.exe
VOLUME ["/data"]
diff --git a/README.md b/README.md
index 015510b..bc16851 100644
--- a/README.md
+++ b/README.md
@@ -90,15 +90,61 @@ LAN as the hub.
### Agent (the machine dials out)
-**Invite a machine** produces a link. Open it on the target machine and it shows a
-one-liner:
+**Invite a machine** produces a link. Open it on the target machine and it shows the
+command with the token already in it. The full walkthrough — per OS, with service
+management and troubleshooting — is served at
+[`/docs`](https://support.freakma.com/docs).
+
+## Registering a device
+
+One command per platform. The hub substitutes its own address and the enrolment token
+into the script before serving it, so there is nothing to fill in but `TOKEN`.
+
+**macOS and Linux**
```bash
-curl -fsSL https://your-hub/download/agent.js -o rcs-agent.js \
- && node rcs-agent.js enroll https://your-hub/enroll/TOKEN \
- && node rcs-agent.js run
+curl -fsSL https://support.freakma.com/install.sh?token=TOKEN | sh
```
+**Windows**
+
+```powershell
+irm https://support.freakma.com/install.ps1?token=TOKEN | iex
+```
+
+**Windows 7 / Server 2008 R2** — PowerShell 2.0 has no `irm`, and .NET 3.5 does not
+negotiate TLS 1.2 without [KB3154518](https://support.microsoft.com/kb/3154518), so
+the short form fails twice over. From `cmd.exe`:
+
+```bat
+powershell -c "[Net.ServicePointManager]::SecurityProtocol=3072; (New-Object Net.WebClient).DownloadString('https://support.freakma.com/install.ps1?token=TOKEN') | iex"
+```
+
+What the scripts do, in order: check for a usable runtime and stop with instructions
+rather than guessing; warn if nothing is listening on `127.0.0.1:5900`; download the
+agent into a per-user directory; enrol, exchanging the one-time token for an agent key
+written `0600`; and register a login-scoped service.
+
+| Platform | Installs to | Kept alive by |
+|---|---|---|
+| macOS | `~/.rcs-agent` | launchd agent, `com.freakma.rcs-agent` |
+| Linux | `~/.rcs-agent` | systemd **user** service `rcs-agent`, with lingering enabled |
+| Windows | `%LOCALAPPDATA%\RemoteControlSupport` | logon task `RemoteControlSupportAgent` |
+
+Nothing needs root or administrator. On Windows the installer uses Node 22 if it is
+already there and falls back to the self-contained `rcs-agent.exe` otherwise, so the
+same command works on a fresh Windows 11 box and on Server 2008 R2.
+
+The Windows installer deliberately registers a **logon task, not a service**: services
+run in session 0 and cannot draw on the interactive desktop, so the *ask first* consent
+prompt would never appear and every session would time out waiting for an answer.
+
+Both scripts are plain text and worth reading before piping them into a shell —
+[`/install.sh`](https://support.freakma.com/install.sh) and
+[`/install.ps1`](https://support.freakma.com/install.ps1) are exactly what runs.
+
+### Doing it by hand
+
The agent needs Node 22+ and a VNC server listening on `127.0.0.1:5900`:
| OS | VNC server |
@@ -233,6 +279,9 @@ Everything is environment variables — see [.env.example](.env.example).
| `POST` | `/api/sessions/:id/kill` | admin |
| `GET` | `/api/sessions/history` | |
| `GET` | `/api/sessions/audit` | admin |
+| `GET` | `/docs` | setup guide, no auth |
+| `GET` | `/download/agent.js` · `/download/agent.exe` | no auth |
+| `GET` | `/install.sh` · `/install.ps1` | no auth, `?token=` is stamped in |
| `POST` | `/api/public/enroll` | no auth, enrolment token |
| `POST` | `/api/public/session/:token` | no auth, support link |
| WS | `/ws/vnc?ticket=` | browser session |
diff --git a/public/docs.html b/public/docs.html
new file mode 100644
index 0000000..6840907
--- /dev/null
+++ b/public/docs.html
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+ Registering a device — Remote Support
+
+
+
+
+
+
+
+
+ How to put a machine under remote support, on macOS, Windows and Linux — what to install,
+ what the one-command installer actually does, and how to keep it running.
+
+
+
Before you start
+
+ Every registered machine needs two things: a VNC server serving its screen on
+ 127.0.0.1:5900, and the agent, which dials out to this
+ hub and forwards that port over an authenticated WebSocket. The agent is what makes NAT and
+ firewalls stop mattering — nothing inbound is ever opened on the machine.
+
+
+ You also need an enrollment link. In the console, open
+ Machines → Invite a machine. The link carries a one-time token and expires;
+ open it on the machine being registered and it shows the exact command with the token already
+ filled in.
+
+
+
+ The alternative is direct mode, where this hub connects straight to the
+ machine's VNC port. That only works when the hub can route to it. If the machine is on another
+ subnet, behind NAT, or reachable only through a VPN or SSH tunnel, use the agent — a direct
+ entry will just time out.
+
+
+
Pick a platform
+
+
+
+
+
1. Turn on Screen Sharing
+
+ System Settings → General → Sharing → Screen Sharing. Under
+ Computer Settings, enable VNC viewers may control screen with password and
+ set a password — the hub authenticates with it and never passes it to the browser.
+
+
+ macOS serves VNC on port 5900 on all interfaces. The agent only ever connects to it over
+ loopback, so you can firewall the port off from the network.
+
+
+
2. Install Node.js 22 or newer
+
+ brew install node, or the installer from
+ nodejs.org. Check with
+ node -v.
+
+
+
3. Run the installer
+
Paste this in Terminal, replacing TOKEN with the one from your enrollment link:
+
+
+
+
+
+ It installs to ~/.rcs-agent, enrolls the machine, and loads a
+ launchd agent so it starts at login and restarts if it dies.
+
+ Windows has none built in. TightVNC
+ or UltraVNC both work. During setup, register it as a system service so it survives reboots,
+ and set a password.
+
+
+
2. Run the installer
+
In PowerShell, with TOKEN from your enrollment link:
+
+
+
+
+
+ No prerequisites. If Node.js 22+ happens to be installed the script uses it; otherwise it
+ downloads rcs-agent.exe, a self-contained build with its own
+ runtime inside. Everything lands in
+ %LOCALAPPDATA%\RemoteControlSupport and no administrator rights
+ are needed.
+
+
+
Windows 7 and Server 2008 R2
+
+ Those ship PowerShell 2.0, which has no irm, and .NET 3.5, which
+ does not negotiate TLS 1.2 by default — the short command fails on both counts. Use this
+ instead, from cmd.exe:
+
+
+
+
+
+
+ If it still cannot connect, the machine is missing
+ KB3154518,
+ which adds TLS 1.2 to .NET 3.5. As a last resort,
+ download the agent on another
+ machine, copy it across, and run
+ rcs-agent.exe enroll <link> then
+ rcs-agent.exe run.
+
+
+
Managing it
+
+
Do this
Command
+
Check it exists
schtasks /query /tn RemoteControlSupportAgent
+
Start
schtasks /run /tn RemoteControlSupportAgent
+
Stop
schtasks /end /tn RemoteControlSupportAgent
+
Remove
schtasks /delete /tn RemoteControlSupportAgent /f
+
+
+
+ Do not install the agent as a Windows service on a machine where
+ ask first is enabled. Services run in session 0 and cannot draw on the interactive
+ desktop, so the consent prompt would never appear and every session would time out waiting
+ for an answer. The installer registers a logon task precisely to avoid this — it
+ runs in the console session, where the prompt is visible.
+
+
+
+
+
+
1. Start a VNC server on the loopback
+
For an existing X session — what you want for support, since it shows the real screen:
+
+
+
+
+
+ On Wayland, x11vnc cannot see the screen. Either log in on an Xorg
+ session, or use a Wayland-native server such as wayvnc.
+
+ It installs to ~/.rcs-agent and registers a
+ systemd user service. It also enables lingering, so the agent keeps running
+ when nobody is logged in — without that, the machine drops off the console at logout, which
+ is exactly when you tend to need it.
+
+ Printers, appliances, locked-down boxes, anything too old for a current runtime — the agent
+ does not have to run on the machine it serves. Run it on any other machine on the same
+ LAN and point it across:
+
+
+
+
+
+
+ That host now needs to reach the target's VNC port, and it becomes the machine whose uptime
+ determines whether the target is reachable.
+
+
+
Checking it worked
+
+
The machine appears in Machines in the console, marked online.
+
Its hostname and OS show up — the agent reports both when it registers.
+
Clicking Connect opens the screen.
+
+
+
When it does not
+
+
Symptom
Cause
+
+
timed out connecting to …:5900
+
This hub cannot route to that address. A direct-mode entry on a subnet the hub cannot
+ reach — register it with the agent instead.
+
+
+
cannot reach …:5900 (ECONNREFUSED)
+
Routing is fine, nothing is listening. The VNC server is not running.
+
+
+
VNC authentication failed
+
Wrong stored password. Note that VNC authentication only uses the first 8 characters.
+
+
+
Enrollment says the link expired
+
Enrollment tokens are one-time and time-limited. Mint a new one.
+
+
+
Machine shows offline right after install
+
The agent is not running. Check the service commands for your platform above.
+
+
+
Sessions hang on "waiting for consent"
+
Ask first is on and the agent cannot show a dialog — it is running headless, as
+ a Windows service, or on Linux without zenity/kdialog.
+
+
+
+
What the installer does
+
+ Both scripts are served by this hub with its address and your token already substituted in, so
+ there is nothing to fill in. They are worth reading before you pipe them into a shell —
+ install.sh and
+ install.ps1 are the exact text that runs. In order they:
+
+
+
check for a usable runtime, and stop with instructions rather than guessing;
+
warn if nothing is listening on 127.0.0.1:5900;
+
download the agent into a per-user directory — no root, no administrator;
+
enroll, which exchanges the one-time token for an agent key stored with 0600 permissions;
+
register a login-scoped service so it comes back after a reboot.
+
+
+
+ Access control, support links and session policy are covered in the project README.
+
+
+
+
+
+
+
+
diff --git a/public/enroll.html b/public/enroll.html
index 614eaf9..956d3c4 100644
--- a/public/enroll.html
+++ b/public/enroll.html
@@ -36,47 +36,81 @@
until you stop the agent.
-
+
+
+
- Make sure a VNC server is running on this machine, listening on
- 127.0.0.1:5900.
+ Turn on Screen Sharing — System Settings → General → Sharing.
- macOS: System Settings → General → Sharing → Screen Sharing.
- Windows: install TightVNC or UltraVNC.
- Linux: x11vnc -localhost -rfbport 5900.
+ Under Computer Settings, allow VNC viewers and set a password.
- Run this in a terminal:
-
+ Paste this into Terminal:
+
-
-
+
-
- On Windows 7 or Server 2008 R2?
+
+
+ Install TightVNC or UltraVNC, set a password, and let it register as a
+ service so it survives a reboot.
+
+
+ Paste this into PowerShell:
+
+
+
+
+
+ Nothing else to install — if Node.js is missing the script fetches a self-contained
+ agent instead.
+
+
+
+
+
+
+ Start a VNC server on the loopback:
+
+
+
+ Install Node.js 22+ if it is not already there.
+
+
+ Paste this into a terminal:
+
+
+
+
+
+
+
+
+ Windows 7 or Server 2008 R2?
- Node.js will not install on those — the last version that supported them is long
- out of date. Use the standalone build instead: one file, nothing to install.
+ Those have PowerShell 2.0 and no TLS 1.2, so the command above fails. Run this from
+ cmd.exe instead: