README: mail is content - mail: on a state, mail: in site.yaml, gdo
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 0s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4jrCgLiKKHAEFuZUJjckH
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-23 19:50:22 +02:00
co-authored by Claude Fable 5.1
parent 58225386b0
commit 9917b07312
+31
View File
@@ -289,6 +289,37 @@ it>` naming the automation that does. A bucket nothing reads fails
lint, so no publicly collected answer can land where no one will ever lint, so no publicly collected answer can land where no one will ever
see it. see it.
**Mail is content** (portal 0.3.43, delivered by
[gdo](https://project.uhhm.no/uhhm/gdo)). A state in `aggregates.yaml`
may carry the mail its arrival sends:
```yaml
states:
open: { event: received, mail: { to: submitter, subject: "Got it, {name}", body: "We read every one. Withdraw it here: {site}/decide/brief?chain={chain}&email={email}" } }
fixed: { event: fixed, mail: { to: board@example.no, subject: "Fixed: {where}", body: "..." } }
```
`to` is `submitter` (the record's `email` field; lint then requires
every form recording into that bucket to collect a required `email`)
or one fixed address. Subject and body are templates over the
record's fields, plus `{site}` (the instance's public URL), `{chain}`
(the record id), `{email}`, `{bucket}`, `{state}`; the body is
markdown, sent as plain text with an HTML alternative. An invite
(`invite:` on an alternative) mails its one-time link built in, in the
site's `lang`. None of it is sent unless `site.yaml` says who it comes
from:
```yaml
mail:
from: UHHM <portal@uhhm.no>
reply_to: bl@uhhm.no # optional
invite: { subject: "...", body: "..." } # optional rewording; {name} {username} {email} {group} {link} {site} {site_name} {expires}
```
Portal renders each mail the moment the record enters the state and
publishes it on `portal.mail.send` in the `WORMHOLE` JetStream stream;
gdo, one per host, hands it to the host's SMTP. A transition that
fires twice sends once.
## Routing: the tree is the router ## Routing: the tree is the router
The `questions/` directory tree is the URL tree — `index.yaml` names The `questions/` directory tree is the URL tree — `index.yaml` names