docs(admin): email is SMTP now, and the Gmail connect flow is gone
Engagement Phase 1's share of this repo (docs/website/ENGAGEMENT.md §6.0b). Four pages described a delivery path that no longer exists — one of them under the heading "There is no SMTP option", which is now the opposite of true. notifications-and-email.mdx: the Email section is rewritten around the three postures the org lead settled on (§7.1 Q5), leading with a relay and naming smtp.gmail.com:587 with an app password as the migration off OAuth2. Two cautions carry the failures that produce no error at all — Implicit TLS left on for port 587, which hangs, and an operator-typed sender the relay will not accept, which is an SPF/DMARC rejection that looks like nothing. Send test is what proves both. troubleshooting.mdx gains those two, plus the enabled toggle, which now gates every message rather than some of them. configuration.mdx loses the "set up Google first" ordering constraint, which is gone with the borrowed client. system-architecture.mdx's encrypted-at-rest list is corrected: the Gmail refresh token is replaced by the transport credentials, which are write-only like the sidecar token. This lands on `edge`, so nothing here is published while `main` still carries the Gmail flow. platform.json and capabilities.mjs are untouched — they belong to Phase 12. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -59,9 +59,10 @@ per-Team settings:
|
||||
## Email
|
||||
|
||||
Configured on the same screen and covered in
|
||||
[Notifications and email](/docs/administration/notifications-and-email/): it is Gmail over
|
||||
OAuth2, it reuses the Google authentication client, and it must be set up on the
|
||||
[Authentication](/docs/administration/authentication/) page first.
|
||||
[Notifications and email](/docs/administration/notifications-and-email/): pick a mail
|
||||
transport, enter its host, port and credentials, and send a test. It depends on nothing
|
||||
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">
|
||||
That is a deliberate fallback rather than a failure — but it does mean the *Contact email*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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';
|
||||
@@ -11,19 +11,62 @@ it.
|
||||
|
||||
## Email
|
||||
|
||||
**Admin → Settings → Email delivery.** The site sends contact-form messages (and test
|
||||
messages) through **Gmail over OAuth2**, delivered to the *Contact email* setting.
|
||||
**Admin → Settings → Email delivery.** The site sends contact-form messages, invitations,
|
||||
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
|
||||
the [Authentication](/docs/administration/authentication/) page first, then press **Connect
|
||||
Gmail** here. Until then the panel reads *Unconfigured* and says exactly that.
|
||||
You pick a mail transport and fill in the fields it asks for. There is no consent flow and
|
||||
no redirect to bounce through — it is a form, and the credentials go straight into the
|
||||
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">
|
||||
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
|
||||
the message in the visitor's own mail client rather than in your logs.
|
||||
Any SMTP server works. Which one you should use depends on how much mail you expect to send.
|
||||
|
||||
**A relay — the recommended one.** Mailgun, SES, Postmark or equivalent: their host, port
|
||||
`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>
|
||||
|
||||
## Announcements
|
||||
|
||||
@@ -97,9 +97,18 @@ a Team. See [Teams](/docs/administration/teams/).
|
||||
|
||||
## Email and announcements never arrive
|
||||
|
||||
- **The contact form opens a mail client.** Email delivery is not connected; that is the
|
||||
documented fallback. Connect Gmail in **Settings → Email delivery** — after configuring
|
||||
the Google provider, which it reuses.
|
||||
- **The contact form opens a mail client.** Email delivery is not configured; that is the
|
||||
documented fallback. Enter SMTP credentials in **Settings → Email delivery**. If this site
|
||||
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
|
||||
Discord Bot screen says *bot unreachable*, it is not running.
|
||||
- **A missed announcement does not come back.** Nothing retries; the post itself is still
|
||||
|
||||
@@ -114,9 +114,10 @@ lifecycle](/docs/modules/module-lifecycle/#failure-is-contained-by-construction)
|
||||
|
||||
### Secrets are encrypted at rest
|
||||
|
||||
OAuth client secrets, the sidecar token and the Gmail refresh token are AES-256-GCM
|
||||
encrypted, keyed by `SECRET_ENC_KEY`. **The sidecar token is write-only in the API** — it is
|
||||
never returned to any client.
|
||||
OAuth client secrets, the sidecar token, the Discord bot token and the mail transport's
|
||||
credentials are AES-256-GCM encrypted, keyed by `SECRET_ENC_KEY`. **The sidecar token and the
|
||||
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">
|
||||
Nothing re-encrypts. What was stored under the old key can no longer be read, and every
|
||||
|
||||
Reference in New Issue
Block a user