docs(admin): email is SMTP now, and the Gmail connect flow is gone #21

Merged
whitlocktech merged 1 commits from docs/engagement-phase-1-smtp into edge 2026-08-29 02:10:16 +00:00
4 changed files with 74 additions and 20 deletions

View File

@@ -59,9 +59,10 @@ per-Team settings:
## Email ## Email
Configured on the same screen and covered in Configured on the same screen and covered in
[Notifications and email](/docs/administration/notifications-and-email/): it is Gmail over [Notifications and email](/docs/administration/notifications-and-email/): pick a mail
OAuth2, it reuses the Google authentication client, and it must be set up on the transport, enter its host, port and credentials, and send a test. It depends on nothing
[Authentication](/docs/administration/authentication/) page first. else on the site — a relay is the recommended posture, a mailbox provider over SMTP the
simplest, and your own MTA needs no credentials at all.
<Aside type="note" title="Until email is connected, the contact form is a mailto: link"> <Aside type="note" title="Until email is connected, the contact form is a mailto: link">
That is a deliberate fallback rather than a failure — but it does mean the *Contact email* That is a deliberate fallback rather than a failure — but it does mean the *Contact email*

View File

@@ -1,6 +1,6 @@
--- ---
title: Notifications and email title: Notifications and email
description: Email over Gmail OAuth2, the announcement pipeline and its legs, the Discord bot, and opt-in push to the mobile app. description: Email over SMTP, the announcement pipeline and its legs, the Discord bot, and opt-in push to the mobile app.
--- ---
import { Aside } from '@astrojs/starlight/components'; import { Aside } from '@astrojs/starlight/components';
@@ -11,19 +11,62 @@ it.
## Email ## Email
**Admin → Settings → Email delivery.** The site sends contact-form messages (and test **Admin → Settings → Email delivery.** The site sends contact-form messages, invitations,
messages) through **Gmail over OAuth2**, delivered to the *Contact email* setting. password resets, team notifications and test messages through **SMTP**. Contact-form mail
goes to the *Contact email* setting.
It reuses the **Google authentication client**, so the order is fixed: configure Google on You pick a mail transport and fill in the fields it asks for. There is no consent flow and
the [Authentication](/docs/administration/authentication/) page first, then press **Connect no redirect to bounce through — it is a form, and the credentials go straight into the
Gmail** here. Until then the panel reads *Unconfigured* and says exactly that. database encrypted at rest, write-only: the panel will tell you a password is *set*, and
will never show it to you again.
The refresh token it stores is encrypted at rest like every other secret. ### Three ways to point it somewhere
<Aside type="note" title="There is no SMTP option"> Any SMTP server works. Which one you should use depends on how much mail you expect to send.
Gmail over OAuth2 is the only supported delivery path today. Until it is connected, the
contact form falls back to a `mailto:` link to the contact address — which works, and puts **A relay — the recommended one.** Mailgun, SES, Postmark or equivalent: their host, port
the message in the visitor's own mail client rather than in your logs. `587`, *Implicit TLS* **off**, and your API key as the password. Deliverability is the hard
part of sending mail — reputation, DKIM, bounce handling — and this is the option where
somebody else owns it. Use this for anything with real volume.
**A mailbox provider over SMTP — the simplest.** For example `smtp.gmail.com`, port `587`,
*Implicit TLS* **off**, your address as the username, and an
[app password](https://support.google.com/accounts/answer/185833) — not your account
password, and it requires 2-Step Verification to be on. Fine for a small site; subject to
the provider's daily send caps.
**Your own MTA.** If you already run mail on the same host: its address, port `25`,
*Implicit TLS* **off**, username and password blank. The site treats a username with no
password as incomplete, since that authenticates as nobody.
<Aside type="caution" title="The two fields that cause most failures">
**Implicit TLS** belongs *on* only for port **465**. On port `587` leave it **off** — the
connection still upgrades to TLS, using STARTTLS. Port 587 with it on does not report an
error; it hangs.
**Send from** must be an address the account is allowed to send as. Unlike a username, this
is not verified when you save it — a server that refuses your sender rejects the mail for
SPF/DMARC reasons that look like nothing at all from the outside. **Send test** is what
proves it, and it names this specifically when it happens.
</Aside>
Until a transport is configured, the contact form falls back to a `mailto:` link to the
contact address — which works, and puts the message in the visitor's own mail client rather
than in your logs. Invitations surface a copyable accept link instead, and password resets
still answer normally.
<Aside type="note" title="Upgrading from the Gmail connect flow">
Earlier versions authorised a mailbox with a **Connect Gmail** consent flow that borrowed
the Google authentication client. That flow has been removed.
If your site used it, mail **stops** on upgrade until you enter SMTP credentials — and
nothing errors when it does, because every sender degrades politely. The admin dashboard
warns you while it is true. `smtp.gmail.com` port 587 with an app password is the shortest
route back.
Single sign-on is unaffected: the Google provider exists for SSO in its own right, and email
merely borrowed its credentials. Removing the borrow also removes a trap — rotating the SSO
secret used to break outbound mail silently.
</Aside> </Aside>
## Announcements ## Announcements

View File

@@ -97,9 +97,18 @@ a Team. See [Teams](/docs/administration/teams/).
## Email and announcements never arrive ## Email and announcements never arrive
- **The contact form opens a mail client.** Email delivery is not connected; that is the - **The contact form opens a mail client.** Email delivery is not configured; that is the
documented fallback. Connect Gmail in **Settings → Email delivery** — after configuring documented fallback. Enter SMTP credentials in **Settings → Email delivery**. If this site
the Google provider, which it reuses. used to send mail and stopped, the Gmail connect flow was removed — the admin dashboard
says so, and [Notifications and email](/docs/administration/notifications-and-email/) has
the migration.
- **Mail is configured but nothing arrives, and there is no error.** Two usual causes, both
invisible without a test send. *Implicit TLS* left on for port 587 hangs rather than
failing; and a **Send from** address the server will not let you send as is rejected for
SPF/DMARC reasons. Press **Send test** — its failure message names both cases.
- **Email was working and the toggle is still on.** *Enable email sending* now gates every
message, not just some of them. If it is off, nothing is sent, including the contact
form.
- **A published post announced nothing.** The Discord bot is a separate container. If the - **A published post announced nothing.** The Discord bot is a separate container. If the
Discord Bot screen says *bot unreachable*, it is not running. Discord Bot screen says *bot unreachable*, it is not running.
- **A missed announcement does not come back.** Nothing retries; the post itself is still - **A missed announcement does not come back.** Nothing retries; the post itself is still

View File

@@ -114,9 +114,10 @@ lifecycle](/docs/modules/module-lifecycle/#failure-is-contained-by-construction)
### Secrets are encrypted at rest ### Secrets are encrypted at rest
OAuth client secrets, the sidecar token and the Gmail refresh token are AES-256-GCM OAuth client secrets, the sidecar token, the Discord bot token and the mail transport's
encrypted, keyed by `SECRET_ENC_KEY`. **The sidecar token is write-only in the API** — it is credentials are AES-256-GCM encrypted, keyed by `SECRET_ENC_KEY`. **The sidecar token and the
never returned to any client. mail credentials are write-only in the API** — neither is ever returned to any client; the
email panel reports only that a password is *set*.
<Aside type="caution" title="Rotating that key orphans every stored secret"> <Aside type="caution" title="Rotating that key orphans every stored secret">
Nothing re-encrypts. What was stored under the old key can no longer be read, and every Nothing re-encrypts. What was stored under the old key can no longer be read, and every