A real deployment could not send mail through an IP-authorized relay. Every attempt surfaced as MAIL_PROVIDER_UNAVAILABLE while the relay was healthy and swaks sent from the same machine. The cause was the greeting, and it is generic rather than tied to any provider. An SMTP client identifies itself in EHLO, and with nothing configured Nodemailer derives that from the machine hostname — substituting the address literal [127.0.0.1] whenever the hostname is not fully qualified, which is the normal state of a container. The relay closed the connection on it with a 421 before any message was offered, which is indistinguishable from the relay being down. EKKLESIA_SMTP_HELO_NAME now lets the deployment say what to greet as. It is validated as a fully qualified domain name — no scheme, port, brackets, whitespace, or control characters, and at least one dot, since a single unqualified label is exactly the input Nodemailer already discards. It is not a credential: it travels in the clear on every connection, so Doctor prints it where an operator can compare it against what the relay expects, and Doctor's probe performs the same greeting the runtime does. Absent preserves Nodemailer's behaviour exactly. It is deliberately not defaulted to the sender's domain: a machine whose hostname is already a valid FQDN may be greeting correctly, and some relays check that against reverse DNS, so replacing it silently would trade one deployment's bug for another's. The regression guard is a raw SMTP server that refuses any greeting but the expected one, because smtp-server exposes no EHLO hook and its earliest hook is never reached by a send-nothing probe. Co-Authored-By: Claude Opus 5 <[email protected]>
111 lines
6.5 KiB
Bash
111 lines
6.5 KiB
Bash
# Local development only. Replace every credential outside a developer workstation.
|
|
#
|
|
# `pnpm doctor` loads this file's uncommitted `.env` counterpart and evaluates exactly the host
|
|
# infrastructure configuration `pnpm api:start` would use, so the readiness report reflects the real
|
|
# deployment without the API process running.
|
|
|
|
# Required for a local workstation, and only ever correct there. Anything else — including an
|
|
# unset value — is validated as production, which requires certificate-verified PostgreSQL TLS
|
|
# (EKKLESIA_DATABASE_TLS_MODE defaults to verify-full) and rejects loopback/http identity origins.
|
|
# The plaintext loopback DATABASE_URL below is therefore only usable with this set.
|
|
NODE_ENV=development
|
|
|
|
POSTGRES_DB=ekklesia
|
|
POSTGRES_USER=ekklesia
|
|
POSTGRES_PASSWORD=ekklesia-development-only
|
|
POSTGRES_PORT=5432
|
|
DATABASE_URL=postgresql://ekklesia:[email protected]:5432/ekklesia
|
|
|
|
# Ekklesia's object-storage provider (ADR 0030). Absent S3_ENDPOINT means this installation has no
|
|
# object storage, which is a complete deployment: only a module that declares a logical bucket in its
|
|
# manifest is affected, and it is reported blocked rather than breaking anything. Production requires
|
|
# https, exactly as the database transport requires verified TLS.
|
|
#
|
|
# These are host infrastructure. No module ever sees them: a module names a logical bucket and the
|
|
# host resolves the binding.
|
|
S3_ACCESS_KEY=ekklesia-development
|
|
S3_SECRET_KEY=ekklesia-development-secret
|
|
S3_ENDPOINT=http://127.0.0.1:8333
|
|
S3_PORT=8333
|
|
S3_REGION=us-east-1
|
|
S3_FORCE_PATH_STYLE=true
|
|
# The origin a *browser* reaches object storage on, used only when signing temporary read URLs for
|
|
# direct delivery (ADR 0031). Leave it unset whenever S3_ENDPOINT is itself reachable from a browser,
|
|
# which is the case for this development stack: Compose publishes the service on loopback, so the
|
|
# endpoint above serves both roles and direct delivery works with no extra configuration.
|
|
#
|
|
# Set it when Core reaches storage by a name a browser cannot resolve — a Compose service name such
|
|
# as http://object-storage:8333, for instance — and browsers reach it through a public origin:
|
|
# S3_DELIVERY_ENDPOINT=https://storage.example.org
|
|
# It must be the origin the browser actually requests. An S3-compatible signature covers the request
|
|
# Host, so a URL signed for one origin cannot be rewritten to another afterwards; a reverse proxy in
|
|
# front of storage must therefore pass the original Host header through unchanged. The scheme is not
|
|
# signed, so terminating TLS at that proxy and forwarding plaintext to the service is fine.
|
|
# S3_DELIVERY_ENDPOINT=
|
|
# Namespaces every physical bucket this installation owns, so several Ekklesia-compatible deployments
|
|
# can share one S3-compatible service. Optional; defaults to "ekklesia".
|
|
S3_BUCKET_PREFIX=ekklesia
|
|
# Consumed only by the development Compose service's own bootstrap, never by Ekklesia's provider —
|
|
# physical buckets are derived from a module's manifest declaration, not configured here.
|
|
S3_BUCKET=ekklesia-development
|
|
|
|
# Ekklesia's outbound email transport (ADR 0037). Absent EKKLESIA_SMTP_HOST means this installation
|
|
# sends no email, which is a complete deployment: nothing in the platform sends any yet, and a
|
|
# future feature that needs to is told so rather than crashing.
|
|
#
|
|
# These are host infrastructure. No module ever sees them, and no feature chooses its own sender:
|
|
# the From address below is the only one this platform can send as.
|
|
#
|
|
# EKKLESIA_SMTP_HOST is a bare hostname — never a URL, a port, or a connection string.
|
|
# EKKLESIA_SMTP_SECURITY is exactly one of:
|
|
# implicit-tls TLS from the first byte, conventionally port 465
|
|
# starttls plaintext connect then a *required* upgrade, conventionally 587 (the default)
|
|
# plaintext no TLS at all; development only, and production refuses it outright
|
|
# EKKLESIA_SMTP_PORT is optional and defaults to the conventional port for the mode.
|
|
#
|
|
# Credentials are optional **as a pair**, because a relay authorized by source IP has neither.
|
|
# Setting one without the other is refused rather than silently connecting anonymously. Never commit
|
|
# a real password: this file is in every clone.
|
|
#
|
|
# A Google Workspace SMTP Relay deployment typically looks like:
|
|
# EKKLESIA_SMTP_HOST=<the relay host from Google Workspace Admin>
|
|
# EKKLESIA_SMTP_PORT=587
|
|
# EKKLESIA_SMTP_SECURITY=starttls
|
|
# [email protected]
|
|
# Whether it also needs a username and password depends on the relay's authorization setting in
|
|
# Google Workspace Admin, which varies per deployment. See docs/development/email.md.
|
|
# EKKLESIA_SMTP_HOST=
|
|
# EKKLESIA_SMTP_PORT=587
|
|
# EKKLESIA_SMTP_SECURITY=starttls
|
|
# EKKLESIA_SMTP_USERNAME=
|
|
# EKKLESIA_SMTP_PASSWORD=
|
|
# [email protected]
|
|
# EKKLESIA_SMTP_FROM_NAME=Ekklesia
|
|
#
|
|
# EKKLESIA_SMTP_HELO_NAME is the name Ekklesia announces itself as in the SMTP EHLO command.
|
|
#
|
|
# Leave it unset unless a relay refuses the connection. An SMTP client has to identify itself, and
|
|
# with nothing configured Nodemailer derives that from the machine hostname — substituting the
|
|
# address literal [127.0.0.1] whenever the hostname is not fully qualified, which is the normal
|
|
# state of a container or an unnamed VM. Some relays close the connection on that greeting with a
|
|
# 421 before any message is offered, which looks exactly like the relay being unreachable.
|
|
#
|
|
# Set it to a fully qualified domain name your deployment owns — typically the sending domain.
|
|
# No scheme, no port, no brackets, and at least one dot; anything else is refused at startup.
|
|
# EKKLESIA_SMTP_HELO_NAME=example.org
|
|
|
|
# Where an emailed verification or password-reset link sends a browser (ADR 0038).
|
|
#
|
|
# Absent means the Dashboard is served from EKKLESIA_IDENTITY_BASE_URL, which is the ordinary
|
|
# same-origin production shape. Set it only when a deployment separates the two, for example an API
|
|
# at https://api.example.org and the Dashboard at https://dash.example.org. It must also appear in
|
|
# EKKLESIA_IDENTITY_TRUSTED_ORIGINS, or the identity system refuses the emailed link — Ekklesia
|
|
# checks that at startup rather than letting every recipient discover it.
|
|
#
|
|
# This is the authenticated Dashboard host, never the public church website.
|
|
# EKKLESIA_DASHBOARD_ORIGIN=https://dash.example.org
|
|
#
|
|
# Verification and password recovery need a working relay above. The initial platform-owner
|
|
# bootstrap (pnpm identity:create-initial-owner) deliberately does not: it creates that one account
|
|
# already verified, so a fresh server can be signed in to before any mail exists.
|