|
| 1 | +# Email Setup (Cloud Edition) |
| 2 | + |
| 3 | +FileMorph's Cloud-overlay features — user registration with email verification, |
| 4 | +password reset, account-deletion confirmation, and (optionally) Stripe billing |
| 5 | +receipts — depend on outbound SMTP. This guide walks through configuring it for |
| 6 | +a self-hosted deployment. |
| 7 | + |
| 8 | +The Community Edition (anonymous-tier conversion / compression with API keys) |
| 9 | +needs none of this. If you don't run the user-account features, leave the |
| 10 | +`SMTP_*` envs empty and skip this document. |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## What needs SMTP |
| 15 | + |
| 16 | +| Feature | Endpoint | What email carries | |
| 17 | +|---|---|---| |
| 18 | +| Email verification | `POST /api/v1/auth/register` and `POST /api/v1/auth/resend-verification` | One-time link binding the verify-token to the user's email-at-issuance. **7-day TTL.** | |
| 19 | +| Password reset | `POST /api/v1/auth/forgot-password` | Single-use reset link. **30-minute TTL**, invalidated by the next password change (hash-version pin). | |
| 20 | +| Account-deletion confirmation | `DELETE /api/v1/auth/account` | Post-commit notification with the user-facing support contact. | |
| 21 | +| Billing receipts | Stripe Customer Portal | Stripe sends these directly via the customer portal — FileMorph itself only sends transactional auth mail. | |
| 22 | + |
| 23 | +If `SMTP_HOST` is empty, every feature above degrades gracefully: |
| 24 | + |
| 25 | +- `/forgot-password` and `/resend-verification` return `503 Service Unavailable` |
| 26 | + with a reason string the UI surfaces. |
| 27 | +- `/register` still creates the user — the verification email is fire-and-forget. |
| 28 | + The user can request a fresh link via `/resend-verification` once SMTP is wired. |
| 29 | +- `/auth/account` deletes the user even if the confirmation email cannot be sent; |
| 30 | + the deletion itself is the legally-binding action. |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Required environment variables |
| 35 | + |
| 36 | +All documented in `.env.example`. The minimum for working transactional mail: |
| 37 | + |
| 38 | +```env |
| 39 | +SMTP_HOST=smtp.example.com # your provider's SMTP relay |
| 40 | +SMTP_PORT=587 # 587 = STARTTLS, 465 = implicit SSL |
| 41 | +SMTP_USERNAME=no-reply@example.com # the auth user (often = FROM address) |
| 42 | +SMTP_PASSWORD=... # provider-issued app password / SMTP secret |
| 43 | +SMTP_FROM_EMAIL=no-reply@example.com # user-visible FROM address |
| 44 | +SMTP_FROM_NAME=YourBrand # display name in the From: header |
| 45 | +SMTP_REPLY_TO=hallo@example.com # optional; where users hit "reply" |
| 46 | +
|
| 47 | +APP_BASE_URL=https://your-domain.example.com # used to build link URLs |
| 48 | +``` |
| 49 | + |
| 50 | +`SMTP_FROM_EMAIL` and `SMTP_FROM_NAME` are what the recipient sees. There are |
| 51 | +no hard-coded `@`-domains anywhere in the user-visible copy — every link, From: |
| 52 | +address, and Reply-To: is taken from these variables. **Self-hosters ship their |
| 53 | +own support identity end-to-end.** |
| 54 | + |
| 55 | +If `SMTP_FROM_EMAIL` is empty, the sender falls back to `SMTP_USERNAME`. Set |
| 56 | +both explicitly in production so the From: address never silently changes |
| 57 | +because someone rotated the SMTP login. |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +## Picking a port (TLS mode) |
| 62 | + |
| 63 | +The TLS mode is chosen by port, no extra flag: |
| 64 | + |
| 65 | +- `SMTP_PORT=465` — implicit SSL from the first byte (`SMTPS`). Used by some |
| 66 | + legacy providers; less common today. |
| 67 | +- `SMTP_PORT=587` — plain connect, then `STARTTLS` upgrade. The modern default. |
| 68 | + RFC 8314 § 3.3 recommends 587 for new deployments. |
| 69 | + |
| 70 | +Some cloud providers (notably Hetzner Cloud at the time of writing) block |
| 71 | +outbound port 465 for new accounts as anti-abuse, while leaving 587 open. If |
| 72 | +your provider does the same, use 587 — it's the path of least friction. Open |
| 73 | +465 only if your SMTP provider requires it and your hosting provider allows |
| 74 | +the egress. |
| 75 | + |
| 76 | +--- |
| 77 | + |
| 78 | +## Provider walk-throughs |
| 79 | + |
| 80 | +The application talks to any RFC-compliant SMTP relay. Three common paths: |
| 81 | + |
| 82 | +### A — Transactional ESP (Mailgun, Postmark, Brevo, ZeptoMail, Resend, …) |
| 83 | + |
| 84 | +Best for production: deliverability is the ESP's full-time job, and you get |
| 85 | +DKIM/SPF/DMARC alignment plus a deliverability dashboard. |
| 86 | + |
| 87 | +Typical setup (vendor-specific values shown as placeholders): |
| 88 | + |
| 89 | +```env |
| 90 | +SMTP_HOST=smtp.<provider>.com |
| 91 | +SMTP_PORT=587 |
| 92 | +SMTP_USERNAME=<api-username-or-token-key> |
| 93 | +SMTP_PASSWORD=<api-token> |
| 94 | +SMTP_FROM_EMAIL=no-reply@your-domain.example.com |
| 95 | +SMTP_FROM_NAME=YourBrand |
| 96 | +SMTP_REPLY_TO=hallo@your-domain.example.com |
| 97 | +``` |
| 98 | + |
| 99 | +Verify the sending domain (`your-domain.example.com`) in the provider's |
| 100 | +console before going live — most ESPs reject `From:` addresses on |
| 101 | +unverified domains, and some put unverified senders into a sandbox mode |
| 102 | +that only delivers to pre-allowlisted recipients. |
| 103 | + |
| 104 | +### B — Mailbox provider's SMTP (Zoho Mail, Fastmail, Gmail Workspace, Mailbox.org, …) |
| 105 | + |
| 106 | +Workable for low volume. The `From:` must usually match the authenticated |
| 107 | +mailbox or a pre-configured alias. |
| 108 | + |
| 109 | +```env |
| 110 | +SMTP_HOST=smtp.<provider>.com |
| 111 | +SMTP_PORT=587 |
| 112 | +SMTP_USERNAME=no-reply@your-domain.example.com |
| 113 | +SMTP_PASSWORD=<app-password> # NOT the account login password |
| 114 | +SMTP_FROM_EMAIL=no-reply@your-domain.example.com |
| 115 | +SMTP_FROM_NAME=YourBrand |
| 116 | +SMTP_REPLY_TO=hallo@your-domain.example.com |
| 117 | +``` |
| 118 | + |
| 119 | +Mailbox providers typically require an **app-specific password** rather than |
| 120 | +the account login — generate it in the provider's security settings. They |
| 121 | +also enforce per-mailbox sending caps; a high-volume deployment will outrun |
| 122 | +them. |
| 123 | + |
| 124 | +### C — Self-hosted relay (Postfix, Exim) on the same host |
| 125 | + |
| 126 | +Possible but rarely worth it: residential-grade IP reputation makes |
| 127 | +deliverability fragile, and you're now operating the SMTP stack on top of |
| 128 | +the application stack. |
| 129 | + |
| 130 | +```env |
| 131 | +SMTP_HOST=127.0.0.1 |
| 132 | +SMTP_PORT=25 # internal-only; not exposed externally |
| 133 | +SMTP_USERNAME= # often empty for localhost relay |
| 134 | +SMTP_PASSWORD= # often empty for localhost relay |
| 135 | +SMTP_FROM_EMAIL=no-reply@your-domain.example.com |
| 136 | +SMTP_FROM_NAME=YourBrand |
| 137 | +``` |
| 138 | + |
| 139 | +Configure the local relay to forward via a reputable relay-host (your |
| 140 | +ISP's smarthost, or a transactional ESP) so messages don't leave from a |
| 141 | +residential IP block. Tighten port 25 to localhost-only at the OS firewall. |
| 142 | + |
| 143 | +--- |
| 144 | + |
| 145 | +## Verifying the configuration |
| 146 | + |
| 147 | +After setting the envs and restarting the container: |
| 148 | + |
| 149 | +```bash |
| 150 | +# Trigger a real password-reset email to a test account. |
| 151 | +curl -X POST https://your-domain.example.com/api/v1/auth/forgot-password \ |
| 152 | + -H "Content-Type: application/json" \ |
| 153 | + -d '{"email": "your-real-inbox@example.com"}' |
| 154 | +# Expected: 200 OK |
| 155 | +# Expected: an email arrives at the inbox within seconds. |
| 156 | +``` |
| 157 | + |
| 158 | +If no mail arrives, check: |
| 159 | + |
| 160 | +1. **Application log** — the sender logs `send_email ok` (success) or |
| 161 | + `send_email failed` (failure). The latter is logged at exception level |
| 162 | + with full SMTP error details visible only in the server log; the HTTP |
| 163 | + response stays generic so the SMTP details never leak to the client. |
| 164 | +2. **DNS / SPF / DKIM / DMARC** — for ESPs and mailbox providers, the |
| 165 | + sending domain must have valid SPF and DKIM records pointing at the |
| 166 | + provider, plus a DMARC policy. Without alignment, Gmail and Outlook |
| 167 | + silently drop or junk-folder the messages. |
| 168 | +3. **Provider sandbox mode** — most ESPs ship new accounts in a sandbox |
| 169 | + that only delivers to pre-allowlisted recipients until the domain is |
| 170 | + verified. |
| 171 | +4. **Outbound port** — confirm your hosting provider is not blocking |
| 172 | + the chosen port. Use `nc -vz <smtp-host> <port>` from inside the |
| 173 | + container to verify reachability. |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## Privacy / DSGVO considerations |
| 178 | + |
| 179 | +Outbound SMTP introduces a **sub-processor**: the provider that relays the |
| 180 | +mail can read the recipient address, the message body, and the IP of the |
| 181 | +sending host. List the provider in your own |
| 182 | +[`docs/sub-processors.md`](sub-processors.md)-equivalent disclosure and |
| 183 | +update your privacy policy accordingly. |
| 184 | + |
| 185 | +The application sends **only** transactional content (auth flows + Stripe |
| 186 | +receipts). It does not collect a marketing-consent flag, does not run |
| 187 | +newsletter campaigns, and exposes no operator-side mailing surface. If you |
| 188 | +add either, that's a new processing purpose under Art. 6 GDPR and needs its |
| 189 | +own legal basis and disclosure. |
| 190 | + |
| 191 | +The token TTLs documented above (`30 min` for password reset, `7 d` for |
| 192 | +email verification) are conservative; operators with stricter security |
| 193 | +policies can lower them by editing |
| 194 | +[`app/core/tokens.py`](https://github.com/MrChengLen/FileMorph/blob/main/app/core/tokens.py). |
| 195 | + |
| 196 | +--- |
| 197 | + |
| 198 | +## See also |
| 199 | + |
| 200 | +- [`docs/installation.md`](installation.md) — env-var overview during initial setup |
| 201 | +- [`docs/self-hosting.md`](self-hosting.md) — production deployment & reverse-proxy |
| 202 | +- [`docs/api-reference.md`](api-reference.md) — auth endpoint contract incl. token TTLs |
| 203 | +- [`docs/sub-processors.md`](sub-processors.md) — what to disclose if you accept users |
| 204 | +- [`.env.example`](https://github.com/MrChengLen/FileMorph/blob/main/.env.example) — every supported variable with a one-line description |
0 commit comments