Overlay and relays
How remote control reaches a workbench through the outbound relay, and how to add the WireGuard overlay.
AIOE reaches workbenches in two ways. The relay is always on: every enrolled workbench keeps an outbound HTTPS connection to the API, and remote control travels down it. The overlay is optional: a private WireGuard network between your workbenches, nodes and the platform, which carries remote control end to end encrypted between machines and makes screen mirroring possible. Neither needs an inbound port on any workbench.
The relay
A workbench holds one Server-Sent Events stream open to /relay/v1/workbench/stream. When someone controls it from the console or the mobile app, the API writes the request to that stream, and the workbench answers on /relay/v1/workbench/respond. The workbench checks every request against the scopes it was granted and against its policy, and records it in its own audit trail under the real person.
There is nothing to deploy for the relay: it is part of the API. What it needs:
- Proxies and load balancers in front of the API must not buffer responses and must allow long reads. See Install with Helm.
- With more than one API replica, Redis, so a request reaching one replica finds a workbench connected to another. See Redis for more than one replica.
- A request waits
AIOE_RPC_TIMEOUTseconds (default 30) for the workbench to answer.
Who may control a workbench: the person who enrolled it, operators and administrators. What they may do is what was granted when it was approved (by default everything except viewing and controlling the screen), narrowed again by the organisation's policy ("Remote Control" in Policy).
The overlay
Each organisation gets its own private address range, a /16 inside 100.64.0.0/10, allocated the first time a node joins. A node makes its own WireGuard key pair, registers only the public key, and receives an address, the peers it may reach, and how to reach them. The private key never leaves the machine, and a node ignores any address outside your range.
| Node | What it is |
|---|---|
| Workbench | AI Workbench on a person's computer. The desktop app runs the overlay agent once enrolled, when the person turns on the mesh in Settings, Organisation. |
| Node | A headless workbench on shared compute. See Nodes. |
| Gateway | The platform's own node on your overlay. The API sends remote control through it when the workbench is on the overlay. |
Who reaches whom is decided by the mesh policy on the console's Mesh page: owners reach their own nodes, operators and administrators reach every node, and tag rules connect groups (for example, nodes tagged dev may reach nodes tagged build). Disabled nodes drop out of every map.
How traffic flows: nodes try each other's addresses directly first, found through STUN and on the local network, which works across most NATs. Where there is no direct path (symmetric NATs, networks that block UDP, hosts that accept only HTTPS) WireGuard packets go through the mesh relay over HTTPS. The mesh relay only ever sees encrypted packets, a node proves it owns its key before it may use it, and packets never cross organisations.
Remote control over the overlay: when a workbench is on the overlay, the API sends remote control through the gateway, signed with the platform's key so the workbench can tell the platform from any other node, and falls back to the relay if that fails. The console marks each call "via mesh" or "via relay". Viewing a workbench's screen works only over the overlay.
Add the overlay on Azure
Deploy with deployMesh=true. The template adds two apps from the aioe-mesh-services image (put it in the registry with the others, tagged meshImageTag):
aioe-<env>-relay: the mesh relay, public HTTPS, one replica;aioe-<env>-gateway: the gateway, reachable only inside the environment.
Pass gatewaySecret (a long random string, for example openssl rand -base64 32) and signingKey with the deployment. The template points the API at both, and lists two relays for nodes: the API's own host first (the API passes /relay/v1/mesh through to the relay app, so a network that allows only your API's host name still works), then the relay app's own host name.
Add the overlay elsewhere
The Helm chart does not include the overlay services. Run the aioe-mesh-services image twice, with different commands:
| Service | Command | Exposure |
|---|---|---|
| Mesh relay | aioe-relay --api https://aioe.example.com --http :8080 | Public HTTPS (put it behind your ingress with TLS). Health check /healthz. |
| Gateway | aioe-gateway --url https://aioe.example.com --token-file <file> --http :8080 --listen-port 51820, with AIOE_GATEWAY_SECRET in its environment | Private: only the API calls it. |
The gateway's token file holds aioe_gw_<tenant>.<secret>, where <tenant> is your AIOE_BOOTSTRAP_TENANT and <secret> is the gateway secret. Then set these on the API:
| Variable | Value |
|---|---|
AIOE_GATEWAY_URL | The gateway's private HTTP address. |
AIOE_GATEWAY_SECRET | The same secret the gateway holds. |
AIOE_RELAY_UPSTREAM | The mesh relay's address, so the API serves the relay on its own host under /relay/v1/mesh. |
AIOE_MESH_RELAYS | The relays nodes try, in order, as JSON. |
AIOE_SIGNING_KEY | The platform's signing key, shared by every API replica. |
[
{ "id": "api", "region": "syd", "host": "https://aioe.example.com/relay/v1/mesh", "port": 443 },
{ "id": "syd", "region": "syd", "host": "relay.aioe.example.com", "port": 443 }
]When both gateway values are set, the API logs remote control: mesh first, relay fallback; otherwise remote control: relay only.
What nodes need from the network
| Need | Default | Without it |
|---|---|---|
| Outbound UDP, for WireGuard and STUN | STUN servers stun.cloudflare.com:3478 and stun.l.google.com:19302 (AIOE_MESH_STUN) | Nodes use the mesh relay. When no STUN server answers, the relay goes first, so they connect in seconds. |
| Outbound HTTPS to the API and the mesh relay | port 443 | Nothing works: this is the one requirement. |
| WebSockets through your proxy | tried first | Nodes switch to a plain HTTPS stream through the same relay, and retry WebSockets every ten minutes. |
The agent honours HTTPS_PROXY, HTTP_PROXY and NO_PROXY, including a Basic user name and password in the proxy address. Windows integrated proxy sign-in (Negotiate or NTLM) is not supported: put a Basic credential in the proxy address, or allow the platform's host without authentication. Behind a proxy that inspects TLS, give the agent your proxy's CA with --ca-file (or AIOE_CA_FILE).
Other overlay settings (MTU 1280, map refresh every 30 s, keepalive 25 s, offline after 120 s) are in Environment variables.
The overlay agent
aioe-mesh is a single program for Linux, Windows and macOS that puts a machine on the overlay. It runs WireGuard in user space: it needs no administrator rights, no network interface and no routes, and only the ports it is told to expose accept connections from the overlay. AI Workbench and headless nodes run it for you.
| Command | What it does |
|---|---|
aioe-mesh run | Joins the overlay and stays connected until stopped. |
aioe-mesh status [--json] | Shows this node, its address, endpoints and peers, with the last handshake of each. |
aioe-mesh keygen | Prints this node's public key, creating the key pair if there is none. |
aioe-mesh version | Prints the version. |
aioe-mesh capabilities | Prints what this build can do, for example dpop. |
Exit code 3 means the platform rejected the token: sign in again before restarting. The agent keeps its key and status in ~/.local/state/aioe-mesh on Linux and macOS (or $XDG_STATE_HOME/aioe-mesh), and %LOCALAPPDATA%\MojoUp\aioe-mesh on Windows.
Not in Mojo Up AI Cloud
The overlay, the screen view and the console's Mesh page are part of self-hosted AIOE only.