Matches portal 0.3.44. The unit runs /usr/bin/gdo. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3.3 KiB
gdo
The transmitter that sends the code through the gate so the iris opens. iris keeps bad content out of a portal site; gdo sends the mail that lets a known person in, and every other mail a site's content says to send.
Portal decides when a person hears something and renders the mail:
mail: on a state in aggregates.yaml sends when a record enters
that state, and an invite mails its one-time link built in. It
publishes each rendered mail on portal.mail.send in the mail
JetStream stream. gdo holds a durable consumer there and hands each
message to the host's SMTP. Portal never talks SMTP; gdo never reads
content; a message survives either of them being down, because the
stream keeps it.
Run
gdo # the consumer; one per host
gdo probe kari@example.no # one message through, reports delivery
gdo --version
| Env | Default | |
|---|---|---|
NATS_URL |
nats://127.0.0.1:4222 |
credentials in the URL, as portal's own |
SMTP_HOST, SMTP_PORT |
127.0.0.1, 25 |
the host's postfix; no TLS, no auth, localhost only |
SMTP_HELO |
the host's full name | what gdo says in EHLO; a strict server refuses a bare hostname |
GDO_CONSUMER |
gdo |
the durable consumer's name |
GDO_TYPST_THEME |
unset | a .typ file; set, every mail carries a PDF rendered from it |
Install on a host from the [uhhm] Arch registry on project.uhhm.no (the repo and its key: see uhhm/corp's README), as root:
pacman -Sy gdo
<edit /etc/gdo/env: NATS_URL with the host's credentials>
systemctl enable --now gdo
The package puts the binary in /usr/bin, the env template in
/etc/gdo/env (mode 0600, the one file with a secret), and the unit
gdo.service. Then gdo probe <you>, with the env loaded.
The message
One JSON object per message, v: 1:
{ "v": 1, "id": "trafikkmeldinger:<record>:ordnet", "site": "Tomter Vel",
"from": "Tomter Vel <vel@example.no>", "reply_to": "post@example.no",
"to": "kari@example.no", "subject": "Ordnet: Svingen ved skolen",
"text": "…markdown as plain text…", "html": "<!doctype html>…" }
id is the JetStream message id: a transition that fires twice sends
once. Sent as multipart/alternative, text first, so the plain text
is what every client can read and the HTML is the courtesy. A message
gdo cannot parse is acked and logged; one with a v it does not know
is left in the stream for a gdo that does; one SMTP refuses is retried
after a minute, for as long as the stream keeps it (a week).
Typst themes
Plain text is the default and needs nothing. With GDO_TYPST_THEME
set to a .typ file and typst on the host, gdo runs
typst compile with --input site=… subject=… text=… to=… and
attaches the PDF as <site>.pdf. Typst's PDF is dependable; its HTML
export is not yet mail-grade, so the HTML alternative stays the plain
one and the theme is the attachment. A theme that fails to render
costs the attachment, never the mail.
A minimal theme:
#let site = sys.inputs.at("site", default: "")
#set page(width: 148mm, height: auto, margin: 18mm)
#set text(font: "Libertinus Serif", size: 11pt)
#text(size: 9pt, fill: gray)[#site]
#v(6mm)
#heading(sys.inputs.at("subject", default: ""))
#v(3mm)
#sys.inputs.at("text", default: "")