v0.88.0: app-email SMTP relay (in-process shim + per-app injection)
In-process go-smtp shim (Shape 1): apps → shim → hub → Resend, Resend key stays hub-side. From-header allowlist (reject 5xx pre-hub), single-shot raw-MIME forward, status→SMTP mapping. Global + per-app toggles gate compose-time env injection from .felhom.yml smtp_mapping. Hungarian UI on settings + app config pages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,33 @@
|
||||
## Changelog
|
||||
|
||||
### v0.88.0 — App-email SMTP relay: in-process shim + per-app injection (2026-06-29)
|
||||
- **What:** deployed apps can now send outbound email (password resets, invites, confirmations) through one
|
||||
managed path — **app → in-controller SMTP shim → hub → Resend** — with the Resend key staying hub-side.
|
||||
Implements `SPIKE-smtp-app-relay-2026-06-28.md` (verdict READY). Architecture: **Shape 1**, the shim runs
|
||||
**in-process inside the controller** (operator-confirmed), reusing the controller's existing hub client.
|
||||
- **New `internal/mailrelay/`:** a `go-smtp` server with two listeners — `:2525` plaintext+STARTTLS and
|
||||
`:2465` implicit-TLS (self-signed cert generated at boot, CN/SAN = the shim service name). Advertises AUTH
|
||||
PLAIN+LOGIN and **accepts any credentials, ignoring them** (apps send none; some require the offer; a
|
||||
~15-line LOGIN sasl server fills go-sasl's gap). `policy.go` validates the **From header** domain against
|
||||
an allowlist (default `felhom.eu`) and rejects with a clean 5xx **before** any hub call. `forward.go`
|
||||
POSTs 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) so the
|
||||
app surfaces the real outcome. `lifecycle.go` starts/stops the shim at runtime so the global toggle takes
|
||||
effect without a controller restart. Listeners bind to the app Docker network only — never host/internet.
|
||||
- **Settings + injection:** new global **app-email** toggle (`settings.AppEmail{Enabled,FromName}`); new
|
||||
`.felhom.yml` **`smtp_mapping`** block (renames host/port/security/from/from-name to an app's env keys, plus
|
||||
fixed `extra` vars); per-app toggle persisted in `app.yaml` (`AppConfig.EmailEnabled`). The relay env is
|
||||
injected at compose time in `stackEnv` (host=shim, port=2525, security/from per mapping) **only when**
|
||||
global ON + per-app ON + the app has a mapping — derived each compose, never persisted. New
|
||||
`config.MailRelayConfig` (listeners, shim host, From allowlist; kill-switch).
|
||||
- **UI (Hungarian):** Settings page "Alkalmazás-email" card (global toggle + optional household From-name);
|
||||
per-app "Email-küldés" toggle on the deployed app's config page (only for apps with `smtp_mapping`),
|
||||
save → recreate the stack to apply.
|
||||
- **Tests:** `mailrelay` (happy-path passthrough byte-equality, From-reject-before-forward + companion,
|
||||
single-shot-on-hub-failure + companion, status mapping, LOGIN lifecycle, real-socket STARTTLS end-to-end);
|
||||
`stacks` (mapping parse, both-toggles-on injection, per-app/global-off no-injection, no-mapping, Mealie-style
|
||||
mapping, household From-name). New dep `github.com/emersion/go-smtp` v0.24.0 + go-sasl.
|
||||
|
||||
### v0.86.0 — Phase 2 managed updates: floor-driven auto-update (2026-06-27)
|
||||
- **What:** the controller now honors an operator-enforced **minimum version** (FLOOR) delivered on the
|
||||
hub report ACK and **auto-updates to the floor** when below it — the managed default (no customer
|
||||
|
||||
Reference in New Issue
Block a user