docs: README — app-email relay (mailrelay) feature section
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2101,6 +2101,32 @@ Response format: `{"ok": true/false, "data": ..., "error": "...", "message": "..
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## App-email relay (`internal/mailrelay/`)
|
||||||
|
|
||||||
|
Gives deployed apps outbound email (password resets, invites, confirmations) through one managed path —
|
||||||
|
**app → in-process SMTP shim → hub → Resend** — with the Resend key staying **hub-side** (never on the box).
|
||||||
|
Architecture **Shape 1**: the shim runs in-process inside the controller, reusing the existing hub client.
|
||||||
|
Implements `felhom.eu/documentation/audits/SPIKE-smtp-app-relay-2026-06-28.md`.
|
||||||
|
|
||||||
|
- **The shim (`internal/mailrelay/`)** is a `go-smtp` server with two listeners — `:2525` plaintext+STARTTLS
|
||||||
|
and `:2465` implicit-TLS (self-signed cert at boot). It advertises AUTH PLAIN+LOGIN and **accepts any
|
||||||
|
credentials, ignoring them** (apps send none; some require the offer). `Data` reads the raw message, enforces
|
||||||
|
the **From-header** domain allowlist (reject `5xx` **before** any hub call), then forwards the **raw MIME** to
|
||||||
|
the hub `POST /api/v1/mail` with the controller's hub Bearer key — **single-shot** (no retry, no spool in v1).
|
||||||
|
The hub HTTP status maps to an SMTP reply (2xx→250, 4xx→451, 5xx→554). Listeners bind to the app Docker
|
||||||
|
network only (the controller container joins `traefik-public`); apps reach the shim by `felhom-controller`.
|
||||||
|
- **Lifecycle** (`lifecycle.go`): the shim starts/stops at runtime to match the global app-email toggle (no
|
||||||
|
controller restart). Wired in `main.go`, gated on a configured hub + the `mail_relay` kill-switch.
|
||||||
|
- **Toggles + injection:** a **global** toggle (`settings.AppEmail`, Settings page) and a **per-app** toggle
|
||||||
|
(`AppConfig.EmailEnabled`, on the app's config page, shown only for apps with an `smtp_mapping`). When both are
|
||||||
|
on and the app declares `.felhom.yml` **`smtp_mapping`**, `stackEnv` injects the relay env at compose time
|
||||||
|
(host=shim, port=2525, security/from per the mapping, From=`<app>@felhom.eu`) — derived each compose, never
|
||||||
|
persisted to `app.yaml`. Config knobs: `mail_relay` (listeners, `shim_host`, `from_domains`, kill-switch).
|
||||||
|
- **What the box never holds:** the Resend key, or any durable mail queue. v2 (deferred) = a separate
|
||||||
|
`felhom-smtp-shim` container + accept-and-spool retry + a `Resend-Idempotency-Key`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Build & Deploy
|
## Build & Deploy
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|||||||
Reference in New Issue
Block a user