feat(email): make transactional email live — branded relay, rate-limit, squat fix (PR1a) #161
Reference in New Issue
Block a user
Delete Branch "feature/email-relay-pr1"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Make transactional email actually send: the backend already had the OTP engine and a Mailer seam, but BACKEND_SMTP_* was never in compose, so every contour fell back to the log mailer. PR1a of a layered feature; the one-tap confirm deeplink and the gateway/UI surface it needs follow in PR1b.
Backend
Mailerseam: a text + HTMLMessage, TLS mode chosen by port (implicit TLS on 465, else STARTTLS), a dial timeout, no client cert (server cert validated vs the system roots).BACKEND_PUBLIC_BASE_URL— the canonical origin for links in the email, never the request Host (anti-injection); required when a relay is configured.Deploy
TEST_) + prod-deploy.yaml (PROD_env + env.sh) + .env.example + README. An empty host keeps the log mailer, so a contour without relay credentials still boots.UI
Docs
Verified locally
gofmt/build/vet, account+server unit, integration (Email/Merge/squat, testcontainers), ui check/test:unit/build/e2e (176 passed). No schema change -> no contour wipe.
Caveats
languageis not yet forwarded by the gateway, so fresh web email-LOGIN emails default to English (link emails use the account language) — a small follow-up.feature/email-relay-deeplink-wipfor PR1b.Un-hide the Profile email box for guest accounts (hidden={!p.isGuest}): a guest binds an email to register / sign in, and a returning address opens the existing merge dialog. Provider linking stays hidden — the Telegram control keeps its wiring behind a hidden attribute — until the non-guest linking matrix (PR2). The two linking e2e specs remain skipped (they assume a non-guest login and the visible Telegram control); update their stale comments.