# Operating a relay
The relay lets two peers that cannot reach each other directly (both behind NAT)
complete a transfer. Both peers dial *out* to the relay, which pairs them by a
rendezvous id and pipes bytes. The TLS-PSK session runs end-to-end *through* the
relay, so it forwards **ciphertext only** — it holds no key and cannot read any
secret, not even for the operator.
You do not need a relay for the common case: if either peer is reachable (a static
IP, a LAN, a VPN), use the direct transport (`--host`) with no shared
infrastructure. Run a relay only for the both-behind-NAT case.
## Run it
```sh
envferry relay --port 8787
# → relay listening on 0.0.0.0:8787
```
No TLS termination or certificate is needed at the relay — the transfer is already
end-to-end encrypted, so you just expose a plain TCP port.
### Flags
| Flag | Default | Purpose |
| --- | --- | --- |
| `--host
` | `0.0.0.0` | Interface to bind. |
| `--port ` | random | TCP port to listen on. |
| `--max-connections ` | `512` | Global cap on concurrent connections. |
| `--max-per-ip ` | `32` | Cap on concurrent connections from one IP. |
| `--pair-timeout ` | `300` | How long a waiting peer is held before it is dropped. |
| `--header-timeout ` | `30` | Deadline for a peer to announce its rendezvous id (slowloris defense). |
| `--max-session-bytes ` | `16777216` | Total bytes a paired session may forward before it is cut. |
| `--max-session-seconds ` | `900` | Wall-clock lifetime of a paired session. |
Public operators may want tighter session caps, e.g.
`envferry relay --port 8787 --max-per-ip 8 --max-session-bytes 4194304 --max-session-seconds 120`.
## Point clients at it
```sh
# sender
envferry send .env --relay your-relay.example:8787
# or set a persistent default (a DNS name or an IP), then omit the value:
envferry config set relay your-relay.example:8787
envferry send .env --relay
# ENVFERRY_RELAY works too and takes precedence over the config file.
```
The relay address is embedded in the `efr1_` code, so `get` needs no relay flag.
For IPv6, bracket the address: `--relay [2001:db8::1]:8787`.
If the sender runs *on* the relay host, dial it locally but advertise the public
address so the receiver can reach it:
```sh
envferry send .env --relay 127.0.0.1:8787 --relay-advertise your-relay.example:8787
```
## Running one publicly — read this first
A public relay is an **unauthenticated network service**. By design the relay is
blind, so it cannot authenticate the transfer code or inspect content — which also
means it cannot tell an envferry transfer from someone tunneling arbitrary bytes
through it. If you expose one to the internet:
- **Firewall / rate-limit it upstream.** The built-in caps (global, per-IP,
timeouts) raise the bar but are not a substitute for network-level limits.
- **Expect abuse and be able to respond.** You cannot filter by content; your
levers are the caps, IP blocks, and restarting the process (which drops all
in-flight sessions). Publish an abuse contact.
- **Set expectations.** Best-effort, no SLA; tell heavy users to self-host.
- **You see metadata, never content** — peer IPs, timing, and byte counts cross
the relay in the clear; the payload does not.
### systemd example
```ini
[Unit]
Description=envferry relay
After=network.target
[Service]
ExecStart=/usr/bin/env envferry relay --port 8787 --max-per-ip 8
Restart=on-failure
DynamicUser=yes
# Optionally restrict which IPs can reach it:
# IPAddressAllow=203.0.113.0/24
# IPAddressDeny=any
[Install]
WantedBy=multi-user.target
```
For a stronger boundary, bind the relay to `127.0.0.1` and expose it only through
a reverse proxy or a WireGuard/Tailscale network you control.