Remote Access · Rex Docs

Remote Access

A Rex server on Linux or FreeBSD listens only on Unix sockets on that machine by default. To use it from Rex for Mac or another computer, you must specify a network listener or forward its socket over SSH. This page covers each option, who it lets in, and how to add the server to a client.

Rex trusts a caller based only on how they connect today, so it can’t check who someone is unless the network tells it. That’s why servers listen only locally by default and why every listener below is cautious about who it lets in. This is a short-term limitation. A full authentication and authorization system is planned, including support for existing SSH keys.

Until then, we recommend Tailscale. It tells Rex who each caller is, which is how Rex decides who gets in, and for the login server, which account they get. Tailscale has powerful ACLs that you can use to safely restrict access for multiple users at the network level. If you can’t use Tailscale, you can use another VPN or SSH.

Listener Needs Works with Identifies callers
Built-in Tailscale A Tailscale account Personal and shared server Yes
Tailscale Serve Tailscale on the machine Shared and login server Yes
Tailnet address Tailscale on the machine Shared and login server Yes
Plain HTTP --allow-anonymous Every mode No
Another VPN --allow-anonymous Every mode No
SSH forward SSH access to the machine Personal and shared server Yes

Built-in Tailscale

With --tailscale, the server joins your tailnet as its own device, using Tailscale built into Rex. The machine doesn’t need Tailscale installed.

rex server start --tailscale

The first time, the server needs to sign in to Tailscale. Its sign-in link appears in rex server status and the server’s log. To sign in without a browser, pass a one-time auth key with --tailscale-auth-key-file FILE, or - to read it from standard input. The server keeps its login, so it needs the key only once.

The device is named rex unless you choose another name with --tailscale-hostname. Its address is https://rex.<tailnet>.ts.net. Your tailnet must have MagicDNS and HTTPS certificates turned on.

To sign it in as a tagged device, add --tailscale-advertise-tag tag:NAME. A tagged device has no owner, so this also needs --tailscale-allow-tailnet-members. See Who Can Connect.

For the shared server, add these options to the service’s command line, as in the next section.

Tailscale Serve

If the machine already runs Tailscale, Tailscale Serve can forward HTTPS connections to a socket that Rex listens on. This is the usual way to reach the shared server or the login server.

Run sudo systemctl edit rex.service (or rex-login-server.service) and add the socket to its command line. Package upgrades keep this file. For the shared server:

[Unit]
After=tailscaled.service
Requires=tailscaled.service

[Service]
ExecStart=
ExecStart=/usr/bin/rex server run --listen /run/rex/server.sock \
  --state-dir /var/lib/rex --socket-mode 0660 --log-format text \
  --extra-listen tailnet-serve:///run/rex/serve.sock

For the login server, the command line is:

ExecStart=
ExecStart=/usr/bin/rex login-server --listen unix:///run/rex/login-server.sock \
  --accounts /etc/rex/accounts.jsonc --runtime-root /run/rex --log-format text \
  --pam-service rex-login-server --extra-listen tailnet-serve:///run/rex/serve.sock

On Alpine, set command_args in /etc/conf.d/rex (or /etc/conf.d/rex-login-server) to the same command line, without /usr/bin/rex. rc_need takes the place of After= and Requires=. For the shared server:

command_args="server run --listen /run/rex/server.sock \
  --state-dir /var/lib/rex --socket-mode 0660 --log-format text \
  --extra-listen tailnet-serve:///run/rex/serve.sock"
rc_need="tailscale"

On FreeBSD, add the socket to rex_flags (or rex_login_server_flags), then restart the service. Its socket is in /var/run/rex:

sysrc rex_flags="--extra-listen tailnet-serve:///var/run/rex/serve.sock"
service rex restart

Then point Tailscale Serve at the socket:

sudo tailscale serve --bg --https=443 unix:/run/rex/serve.sock

On FreeBSD, that’s unix:/var/run/rex/serve.sock.

The address is the machine’s own tailnet name, such as https://build.<tailnet>.ts.net.

Tailscale Serve doesn’t tell Rex who a tagged device is, so tagged devices such as CI runners can’t connect this way. Use a tailnet address for them instead.

Use tailnet-serve:// for the Serve socket, never unix://. With unix://, the server starts without an error, but treats every tailnet caller as the account that runs tailscaled, usually root.

Socket-Activated Servers

With rex.socket or rex-login-server.socket, the drop-in above works unchanged, but the server creates the Serve socket only once it’s running, so a request through Tailscale Serve can’t start it. To let it, add the Serve socket to the socket unit too. Run sudo systemctl edit rex.socket (or rex-login-server.socket) and add:

[Socket]
ListenStream=/run/rex/serve.sock

Keep the --extra-listen option in the service’s drop-in. Then stop both units and start the socket again, so that systemd creates the new socket:

sudo systemctl stop rex.socket rex.service
sudo systemctl start rex.socket

For the login server, that’s rex-login-server.socket and rex-login-server.service. The Serve socket gets the socket unit’s mode, and Tailscale Serve still reaches it, because tailscaled normally runs as root. See Customizing the Socket.

Tailnet Address

A server can also listen directly on the machine’s tailnet addresses, using the machine’s own Tailscale to identify callers. Add this to the server’s command line, the same way as the Serve socket above:

--extra-listen tailnet://:7297

The port is required. The connection is plain HTTP, encrypted by Tailscale, at http://build.<tailnet>.ts.net:7297.

Who Can Connect

A connection to a server can do anything that server can, including starting programs as its account. Control access with care.

For a personal server or the shared server, only the Tailscale user who owns the machine (or the Rex device, with built-in Tailscale) is let in by default. To let in other people:

Listener Option Lets in
Built-in Tailscale --tailscale-allow-tailnet-members Anyone on the tailnet your ACLs allow
Serve or address --tailnet-allow-identified Anyone Tailscale can identify

A tagged machine or device has no owner, so it needs one of these options to let anyone in.

The login server is different. It lets in everyone Tailscale can identify because its accounts file decides which accounts each person may use.

To see who’s connected to a server, run rex client ls on it. Its principal column shows the identity of each client.

Plain HTTP

A plain HTTP listener can’t tell callers apart, so Rex refuses one unless you also pass --allow-anonymous:

rex server start --extra-listen :7297 --allow-anonymous

Every connection then has full access. Only do this on a network you completely control. The login server lets such callers use only accounts whose principals include anonymous, and never root.

Again, on an open network this is super dangerous and you probably shouldn’t do it. This option exists so that you can pair it with a network that limits who can reach the server, such as your own VPN.

VPNs and SSH

Tailscale isn’t the only way to reach a server privately. A VPN and an SSH forward work on the same idea: the server stays off the open network, and something you already trust encrypts the connection and decides who can reach it. They differ in what the server exposes and what it learns about the caller.

Over a VPN

The server listens on the machine’s address on the VPN, such as WireGuard, ZeroTier, NetBird, Nebula, or a company VPN:

rex server start \
  --extra-listen http://10.0.0.5:7297 --allow-anonymous

Use the VPN address itself. An address without a host, such as :7297, listens on every address the machine has, including those on its other networks.

Everyone who can reach that address has full access. Rex can’t tell callers apart, so each one can do anything the server can, including starting programs as its account. On a private VPN, that means everyone on the VPN and every account on this machine. If your VPN has access rules, use them to limit which devices can reach this port. The login server limits these callers as described in Plain HTTP.

The listener depends on the VPN being up. A server that can’t listen on an address doesn’t start at all, not even on its Unix socket. So put these options on the service’s command line, as in Tailscale Serve, rather than relying on rex server start, which doesn’t save its options or change a server that’s already running. systemd and FreeBSD try again every 5 seconds, so the server comes up soon after the VPN connects.

To connect, add http://10.0.0.5:7297 as a host on a device that’s on the VPN. Some devices run only one VPN at a time, so turning on another one, including Tailscale, disconnects Rex.

SSH Forwarding

If SSH is the only way to reach the machine, you don’t need a VPN or a network listener. SSH can forward the server’s Unix socket to your Mac, and Rex connects to the local end as if the server were running there:

ssh -N build.example.com \
  -L ~/.ssh/rex-build.sock:/run/user/1000/rex/server.sock

A forward doesn’t start a server the way a local rex command does, so run rex server start on the machine first. On Linux, your personal server’s socket goes away when you log out unless you run loginctl enable-linger.

rex server status on the machine shows the path to forward. Add the full path of the local socket as a host in Rex for Mac or with rex hosts add.

The server sees your SSH user, so this needs no --allow-anonymous and opens no port. It works from a Mac or the command line but not from Rex for iOS, and it lasts only as long as the SSH connection.

Adding the Server to a Client

In Rex for Mac, choose Add Remote Host… and enter the server’s address.

On the command line, pass the address with -S, or save it as a host and use its label:

rex -S https://build.example.ts.net ls
rex hosts add build https://build.example.ts.net
rex -S build ls

A bare host name, such as build.example.ts.net, tries HTTPS on port 443, then HTTPS on port 7297, then HTTP on port 7297.

To connect to the login server as a specific account, put it before the host, such as https://root@build.example.ts.net. See Choosing an Account.

Note: rex hosts today is CLI-only. It doesn’t sync to your GUI client or across machines. This will change in the future.

NixOS

The NixOS module has options for each listener:

Option Sets up
services.rex.tailscale.enable Built-in Tailscale, with hostname, authKeyFile, and advertiseTags
services.rex.tailscaleServe.enable Tailscale Serve for the shared server
services.rex.loginServer.tailscaleServe.enable Tailscale Serve for the login server
services.rex.allowAnonymous Plain HTTP for the shared server. openFirewall opens its port.
services.rex.loginServer.allowAnonymous Plain HTTP listeners for the login server

With socketActivation on, the module’s socket unit also holds the Serve socket, so a request through Tailscale Serve starts the server.