Relay Last verified:

How the Relay Works

How a device connects to a Viaalink Relay, how an address reaches that device, which ports and protocols are involved, what is encrypted where, and how connections recover.

The short version

A machine that wants to publish something opens one outbound SSH connection to your Relay. Over that connection it asks the Relay to accept traffic on its behalf. When a visitor opens the address, the Relay sends the request back down the same SSH connection to the machine, and the machine hands it to the local service.

Nothing on the publishing machine listens on the internet. It needs no open port and no router rule — only the ability to reach the Relay’s SSH port (2222).

The parts

PartRuns onWhat it does
Relay server (viaalink-relay)Your server, NAS, or PCAccepts SSH connections from devices, hands out addresses, receives visitor traffic, and carries it to the right device. Also serves the setup dashboard (/_relay/dashboard) and, when enabled, calls through firewalls.
Caddy (optional, in front)Same machineTakes public ports 80 and 443, holds the Let’s Encrypt certificates, and passes requests to the Relay’s HTTP listener on 8080. See Run Relay with Docker.
ClientThe machine you publish fromEither standard OpenSSH (ssh -R) or the client built into the Relay binary (viaalink-relay connect). via publish starts the built-in client for you.
VisitorAnywhereOpens the address in a browser or app. Needs nothing installed and no key.

The path of one request

text

Step by step: a device connects

  1. TCP connection. The device connects to the Relay’s SSH listener (--ssh-address, port 2222 in every package). It must finish logging in within five seconds (--cleanup-unauthed-timeout, default 5s), or the Relay closes the connection.
  2. SSH handshake. The Relay proves who it is with its SSH host key, kept in the folder set by --private-keys-directory and created on first start when that folder is empty. Everything after this point is encrypted.
  3. Login. The device proves who it is with an SSH key. The Relay compares the key with the approved keys in --authentication-keys-directory: every file there may hold one or more lines in authorized_keys format, and the folder is re-read every 200 ms, so adding or deleting a file takes effect without a restart. A shared password (--authentication-password) is possible but off by default. A device gets three tries per connection. In license mode the Relay also checks that a new device fits your plan’s number of connected devices.
  4. Asking for addresses. The device sends standard SSH remote-forward requests — the same ones ssh -R sends. The Relay decides what each request becomes from the name and the port in it (see the next section) and replies with what it assigned.
  5. The welcome text. If the device opens an SSH session (plain ssh does; viaalink-relay connect does too), the Relay writes --welcome-message and one line per address, such as HTTPS: https://k3x.relay.example.com or TCP Alias: mybox:22. This text is the authoritative statement of the address the Relay assigned.
  6. Traffic. For every visitor connection, the Relay opens a new channel to the device over the existing SSH connection. The client connects that channel to the local service (for example localhost:3000) and copies bytes both ways.

The Relay keeps no state for a device beyond its open connection: when the SSH connection closes, every address it held is removed.

How devices get approved

A device’s public key reaches --authentication-keys-directory in one of three ways:

  • the operator copies the .pub file into the folder (Access Control and Security);
  • the operator pastes it, or generates a new key pair, on the Users tab of /_relay/dashboard;
  • the device registers itself by sending its public key to /_relay/register together with an invite code the operator created on the dashboard (or with no code when the operator allows open registration). This endpoint only answers when the Relay runs with a license file.

What a request turns into

The name and port a device asks for decide the kind of address:

The device asks forExampleBecomesWho can reach it
a name with port 80 or 443ssh -R myapp:80:localhost:3000a web address, https://myapp.relay.example.comAnyone, unless you add a password per link or IP/country rules
localhost (or an IP) with another portssh -R 5432:localhost:5432a public TCP port on the RelayAnyone who can reach that port
a name with another portssh -R mybox:22:localhost:22a private alias, mybox:22Only other devices logged in to the same Relay — see SSH through the Relay

Names and ports are chosen by the Relay unless you change these defaults:

  • --bind-random-subdomains (on by default) gives every web address a random three-character name. Turn it off to let devices keep the name they ask for.
  • --bind-random-aliases (on by default) does the same for private aliases.
  • --bind-random-ports (on by default) gives every public TCP forward a random port from --port-bind-range.

With random names on, a device gets a new address each time it reconnects. The welcome text and the client’s output always show the current one.

An address can also carry a path (--bind-http-path) or a password per link (user:password@myapp:80:localhost:3000, allowed while --bind-http-auth is on).

Ports and protocols

PortProtocolOpened byPurpose
2222TCP (SSH)Publishing devices and SSH users, inbound to the RelayDevice connections, private aliases, and SSH through the Relay
80TCP (HTTP)Visitors and Let’s EncryptPlain HTTP, redirect to HTTPS, HTTP-01 certificate checks
443TCP (HTTPS)VisitorsEvery published web address
8080TCP (HTTP)Caddy on the same machine; your browser on the LANThe Relay’s own HTTP listener behind Caddy, and the setup dashboard before HTTPS is set up
8443TCP (HTTPS)—The Relay’s own HTTPS listener, used only with --https
randomTCPAnyonePublic TCP forwards, when you use them
3478UDPThe Viaalink appCalls through firewalls (STUN and TURN)
49152–49167UDPThe Viaalink appMedia for calls through firewalls, one port per stream
4222TCPThe Viaalink app, optionalMessaging (--nats-enabled)
4223TCP (WebSocket)Phones, optionalMessaging for phone clients

The binary’s compiled defaults are localhost:2222, localhost:80 and localhost:443; the Linux, Docker and ASUSTOR setups change them to :2222, :8080 and :8443 so Caddy can own 80 and 443. The full router guidance is on Requirements and Network Architecture.

Only the Relay needs open ports. Publishing machines and SSH users connect out to 2222 and need nothing open themselves.

Security model

What is encrypted where

HopProtection
Visitor → RelayHTTPS with the Relay’s Let’s Encrypt certificate. Port 80 stays plain HTTP unless you redirect it (--force-all-https).
Caddy → Relay HTTP listenerPlain HTTP, but only inside the Relay machine or its private container network.
Relay → deviceInside the device’s SSH connection (encrypted and integrity-checked).
Device → local serviceWhatever the local hop is — usually plain traffic to localhost on the same machine.

HTTPS ends at the Relay: the Relay has to read the request to know which device it belongs to, so whoever runs the Relay could, in principle, see web traffic in the clear. When that is not acceptable:

  • use TLS passthrough (--sni-proxy): the Relay routes on the name in the TLS handshake and your own service holds the certificate;
  • or publish a protocol that encrypts by itself — SSH over a private alias, for example. The Relay copies those bytes without being able to read them.

Who proves what to whom

WhoProves itself toHow
DeviceRelaySSH key listed in the approved keys folder (or the shared password, if you turned it on)
RelayDeviceSSH host key. OpenSSH checks it against ~/.ssh/known_hosts and warns when it changes. The built-in client (viaalink-relay connect) currently accepts the host key without checking it — use OpenSSH when you need the Relay’s identity pinned.
RelayVisitorIts Let’s Encrypt certificate
VisitorRelayNothing by default. Optional: a password per link, IP and country rules (Access Control and Security)
OperatorSetup dashboardThe setup code (--setup-token, or the generated setup-token.txt)

Keeping the connection alive

From the Relay’s side

The Relay sends a keepalive request to every device every 5 seconds (--ping-client, --ping-client-interval). A connection that stays silent for that interval plus --ping-client-timeout (5 seconds) is closed, and its addresses are removed. A device that disappears without saying goodbye is gone from the Relay within about ten seconds.

From the built-in client’s side

viaalink-relay connect (and so via publish) keeps the connection up by itself:

  • Keepalive: it sends a keepalive every --keepalive seconds (default 10), waits --keepalive-timeout seconds (default 5) for each answer, and treats three missed answers in a row as a dead connection.
  • Reconnect: with --reconnect (on by default) it reconnects after a drop, waiting 1 second before the first attempt and doubling the wait up to 60 seconds, with up to a quarter extra at random so many devices do not return at the same moment.
  • Fresh start: after reconnecting it asks for every address again. With random names on, the addresses change — the new ones are printed as soon as the Relay assigns them.

With plain OpenSSH

OpenSSH does not reconnect on its own. Add ServerAliveInterval and ExitOnForwardFailure to the connection and run it under a service manager that restarts it, or use viaalink-relay connect instead. SSH through the Relay has a ready-made ~/.ssh/config.

Asking a Relay what it serves

To see which domains a Relay serves without publishing anything, connect with your key and read its policy:

bash

Nothing is bound and nothing appears in the Relay’s list of connections. Add --json for machine-readable output.