TS Pro Relay

Outbound email relay for hosts that block SMTP.

  • Trusted Servants Pro
  • Python
TS Pro Relay screenshot

TS Pro Relay is a small self-hosted outbound email relay for Trusted Servants Pro deployments that live on hosts which block outbound SMTP ports (DigitalOcean droplets and similar). The portal POSTs messages as JSON over HTTPS to the relay, which then delivers them by SMTP from a location with unrestricted egress — keeping mail credentials isolated from the application.

Features

  • Web admin interface with login, for configuration and monitoring
  • Transaction log of every send attempt — status, sender, recipients, subject and errors, with counters
  • Settings for the upstream SMTP server, API-key management, a sender allowlist, and attachment-size limits
  • Bearer-token API (POST /api/send) for programmatic delivery, plus a test email button
  • Encrypted at rest — SMTP credentials and API keys encrypted with RELAY_SECRET_KEY
  • Optional Cloudflare Turnstile bot protection on login, and an unauthenticated /healthz probe

Requirements

Docker (or Docker Compose), network access to an upstream SMTP server, and a TLS reverse proxy for production.

Install with Docker Compose

Create docker-compose.yml:

services:
  relay:
    image: hyprlab/tspro-relay:latest
    container_name: tspro-relay
    # The relay serves BOTH the admin UI and the JSON send API on one port.
    # In production put a TLS-terminating reverse proxy in front and have
    # Trusted Servants Pro POST to the https:// URL.
    ports:
      - "0.0.0.0:8026:8000"
    environment:
      # Signs sessions AND derives the at-rest encryption key for the stored
      # SMTP password + API key. REQUIRED — rotating it invalidates those
      # secrets. Generate one with:
      #   python -c "import secrets; print(secrets.token_urlsafe(48))"
      - RELAY_SECRET_KEY=${RELAY_SECRET_KEY:?set RELAY_SECRET_KEY in .env}
      # First-boot admin login (ignored once the admin row exists). REQUIRED —
      # the relay does not fall back to admin/admin.
      - RELAY_ADMIN_USER=${RELAY_ADMIN_USER:-admin}
      - RELAY_ADMIN_PASSWORD=${RELAY_ADMIN_PASSWORD:?set RELAY_ADMIN_PASSWORD in .env}
      - RELAY_LOG_LEVEL=${RELAY_LOG_LEVEL:-INFO}
      # Set to 1 ONLY for local HTTP testing without TLS. Leave blank in prod.
      - RELAY_INSECURE_COOKIES=${RELAY_INSECURE_COOKIES:-}
      # Optional: comma-separated IPs/CIDRs of reverse proxies. Blank (default)
      # trusts X-Forwarded-For as-is; when set, the header is honoured only
      # from these addresses, which makes logged source IPs spoof-proof.
      - RELAY_TRUSTED_PROXIES=${RELAY_TRUSTED_PROXIES:-}
      # Per-IP ceiling on /api/send requests per hour (0 disables).
      - RELAY_SEND_PER_HOUR=${RELAY_SEND_PER_HOUR:-60}
    volumes:
      - ./data:/data        # relay.db (settings, admin, transaction log)
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "python", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/healthz',timeout=5).status==200 else 1)"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

Generate a secret key into .env, then start it and open http://<host>:8026:

python -c "import secrets; print(secrets.token_urlsafe(48))"   # -> RELAY_SECRET_KEY in .env
docker compose up -d

To build from source, swap image: for build: . and run docker compose up -d --build.

Configuration

Variable Required Default Purpose
RELAY_SECRET_KEY Yes Signs sessions and encrypts the stored SMTP password + API key
RELAY_ADMIN_USER No admin Initial admin username (first boot only)
RELAY_ADMIN_PASSWORD No admin Initial admin password (change via the UI)
RELAY_LOG_LEVEL No INFO DEBUG / INFO / WARNING / ERROR

The upstream SMTP host, credentials, API key, sender allowlist and attachment limit are all managed from the Settings page rather than env vars.

Example .env

Copy this to .env next to your docker-compose.yml and adjust:

# --- Trusted Servants Pro email relay ---
# Copy to `.env` and fill in. Never commit the real .env.
#
# Everything else (SMTP server, API key, allowed senders, admin password)
# is configured from the relay's web interface — no JSON/env editing.

# Signs login sessions AND encrypts the stored SMTP password + API key.
# REQUIRED. Generate a strong value:
#   python -c "import secrets; print(secrets.token_urlsafe(48))"
# Rotating this invalidates stored secrets — you'd re-enter them in the UI.
RELAY_SECRET_KEY=

# First-boot admin login. Used only to create the initial admin account;
# change the password from the UI afterwards (these are then ignored).
# REQUIRED — the relay refuses to start without a password, and forces a
# change at first login if it is ever seeded as "admin".
RELAY_ADMIN_USER=admin
RELAY_ADMIN_PASSWORD=change-me-on-first-login

# Optional: comma-separated IPs/CIDRs of reverse proxies
# (e.g. 127.0.0.1/32 or 172.16.0.0/12 for a proxy in docker).
# Blank (default): X-Forwarded-For is trusted as-is for logged client
# IPs. When set, the header is only honoured from these addresses,
# which makes the Transaction Log's source IPs spoof-proof.
RELAY_TRUSTED_PROXIES=

# Optional: per-IP ceiling on /api/send requests per hour (0 disables).
# Bounds spam amplification if the API key ever leaks. Default 60.
RELAY_SEND_PER_HOUR=60

# Optional: log verbosity (DEBUG|INFO|WARNING|ERROR). Default INFO.
RELAY_LOG_LEVEL=INFO

# Optional: set to 1 ONLY for local HTTP testing (no TLS). This lets the
# login cookie work over plain http://. Leave blank in production (TLS).
RELAY_INSECURE_COOKIES=

Connect it to Trusted Servants Pro

Run the relay behind TLS (e.g. Caddy: relay.example.com { reverse_proxy 127.0.0.1:8026 }), then in the TSP portal go to Settings → Domain / Email, set the sending method to API relay (HTTPS), enter the relay URL and API key, and click Send Test — the result appears in the relay's Transaction Log.

AI notice

TS Pro Relay is built by a human maintainer working with generative AI as a development tool:

  • Code — the large majority of the code in the repository was written with Anthropic's Claude (via Claude Code), working from the maintainer's direction. The maintainer decides what gets built, reviews the results, tests every release, and signs off on everything that ships.
  • Text — documentation and release notes are largely AI-drafted and human-edited.
  • The software itself contains no AI. TS Pro Relay has no AI features and makes no requests to AI services — AI was used to build it, not to run it.

Bug reports and pull requests are welcome from humans and their AI tools alike; everything merged gets the same human review.

License

Free and open source under the GNU AGPL-3.0. Companion to Trusted Servants Pro · Source on GitHub.