Self-Hosting

Deploy the Relay Server

How to run the invite relay with Docker Compose. Takes about 15 minutes on a fresh server.

Deploy the Relay Server

This guide assumes you have a Linux server with Docker and Docker Compose installed. If not, install them first: docs.docker.com/engine/install.

Get the code

Clone the repository to your server:

git clone https://github.com/your-org/proto.git
cd proto/infra/relay

Configure

Copy the example environment file:

cp .env.example .env

Edit .env:

# The public URL your relay is reachable at.
# If using Tor, this will be your .onion address (set after tor setup).
RELAY_BASE_URL=https://your-server.example.com

# The name shown on the invitation landing page.
APP_NAME=My Family Tree

# Token expiry in hours. 72 hours is the default.
TOKEN_TTL_HOURS=72

# Port the relay listens on inside the container.
# You do not need to change this unless you have a port conflict.
RELAY_PORT=3000

Start the relay

docker compose up -d

Check that it is running:

curl http://localhost:3000/health

Expected response:

{ "status": "ok" }

Test a token flow

Post a test token:

curl -s -X POST http://localhost:3000/api/token \
  -H "Content-Type: application/json" \
  -d '{"ciphertext":"dGVzdA","ttlHours":1}' | jq

You should receive a response with an invite URL.

Claim the token:

curl -s http://localhost:3000/api/invite/<token-from-above> | jq

The ciphertext should be returned and the token deleted. Claiming again should return a 404.

Expose the relay

Do not expose the relay directly with a public IP and no other protection. The relay has no authentication on who can post tokens. Rate limiting is built in, but you should put it behind a reverse proxy (nginx or Caddy) with TLS, or preferably behind Tor.

If you want HTTPS with a domain name, set up Caddy in front of the relay:

# Add to docker-compose.yml under services:
caddy:
  image: caddy:alpine
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./Caddyfile:/etc/caddy/Caddyfile
    - caddy-data:/data
# Caddyfile
your-server.example.com {
    reverse_proxy relay:3000
}

For Tor configuration (recommended over public HTTPS), see Configure Tor Hidden Service.

Logs

The relay logs nothing about tokens or IP addresses by design. The only log output is startup confirmation and health check responses.

To view operational logs:

docker compose logs -f relay

Stop and restart

docker compose down    # stop
docker compose up -d   # start

Token data lives in memory. Pending tokens are lost on restart. This is intentional — restart clears all pending invitations. Tell your users to regenerate any pending links after a server restart.

Update

git pull
docker compose build
docker compose up -d