Skip to main content
The SSH tunnel is the recommended way to drive a remote backend. The ssh client on your local machine forwards a loopback port to a Mains backend on the remote host, so the UI connects to ws://127.0.0.1:<localPort> and every byte is tunneled — encrypted and authenticated by SSH. The remote backend never has to listen on a routable interface, and there’s no hosted infrastructure to set up. This is the classic “run agents on my powerful dev box, watch and approve from my laptop” setup.

Prerequisites

  • SSH access to the remote host (an ~/.ssh/config alias or user@host).
  • The agent CLIs installed and authenticated on the remote (claude login, codex auth login, …) — that’s where they run.
  • A Mains backend reachable on the remote, either already running or auto-launched by the tunnel (below).

Connecting

Open Relay in the sidebar, go to Add, and choose SSH tunnel.
1

Name the backend

A label like Dev box — how it’ll appear in your client list.
2

Enter the SSH host

An alias from ~/.ssh/config or user@host. Detected hosts from your SSH config and known_hosts appear below the field with an Add host shortcut.
3

Set the remote port

The loopback port the backend listens on, on the remote (default 8787).
4

Optionally auto-launch the backend

Provide a launch command and the tunnel starts the backend for you, e.g.
Leave it blank to attach to a backend that’s already running (ssh -N).
5

Add, then Connect

The backend is saved to your client list. Click Connect — the status dot turns connected and the whole UI is now driving the remote.

Pairing Token

Starting The Backend On The Remote

If you aren’t auto-launching it, start a headless backend on the remote host:
It prints a pairing token and listens on 127.0.0.1:8787. Reuse that token and port when adding the tunnel.

Reliability

The tunnel sets ExitOnForwardFailure=yes (so a blocked forward fails fast instead of silently) and keepalives (ServerAliveInterval=30, ServerAliveCountMax=3) so a dropped link is detected quickly. The transport then reconnects with backoff and refetches durable state from the remote database.

Troubleshooting