2026-06-03 22:25:38 -04:00
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# tailwart — operator configuration (Tailscale × Stalwart)
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# Copy to .env and fill in. The mailbox stack (docker-compose.yml) and the
|
|
|
|
|
|
# edge proxy (caddy/docker-compose.yml) both read their values from here.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Design: Stalwart runs as a Tailscale sidecar with NO WAN presence. A
|
|
|
|
|
|
# layer-4 (TCP) Caddy proxy on a public-IP host pipes the raw mail ports to it
|
|
|
|
|
|
# over the tailnet. The proxy can live on a DIFFERENT machine than the mailbox
|
|
|
|
|
|
# — that's the whole point. See CLAUDE.md.
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Tailscale — same OAuth client as the rest of the tailnet
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Sidecar advertises tag:stalwart. Assign that tag to the OAuth client on both
|
|
|
|
|
|
# the Devices/Core and Keys/AuthKeys scopes, or node creation 403s at boot.
|
|
|
|
|
|
TS_OAUTH_CLIENT_SECRET=
|
|
|
|
|
|
TS_TAILNET=tailfe8c.ts.net
|
|
|
|
|
|
|
|
|
|
|
|
# MagicDNS hostname for the Stalwart sidecar → reachable at
|
|
|
|
|
|
# ${STALWART_MAGIC_NAME}.${TS_TAILNET} over the tailnet.
|
|
|
|
|
|
STALWART_MAGIC_NAME=stalwart
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Mail identity
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# The domain you send/receive for, and the public MX/hostname clients connect
|
|
|
|
|
|
# to (its A record points at the EDGE PROXY's public IP, not the mailbox).
|
|
|
|
|
|
STALWART_DOMAIN=infinidim.net
|
|
|
|
|
|
STALWART_HOSTNAME=mail.infinidim.net
|
|
|
|
|
|
|
|
|
|
|
|
# Initial admin password for the Stalwart web console (first boot only).
|
|
|
|
|
|
# Generate: openssl rand -base64 24
|
|
|
|
|
|
STALWART_FALLBACK_ADMIN_SECRET=
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Over-engineering, part 1/3 — Postgres (shared instance on the tailnet)
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Stalwart's metadata + full-text store. Create the role/db once (see README).
|
|
|
|
|
|
DB_MAGIC_NAME=the-record-prod
|
|
|
|
|
|
STALWART_DB_NAME=stalwart
|
|
|
|
|
|
STALWART_DB_USER=stalwart
|
|
|
|
|
|
# Generate: openssl rand -base64 24 (avoid / and + if you ever inline it in a URL)
|
|
|
|
|
|
STALWART_DB_PASSWORD=
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Over-engineering, part 2/3 — Redis (shared instance on the tailnet)
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Stalwart's in-memory store: distributed rate limits, caches, token revocation.
|
|
|
|
|
|
REDIS_MAGIC_NAME=slo-time-prod
|
|
|
|
|
|
# Use a dedicated logical DB index so we don't collide with the fediverse apps.
|
|
|
|
|
|
STALWART_REDIS_DB=3
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Over-engineering, part 3/3 — Garage S3 (shared instance on the tailnet)
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Stalwart's blob store: message bodies, attachments, sieve scripts.
|
|
|
|
|
|
# Reuses the shared Garage access key; needs its own bucket (see README).
|
|
|
|
|
|
GARAGE_MAGIC_NAME=garage
|
|
|
|
|
|
GARAGE_REGION=garage
|
|
|
|
|
|
GARAGE_ACCESS_KEY_ID=
|
|
|
|
|
|
GARAGE_SECRET_ACCESS_KEY=
|
|
|
|
|
|
STALWART_S3_BUCKET=stalwart-mail
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Outbound delivery
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# Most VPS providers block outbound :25. If yours does, relay through a
|
|
|
|
|
|
# smarthost (host:port). Leave blank to attempt direct MX delivery.
|
2026-06-11 17:43:21 -04:00
|
|
|
|
#
|
|
|
|
|
|
# IMPORTANT: Use an IPv4 literal or a tailnet IP — never a dual-stack hostname.
|
|
|
|
|
|
# The container has no IPv6 and will NOT fall back from AAAA to A; any host
|
|
|
|
|
|
# with an AAAA record will fail immediately (os error 101). Relaying over the
|
|
|
|
|
|
# tailnet (100.x:587) sidesteps this entirely and also bypasses VPS SMTP blocks.
|
2026-06-03 22:25:38 -04:00
|
|
|
|
STALWART_SMARTHOST=
|
|
|
|
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
2026-06-13 23:35:15 -04:00
|
|
|
|
# Provisioning — `./provision-stalwart.sh` configures Stalwart from this .env
|
2026-06-03 22:25:38 -04:00
|
|
|
|
# ----------------------------------------------------------------------------
|
2026-06-13 23:35:15 -04:00
|
|
|
|
# Run it AFTER `docker compose up -d`. It writes stores, listeners, the primary
|
|
|
|
|
|
# domain (+DKIM), the admin + relay/catch-all accounts, TLS/DNS and (optional)
|
|
|
|
|
|
# SSO — all via the x: JMAP API. Idempotent; re-run any time.
|
|
|
|
|
|
#
|
|
|
|
|
|
# TIER 1 (default, trustless): leave the DNS keys below blank. The domain is
|
|
|
|
|
|
# created in MANUAL dns/dkim mode and the script PRINTS the records to publish.
|
|
|
|
|
|
# Certs: mount your wildcard, or front :80 at the edge for HTTP-01.
|
|
|
|
|
|
#
|
|
|
|
|
|
# TIER 2 (auto-DNS): set the DNS provider keys and Stalwart auto-publishes every
|
|
|
|
|
|
# record (MX/SPF/DKIM/DMARC/MTA-STS/SRV/CAA/TLS-RPT) and does ACME DNS-01.
|
|
|
|
|
|
# Provider tokens are fiddly and provider-specific (HE's API is flaky; Spaceship
|
|
|
|
|
|
# needs API access enabled on the key) — so this stays opt-in and user-managed.
|
|
|
|
|
|
STALWART_DNS_PROVIDER=spaceship # currently only 'spaceship' is wired
|
|
|
|
|
|
STALWART_DNS_API_KEY= # set => tier 2; blank => tier 1
|
|
|
|
|
|
STALWART_DNS_API_SECRET=
|
|
|
|
|
|
STALWART_DNS_DESC=managed # label for the x:DnsServer entry
|
|
|
|
|
|
# ACME contact email (enables Let's Encrypt DNS-01 in tier 2). Blank = skip ACME.
|
|
|
|
|
|
STALWART_ACME_CONTACT=
|
|
|
|
|
|
|
|
|
|
|
|
# --- SSO: let Authelia manage Stalwart login (optional) ---------------------
|
|
|
|
|
|
# true => provision-stalwart.sh creates an OIDC directory pointing at
|
|
|
|
|
|
# AUTHELIA_PORTAL_URL and prints the Authelia client block to paste. admin and
|
|
|
|
|
|
# the relay account KEEP password auth as break-glass, so SSO can't lock you out.
|
|
|
|
|
|
# (Login flow is UNVALIDATED on a throwaway here — test before trusting upstream.)
|
|
|
|
|
|
STALWART_SSO_ENABLE=false
|
|
|
|
|
|
STALWART_OIDC_CLIENT_SECRET= # shared secret for the Stalwart<->Authelia client
|
2026-06-11 21:30:18 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# AUTHELIA — merged from /opt/authelia. Shares TS_OAUTH_CLIENT_SECRET, TS_TAILNET,
|
|
|
|
|
|
# DB_MAGIC_NAME, REDIS_MAGIC_NAME from the stalwart section above.
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# authelia — operator configuration (SSO / 2FA / OIDC, Tailscale sidecar)
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
# Copy to .env and fill in. Self-contained stack at /opt/authelia, outside any
|
|
|
|
|
|
# upstream repo. Reuses the shared tailnet Postgres + Redis + SMTP relay.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Authelia is a forward-auth / OIDC provider: it runs tailnet-only and the main
|
|
|
|
|
|
# box Caddy fronts its portal (auth.<domain>) and routes protected vhosts
|
|
|
|
|
|
# through its /api/verify endpoint. See caddy-forward-auth.snippet.
|
|
|
|
|
|
# ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
# Tailscale — same OAuth client as the rest of the tailnet (tag:authelia).
|
|
|
|
|
|
AUTHELIA_MAGIC_NAME=authelia
|
|
|
|
|
|
|
|
|
|
|
|
# Identity / cookie scope. Portal lives at AUTHELIA_PORTAL_URL; the session
|
|
|
|
|
|
# cookie is valid across *.AUTHELIA_DOMAIN.
|
|
|
|
|
|
AUTHELIA_DOMAIN=infinidim.net
|
|
|
|
|
|
AUTHELIA_PORTAL_URL=https://auth.infinidim.net
|
|
|
|
|
|
|
|
|
|
|
|
# Postgres (shared) — storage backend (users' 2FA devices, identity, consent).
|
|
|
|
|
|
AUTHELIA_DB_NAME=authelia
|
|
|
|
|
|
AUTHELIA_DB_USER=authelia
|
|
|
|
|
|
# Generate: openssl rand -hex 24
|
|
|
|
|
|
AUTHELIA_DB_PASSWORD=
|
|
|
|
|
|
|
|
|
|
|
|
# Redis (shared) — session backend. Dedicated logical DB index.
|
|
|
|
|
|
AUTHELIA_REDIS_DB=4
|
|
|
|
|
|
|
|
|
|
|
|
# SMTP relay (shared) — password-reset + 2FA notifications.
|
|
|
|
|
|
SMTP_HOST=smtp.example.com
|
|
|
|
|
|
SMTP_PORT=587
|
|
|
|
|
|
SMTP_USER=
|
|
|
|
|
|
SMTP_PASSWORD=
|
|
|
|
|
|
AUTHELIA_SMTP_SENDER=no-reply@infinidim.net
|
|
|
|
|
|
|
|
|
|
|
|
# Authelia secrets. Generate each: openssl rand -hex 32
|
|
|
|
|
|
AUTHELIA_SESSION_SECRET=
|
|
|
|
|
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=
|
|
|
|
|
|
AUTHELIA_JWT_SECRET=
|
|
|
|
|
|
AUTHELIA_OIDC_HMAC_SECRET=
|
|
|
|
|
|
|
|
|
|
|
|
# First admin (web portal login). The hash goes in config/users_database.yml:
|
|
|
|
|
|
# docker run --rm authelia/authelia:latest \
|
|
|
|
|
|
# authelia crypto hash generate argon2 --password 'YOURPASS'
|
|
|
|
|
|
AUTHELIA_ADMIN_USER=admin
|
|
|
|
|
|
AUTHELIA_ADMIN_PASSWORD=
|