Documentation / Run your own

Relay

A relay connects two devices that cannot reach each other directly, for example because they sit behind different home routers. It forwards data that is already encrypted between the devices. The project operates one official relay, relay.easysync.me, which the apps preselect in the form for adding a relay. You can run your own instead; this page is about that.

What a relay does and does not do #

  • It does not hold the keys of your devices and cannot read what they send. It does not store payload data, and it does not write application-level logs of connections or payloads. A relay that issues access keys (below) keeps one small file of hashed keys, with their days of creation and expiry.

  • It does see the IP addresses that connect to it, when they connect, for how long and how much data they send. A reverse proxy or a hosting provider in front of it sees the same. It is a content-blind relay, not an anonymous one. Read What the relay sees before you decide whom to trust with that.

  • It is not a user directory. By default a deployment has one access token (or a few, for rotation), and everyone who has it can use the relay. The token keeps strangers out; it does not separate your devices from other users of the same relay. A relay can also hand out a key of its own to every device that asks (see "Issue access keys" below); such a key limits abuse but is not proof that a request comes from an easysync app, so do not describe your relay as reachable only by easysync apps.

  • Relay (Service and Docker image): Built, tested on Linux. The relay is implemented and passes the project's tests, including pairing and file transfers through it. No Docker image is published before a release, and the relay is a single instance.

Run the image #

The relay image is the program easysync-relay in a minimal image, monozeros/easysync-relay on Docker Hub. It runs as a non-root user, works with a read-only root file system and needs no capabilities. A relay that only checks static tokens keeps no state. A relay that issues access keys writes one state file, in the volume /var/lib/easysync-relay that the image ships for it. Its configuration and secrets are files that you mount read-only; secrets are never environment variables or arguments. Its tags, how to pin it and a complete setup with TLS in the relay or behind a reverse proxy are on Run your own relay.

docker run -d --name easysync-relay \
  --read-only --cap-drop ALL --security-opt no-new-privileges --memory 2g \
  -p 443:8443 \
  -v easysync-relay-config:/etc/easysync-relay:ro \
  -v easysync-relay-state:/var/lib/easysync-relay \
  monozeros/easysync-relay:X.Y.Z

The container listens on port 8443, which needs no privilege; the host maps it to 443 in this example. The files in the mounted directory must be readable by user id 65532 and by nobody else, for example mode 0400. Size the memory limit of the container (docker run --memory) for the connections you expect: it is the budget for the buffers of all running transfers. The state volume is needed only if you issue access keys (below); leave it out otherwise. Run one instance; pairings and connections live in a single process.

The image checks itself: its health check runs easysync-relay healthcheck, which asks the relay's local health listener and exits with 0 only if it answers.

Configure it #

The relay reads relay.toml, by default /etc/easysync-relay/relay.toml. A small configuration that terminates TLS itself:

listen = "0.0.0.0:8443"
tls_cert_file = "/etc/easysync-relay/cert.pem"
tls_key_file = "/etc/easysync-relay/key.pem"
access_token_files = ["/etc/easysync-relay/token"]
  • Access token. The file holds one token as unpadded base64url text; the decoded token must be at least 16 and at most 64 bytes long. Make it from good random data, for example 32 bytes. A relay refuses to start with a token that is too short.
  • TLS. With tls_cert_file and tls_key_file the relay terminates TLS. Without them it refuses to start, unless you set allow_plain_ws = true. Do that only behind a reverse proxy that terminates TLS, on a network that nobody else can reach, because the token is sent in clear text over plain WebSocket.
  • Behind a reverse proxy. The relay does not read proxy headers on the WebSocket path, so all limits that apply per source address then apply to the proxy's address. Only the key-issuing routes can read X-Forwarded-For, and only from the proxies you list in trusted_proxies of the [issue] table (below). The proxy must not log the paths of WebSocket upgrades, the header that carries the token, nor the path of the issuing routes.
  • Limits. The [limits] table sets timeouts, rates per minute and the maximum numbers of connections. The defaults are meant to be sensible.

Certificates, keys and token files are read when the relay starts. To change them, restart the relay; running transfers continue after a short interruption.

Issue access keys #

Instead of (or besides) static tokens, a relay can give every device its own access key. A device asks once, the relay answers with 32 random bytes, and the device presents that key like any token. This is off unless you switch it on in an optional [issue] table:

[issue]
enabled = true
state_file = "/var/lib/easysync-relay/tokens.state"
  • State file. The relay stores the SHA-256 of every issued key, with the day it was created and the day it expires, and one counter for revocations; no address, origin or user agent. The directory must be writable for the relay; a file it cannot read or parse is a start error. It is not a file to back up: if it is lost, devices register again.
  • Lifetime and limits. An issued key expires when it is not used for lifetime_days; a use moves the expiry. new_per_ip_per_day, new_per_hour_total and max_active bound how many keys the relay hands out.
  • Web app. A browser may ask only from an origin listed in allowed_origins.
  • Behind a reverse proxy. Set trusted_proxies to the proxies whose X-Forwarded-For the issuing routes may trust; with allow_plain_ws the list must not be empty. The proxy sets that header itself on these routes.
  • Control file. With control_file you can switch issuing off, end every issued key at once or block single keys by their hash, without restarting the relay. The relay needs the file at start if it is configured.
  • What it means for privacy. A key identifies one device for as long as it lives: the relay sees it at every connection, and its expiry day shows roughly when it was last used. Tell your users; see What the relay sees.

Static tokens keep working beside issued keys.

All keys, with types and defaults, are listed in Relay configuration. The command line is in Relay command line.

Tell your devices about it #

On a device, add the relay. The command asks for the access token on the terminal:

easysync relay add <url>

The URL starts with wss://. To offer this relay to the devices you pair with, so that they learn about it during the pairing, add --shared.