Configuration Reference
Both components read a TOML config from /etc/hash-relay/. The install scripts generate these for you; the snippets below show the key sections.
server.toml
# ── Logging ──────────────────────────────────────────────
[log]
file_enabled = true
log_dir = "/var/log/hash-relay"
level = "info" # trace|debug|info|warn|error|off
# ── Network ──────────────────────────────────────────────
[server]
listen = "0.0.0.0"
tcp_port = 8555
quic_port = 9555
# ── Encryption / TLS ─────────────────────────────────────
[server.enc]
tls = "self_signed" # self_signed | acme | file
frame_encrypt_alg = 0 # 0=off 1=AES-GCM 2=ChaCha20
# For tls = "acme":
# domain = "relay.example.com"
# contact = "mailto:admin@example.com"
# For tls = "file":
# cert_path = "/etc/ssl/certs/hash-relay/fullchain.pem"
# key_path = "/etc/ssl/private/hash-relay/privkey.pem"
# ── Auth ─────────────────────────────────────────────────
[server.auth]
type = "token"
max_timestamp_drift = 300
# ── Database (URL scheme selects the driver) ─────────────
[server.database]
url = "sqlite:///var/lib/hash-relay/hashrelay.db?mode=rwc"
# url = "mysql://user:pass@127.0.0.1:3306/hashrelay"
# url = "postgres://user:pass@127.0.0.1:5432/hashrelay"
max_connections = 50
min_connections = 5
# ── Management API ───────────────────────────────────────
[server.api]
listen = "127.0.0.1"
port = 8080
jwt_secret = "CHANGE_ME_TO_A_LONG_RANDOM_STRING"
jwt_expiry = 86400
# ── Web admin UI (served on the API port) ────────────────
[server.web]
enabled = true
dir = "/usr/share/hash-relay/web"client.toml
[log]
level = "info"
[client]
server_addr = "relay.example.com"
tcp_port = 8555 # must match server [server].tcp_port
quic_port = 9555 # must match server [server].quic_port
client_id = "farm-a"
token = "CHANGE_ME_ACCESS_TOKEN"
[client.enc]
frame_encrypt_alg = 0 # must match server
insecure = true # skip cert validation (self-signed)
# pin_sha256 = "..." # pin self-signed cert (from server logs)
[client.connection]
reconnect_interval = 5
max_reconnect_attempts = 0 # 0 = unlimited
heartbeat_interval = 60
heartbeat_timeout_count = 3Local listen ports are not configured on the client. After handshake, the server pushes port mappings via ConfigSync and the client opens listeners dynamically. The transport (TCP or QUIC) follows each mapping's protocol.
Environment Variable Overrides
The install scripts accept these overrides; RUST_LOG and DATABASE_URL also apply at runtime via the service unit:
| Variable | Applies To | Effect |
|---|---|---|
| TCP_PORT / QUIC_PORT / API_PORT | server install | Set listen ports |
| DATABASE_URL | server | Override [server.database].url |
| JWT_SECRET | server install | Set [server.api].jwt_secret |
| SERVER_ADDR / TCP_PORT / QUIC_PORT | client install | Set relay endpoint |
| CLIENT_ID / TOKEN | client install | Set node identity & token |
| RUST_LOG | both | Override [log].level |
TLS Options
- self_signed (default) — persistent self-signed cert for LAN / no domain; pin it on clients with
pin_sha256 - acme — automatic Let's Encrypt issuance and renewal (requires a domain)
- file — supply your own
cert_path/key_path
Certificates hot-reload — the server checks file mtime periodically, so no restart is needed after renewal.