Deploying

trueseal-relay ships as a single binary. Three things to get one running: a keypair, a config, and a storage backend.

Source: github.com/julianbonomini/trueseal-relay. Binary releases on the repo’s Releases page.

Keypair generation

The relay identity is a long-term X25519 keypair. Generate it once at setup:

trueseal-relay -genkey -keyout keypair.hex

That writes the hex-encoded private key to keypair.hex (mode 0600) and prints the public key to stdout:

keypair written to keypair.hex
relay public key (share with clients): a1b2c3d4...

Guard the private key. Anyone with it can impersonate your relay — devices verify the relay’s identity using this key on every handshake. The public key is safe to share around: bake it into your client binary, publish it on a landing page, drop it in your self-hosting README.

Note: Relay impersonation doesn’t compromise data security. Everything is end-to-end encrypted before it reaches the relay, so an impersonator gets ciphertext they can’t decrypt. The damage is limited to delivery — blobs pushed to a rogue relay never reach their recipients. No data is exposed.

Configuration

trueseal-relay reads config from a TOML file (default relay.toml) with optional environment variable overrides. Any TRUESEAL_RELAY_* env var takes precedence over the file.

Minimal relay.toml:

[relay]
keypair_path = "/etc/trueseal-relay/keypair.hex"

[store]
type = "sqlite"
sqlite_path = "/var/lib/trueseal-relay/inbox.db"

Full reference:

FieldEnv varDefaultDescription
relay.keypair_pathTRUESEAL_RELAY_KEYPAIR_PATHPath to hex-encoded private key. Required.
relay.listen_pushTRUESEAL_RELAY_LISTEN_PUSH:7701Push Session listener address.
relay.listen_receiveTRUESEAL_RELAY_LISTEN_RECEIVE:7700Receive Session listener address.
relay.ttlTRUESEAL_RELAY_TTL720h (30 days)Blob TTL before reaping.
relay.reap_intervalTRUESEAL_RELAY_REAP_INTERVAL1hHow often the reaper runs.
relay.max_envelope_bytesTRUESEAL_RELAY_MAX_ENVELOPE_BYTES1048576 (1 MiB)Max envelope size. Larger blobs are rejected with Error.
store.typeTRUESEAL_RELAY_STORE_TYPEsqliteStorage backend: sqlite or postgres.
store.sqlite_pathTRUESEAL_RELAY_STORE_SQLITE_PATHSQLite database file path. Required when store.type = sqlite.

Single-node deployment

This is the default and the recommended setup for self-hosted instances. SQLite, no external dependencies.

trueseal-relay -config relay.toml

Or Docker-friendly with no config file:

trueseal-relay -no-config

With -no-config, every setting has to come from environment variables.

Example docker-compose.yml:

services:
  trueseal-relay:
    image: trueseal-relay:latest
    command: ["-no-config"]
    environment:
      TRUESEAL_RELAY_KEYPAIR_PATH: /secrets/keypair.hex
      TRUESEAL_RELAY_STORE_SQLITE_PATH: /data/inbox.db
    volumes:
      - ./keypair.hex:/secrets/keypair.hex:ro
      - relay-data:/data
    ports:
      - "7700:7700"
      - "7701:7701"

volumes:
  relay-data:

Graceful shutdown

trueseal-relay handles SIGINT and SIGTERM. On signal, it stops accepting new connections and waits up to 30 seconds for active sessions to drain cleanly before exiting.

Operational notes

  • Firewall. Open TCP 7700 (receive) and 7701 (push) to the internet. Devices connect outbound; the relay never initiates connections.
  • Disk. Size SQLite storage based on your expected group size, blob size, and TTL. A conservative rule of thumb: devices × blobs_per_day × avg_blob_bytes × ttl_days.
  • Logs. trueseal-relay logs connection events and public keys only. It never logs envelope content. Don’t add content logging — doing so would break the zero-knowledge property the whole stack rests on.