Remote Access and Security
Overview
Section titled “Overview”CONNECT has exactly one access rule, evaluated per request:
A caller is allowed in if it is on the local machine, if it presents a valid API key, or if it presents a valid dev key while remote development is switched on.
Everything else is answered with 401. There is one authentication instance and no per-endpoint exception list — individual endpoints add authorization checks on top (who may do what), never a different way to prove who you are.
The third case is the remote development mode: a second, separate key that is off by default and that developers of realvirtual WEB use to work on one running session from two machines. If you are not doing that, it does not exist for you — leave it empty and nothing changes.
For a gateway used only on the PC it runs on, this means: nothing to configure, and nothing reachable from outside without a key.
Setting an API key
Section titled “Setting an API key”Set ApiKey in appsettings.json next to the executable and restart the gateway:
{ "ApiKey": "a-long-random-value" }An empty key disables the key mechanism, which is fine as long as the gateway is only used locally.
A key can be presented in three ways:
| Way | Used by |
|---|---|
X-API-Key header | Scripts, Unity, MCP clients |
Authorization: Bearer <key> | HTTP clients that prefer it |
| Session cookie | Browsers, after the one-time link below |
Opening the HMI from another machine
Section titled “Opening the HMI from another machine”A browser cannot attach a header to a page it is asked to load, so a remote HMI would not be able to fetch even its own model. The one-time link solves this:
http://<host>:5100/?apikey=<key>Opening that URL once exchanges the key for a session cookie — httpOnly, SameSite=Strict, valid for one working day — and redirects to the clean address. Every later request from that browser, including every model download, carries the cookie.
The exchange happens on a page navigation only. Scripts, Unity and AI clients keep using the header and are unaffected.
Reachable without anybody choosing it
Section titled “Reachable without anybody choosing it”Windows binds all network adapters by default, so a gateway can be reachable from other machines even though nobody set that up. Exactly that combination — reachable beyond the local machine and no API key — raises a notice in the tray menu with what to do: set ApiKey, then open the gateway once with the one-time link.
A purely local installation stays silent.
TLS and reverse proxies
Section titled “TLS and reverse proxies”CONNECT does not terminate TLS itself. The supported remote setup is a reverse proxy that terminates TLS and speaks plain HTTP to the gateway on the local machine.
When you do that, configure TrustedProxies with the proxy’s address. Without it, the proxy is the transport peer — so every request arriving through it would look local and the key would never be demanded. That is deliberate: a request that still carries unresolved forwarding headers is never treated as local.
The gateway warns at startup when it is reachable beyond the local machine without TLS, without trusted proxies or without an API key.
What is deliberately different
Section titled “What is deliberately different”Two exceptions exist, and both are intentional.
/health answers without any proof. Monitoring has to work. It carries liveness, version identity and yes/no capability flags — nothing else. The operational detail (signal and client counts, license state) lives at /health/detail, behind the normal rule.
Some development routes refuse remote callers even with a valid API key. One of them runs a test command, and the gateway API key must never become remote code execution. The only way past that ceiling is the separate dev key below — a different secret, behind a switch that ships off.
Remote development from another machine
Section titled “Remote development from another machine”This section is for developers of realvirtual WEB. If you run CONNECT as a gateway, skip it — the feature is off and stays off unless you deliberately turn both halves on.
When CONNECT runs the realvirtual WEB development server, that server is served to the local machine only, and the normal API key does not change it: a development workspace holds sources and customer project data, and it is not a delivery. To work on one running session from a second machine — desktop and laptop, office and on the road, with live reload and the plant connection intact — two things have to be set:
| Half | Where |
|---|---|
A dev key, Dev.ApiKey | appsettings.json next to the executable — empty by default |
| The switch realvirtual WEB → Allow remote access (dev) | Tray menu — off by default, applies immediately |
Either one missing means the remote machine is refused. The normal ApiKey never opens this.
Setting it up
Section titled “Setting it up”Publish the gateway port to your tailnet — this is the tested path, and using the same port matters, because it keeps the page, the live-reload socket, the HMI bridge and the AI endpoint on one address:
tailscale serve --bg --https=5100 http://127.0.0.1:5100# close it againtailscale serve --https=5100 offThen open this once on the other machine:
https://<magicdns-name>:5100/?devkey=<your dev key>The key is exchanged for a session cookie and the address is cleaned up, exactly like the API-key link above. Everything afterwards — sources, live reload, the HMI — travels on that cookie. Scripts use the X-Dev-Key header instead.
Two things that catch people out
Section titled “Two things that catch people out”- The AI endpoint needs its own switch. MCP server → Allow remote clients is a separate setting that the dev key does not touch. Remote development without remote AI clients is a perfectly normal state.
- Switching the toggle off applies to new requests. A live-reload connection that is already open keeps running until it goes idle. Close the browser tab to end it immediately.
On Linux the startup gate still requires the normal ApiKey whenever the gateway is reachable beyond the local machine; the dev key does not substitute for it. An unlicensed installation gets no remote development either.
Browser-origin checks
Section titled “Browser-origin checks”Three surfaces additionally check where the browser came from, because the access rule alone cannot detect a DNS rebinding attack — the attacker’s page carries a consistent name that resolves to the local address:
| Surface | Restriction |
|---|---|
| MCP endpoint | Trusted origins only, and local clients only unless remote MCP clients are explicitly allowed |
| Bundled HMI bridge | Trusted origins or the gateway’s own origin |
| Update confirmation | Trusted origins or the gateway’s own origin |
A bare IP address is accepted as a host, which is what keeps a remote HMI on http://192.168.x.y:5100 working: rebinding needs a name whose DNS answer can be changed after the page was served.
AI clients
Section titled “AI clients”The gateway exposes an MCP endpoint for AI tools. It is on by default, and writes are allowed by default — the access rule above is what makes that safe. Both switches are in the tray menu:
- MCP server → Enabled — a startup switch; the tray offers the restart that applies a change.
- MCP server → Allow write access — read live; connected clients are notified when it changes.
Remote MCP clients are refused unless explicitly allowed. Details: AI clients.
See also
Section titled “See also”- Installation — where
appsettings.jsonlives - Updates — how the update confirmation is protected
- REST and WebSocket API — using the key from a script