## Context The Journal has passkey auth (primary) and magic link login (fallback). Magic links work in dev (auto-redirect) but in production the link is only logged to console — users never receive it. No email infrastructure exists. ## Goals / Non-Goals **Goals:** - Actually deliver magic link emails in production - Send a welcome email after registration - Provider-agnostic email interface (easy to swap providers) - HTML templates with plain-text fallback - Easy to add new email types in the future (just add a template + call) - Dev mode: still log to console / return devLink (no real emails) **Non-Goals:** - Marketing emails or newsletters - Email preferences / unsubscribe (only transactional) - Inline CSS / complex email layouts (keep it simple text-focused) - Queuing or retry logic (provider handles delivery) ## Decisions ### D1: SMTP via nodemailer Use [nodemailer](https://nodemailer.com) with SMTP — standard, provider-agnostic, works with any SMTP server. Single dependency: `nodemailer` npm package. Configured via `SMTP_URL` env var (e.g., `smtp://user:pass@mail.example.com:587`). **Alternative considered**: Resend API. Simpler initial setup but creates vendor dependency. SMTP is a standard protocol that works with any provider and aligns with the self-hostable philosophy. ### D2: Email module in apps/journal/app/lib/email.server.ts Not a shared package — only the Journal sends emails. Keep it simple: - `sendEmail(to, subject, html, text)` — low-level sender - `sendMagicLink(email, link)` — template wrapper - `sendWelcome(email, username)` — template wrapper In dev mode, `sendEmail` logs to console instead of calling Resend. ### D3: Inline HTML templates Simple functions returning HTML strings. No template engine. Each email type is a function: `magicLinkTemplate(link)`, `welcomeTemplate(username)`. Plain text generated by stripping tags. ### D4: Sender domain: noreply@trails.cool Requires DNS records (DKIM/SPF/DMARC) for deliverability. Add TXT records via Terraform or manually in Hetzner DNS. ## Risks / Trade-offs - **SMTP server required** → Must provide an SMTP server. Can use any provider (Mailgun, Amazon SES, Postfix, etc.) or a simple relay. - **DNS records required** → Must add SPF/DKIM/DMARC records for deliverability. - **No built-in retry** → nodemailer doesn't queue. If SMTP is down, the send fails. Acceptable for current scale — welcome emails are fire-and-forget, magic links can be re-requested.