Mojo UpDocs
Self-hosted AIOE

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_TIMEOUT seconds (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.

NodeWhat it is
WorkbenchAI 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.
NodeA headless workbench on shared compute. See Nodes.
GatewayThe 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:

ServiceCommandExposure
Mesh relayaioe-relay --api https://aioe.example.com --http :8080Public HTTPS (put it behind your ingress with TLS). Health check /healthz.
Gatewayaioe-gateway --url https://aioe.example.com --token-file <file> --http :8080 --listen-port 51820, with AIOE_GATEWAY_SECRET in its environmentPrivate: 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:

VariableValue
AIOE_GATEWAY_URLThe gateway's private HTTP address.
AIOE_GATEWAY_SECRETThe same secret the gateway holds.
AIOE_RELAY_UPSTREAMThe mesh relay's address, so the API serves the relay on its own host under /relay/v1/mesh.
AIOE_MESH_RELAYSThe relays nodes try, in order, as JSON.
AIOE_SIGNING_KEYThe platform's signing key, shared by every API replica.
AIOE_MESH_RELAYS
[
  { "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

NeedDefaultWithout it
Outbound UDP, for WireGuard and STUNSTUN 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 relayport 443Nothing works: this is the one requirement.
WebSockets through your proxytried firstNodes 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.

CommandWhat it does
aioe-mesh runJoins 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 keygenPrints this node's public key, creating the key pair if there is none.
aioe-mesh versionPrints the version.
aioe-mesh capabilitiesPrints 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.

On this page