docs(site): the engagement system — cutover 7 of 7 (edge → main)
#28
@@ -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*
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user