hub v0.18.0: app-email passthrough POST /api/v1/mail → Resend SMTP
Raw-MIME passthrough (STARTTLS, AUTH LOGIN) — separate from the notify HTTP-API alert path (which drops inline CID images). Per-customer token-bucket rate limit, From-header allowlist backstop. Resend key stays hub-side. No new external dep. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,27 @@
|
||||
# Felhom Hub — Changelog
|
||||
|
||||
## v0.18.0 — App-email passthrough: POST /api/v1/mail → Resend SMTP (2026-06-29)
|
||||
|
||||
The hub can now relay a customer box's outbound app email to Resend, re-emitting the raw MIME
|
||||
**unchanged** over SMTP. This is the hub leg of the app-email relay (apps → on-box shim → hub →
|
||||
Resend); the Resend key stays hub-side. Implements `documentation/audits/SPIKE-smtp-app-relay-2026-06-28.md`.
|
||||
|
||||
- **New `internal/mailrelay/relay.go`:** `ResendSMTP` (a `Sender`) — STARTTLS to `smtp.resend.com:587`,
|
||||
`AUTH LOGIN resend/<key>` (a small `net/smtp.Auth` LOGIN impl; stdlib ships PlainAuth only), then raw
|
||||
`MAIL`/`RCPT`/`DATA`. **Raw passthrough** — NOT the `internal/notify` Resend **HTTP-API** path, which is
|
||||
unchanged for the hub's own structured alerts and silently drops inline CID images (spike §4). No new
|
||||
external dependency (stdlib `net/smtp`).
|
||||
- **New `POST /api/v1/mail`** (`internal/api/mail.go`): authenticates the box (`checkAuthCustomer`), enforces
|
||||
the **From-header** domain allowlist (backstop; reject 403), applies a **per-customer in-memory token-bucket
|
||||
rate limit** (default 30/min → 429 so one box can't drain the shared Resend quota), then passes the raw
|
||||
bytes through to Resend. Success→200, Resend failure→502 (the box's shim maps that to the app).
|
||||
- **Config:** new `mail` section (`per_customer_per_minute`, `from_domains`); wired in `cmd/hub/main.go` only
|
||||
when a Resend key is present (else the endpoint returns 503). The existing `notify/dispatcher.go` alert path
|
||||
is untouched.
|
||||
- **Tests:** passthrough byte-equality (raw bytes reach the sender unchanged, not parsed), From-reject +
|
||||
companion red-proof, per-customer rate-limit + isolation + companion, send-failure→502, 401/503/400 paths,
|
||||
token-bucket unit (injected clock), LOGIN auth + From-domain parsing.
|
||||
|
||||
## v0.17.0 — Resend key sourced from a Secret, out of git (2026-06-29)
|
||||
|
||||
Resend rotation + de-git hygiene. The hub's Resend API key was committed in plaintext in
|
||||
|
||||
Reference in New Issue
Block a user