fix(email): send portal mail from mail.gitborg.se #114

Sammanfogat
supernaut sammanfogade 1 incheckning från fix/mail-domain-sender in i main 2026-07-31 00:59:59 +00:00
Ägare

The sending domain moved from email.gitborg.se to mail.gitborg.se. Only bitborg-infra was
updated; the portal's From address is hardcoded here and not env-configurable on purpose, so no
Ansible run could reach it. Every portal mail would still have gone out as
no-reply@email.gitborg.se.

Why this matters

The old address is now unauthenticated. Verified against live DNS:

Record Result
sweego1._domainkey.mail.gitborg.se ✅ CNAME → …-dkim.sweego.co, valid v=DKIM1 key
_dmarc.mail.gitborg.se ✅ v=DMARC1; p=none;
email.gitborg.se ❌ no DNS records at all — no DKIM, no TXT

So Sweego is likely to reject the send as an unverified sending domain, and anything that did get
through would fail DKIM alignment. The affected path is the sign-up credential-reset email: the
sign-up completes, the account is provisioned, and the user never receives the link to set a
password. Failure is silent from the user's side.

p=none means nothing is being rejected on policy grounds, which makes this quieter, not safer.

Changes

  • src/lib/email.ts — EMAIL_FROM → no-reply@mail.gitborg.se; comments corrected and the DKIM
    location recorded.
  • README.md, .env.example — same address in prose.

The underlying coupling

The From address lives in two repos with nothing asserting they agree: this constant, and
forgejo_mailer_from in bitborg-infra. The infra template comment was even updated to say "From is
fixed to no-reply@mail.gitborg.se in app code"
while the app code still read email.gitborg.se — a
comment asserting a fact about another repo, unchecked by anything.

Tracked in #113, which proposes boot-time validation against an infra-supplied allowlist: it keeps
the security property that motivated hardcoding, needs no cross-repo CI, and turns a silent delivery
failure into a loud startup failure.

Verification

  • pnpm check — 0 errors (113 files). The three warnings are pre-existing: a deprecated
    astro:schema z import and two unused getRelativeLocaleUrl imports in the signup pages.
  • Pre-push e2e suite passed.
  • Not yet verified end-to-end. This needs a deploy plus the matching infra apply (which carries
    the reissued SMTP credentials) before mail can be tested. After both land: trigger a sign-up and
    confirm the received headers show d=mail.gitborg.se with dkim=pass.

Merge order

Independent of the infra PR — but the portal keeps sending from a dead domain until this one
deploys, so it should not wait on it.

The sending domain moved from `email.gitborg.se` to `mail.gitborg.se`. Only bitborg-infra was updated; the portal's From address is hardcoded here and **not env-configurable on purpose**, so no Ansible run could reach it. Every portal mail would still have gone out as `no-reply@email.gitborg.se`. ## Why this matters The old address is now unauthenticated. Verified against live DNS: | Record | Result | | --- | --- | | `sweego1._domainkey.mail.gitborg.se` | ✅ CNAME → `…-dkim.sweego.co`, valid `v=DKIM1` key | | `_dmarc.mail.gitborg.se` | ✅ `v=DMARC1; p=none;` | | `email.gitborg.se` | ❌ **no DNS records at all** — no DKIM, no TXT | So Sweego is likely to reject the send as an unverified sending domain, and anything that did get through would fail DKIM alignment. The affected path is the **sign-up credential-reset email**: the sign-up completes, the account is provisioned, and the user never receives the link to set a password. Failure is silent from the user's side. `p=none` means nothing is being rejected on policy grounds, which makes this quieter, not safer. ## Changes - `src/lib/email.ts` — `EMAIL_FROM` → `no-reply@mail.gitborg.se`; comments corrected and the DKIM location recorded. - `README.md`, `.env.example` — same address in prose. ## The underlying coupling The From address lives in **two repos** with nothing asserting they agree: this constant, and `forgejo_mailer_from` in bitborg-infra. The infra template comment was even updated to say *"From is fixed to no-reply@mail.gitborg.se in app code"* while the app code still read `email.gitborg.se` — a comment asserting a fact about another repo, unchecked by anything. Tracked in #113, which proposes boot-time validation against an infra-supplied allowlist: it keeps the security property that motivated hardcoding, needs no cross-repo CI, and turns a silent delivery failure into a loud startup failure. ## Verification - `pnpm check` — 0 errors (113 files). The three warnings are pre-existing: a deprecated `astro:schema` `z` import and two unused `getRelativeLocaleUrl` imports in the signup pages. - Pre-push e2e suite passed. - **Not yet verified end-to-end.** This needs a deploy plus the matching infra apply (which carries the reissued SMTP credentials) before mail can be tested. After both land: trigger a sign-up and confirm the received headers show `d=mail.gitborg.se` with `dkim=pass`. ## Merge order Independent of the infra PR — but the portal keeps sending from a dead domain until **this** one deploys, so it should not wait on it.
supernaut lade till 1 incheckning 2026-07-31 00:41:31 +00:00
fix(email): send portal mail from mail.gitborg.se
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m15s
76c5ba3f6d
The sending domain moved from email.gitborg.se to mail.gitborg.se, but only
gitborg-infra was updated (forgejo_mailer_from, the monitoring alert senders,
the vault credentials). The portal's From address is hardcoded here and is not
env-configurable, so the Ansible change could not reach it: every portal mail
would still have gone out as no-reply@email.gitborg.se.

That address is now unauthenticated. Only mail.gitborg.se has DKIM
(sweego1._domainkey.mail.gitborg.se -> ...-dkim.sweego.co) and an explicit
_dmarc record; email.gitborg.se has no DNS records left at all. Sweego is
likely to reject the send outright as an unverified sending domain, and
anything that did get through would fail DKIM alignment. The affected path is
the sign-up credential-reset mail, so sign-up completion was at risk.

The comment now also names the cross-repo duplication: the From lives in both
this file and gitborg-infra with nothing asserting they match, which is how the
two came apart. Tracked in #113.
supernaut sammanfogade incheckning 086e208bcc till main 2026-07-31 00:59:59 +00:00
supernaut tog bort grenen fix/mail-domain-sender 2026-07-31 00:59:59 +00:00
supernaut refererade denna ändringsförfrågan från en incheckning 2026-07-31 01:00:00 +00:00
supernaut refererade denna ändringsförfrågan från en incheckning 2026-08-03 09:41:50 +00:00
Logga in för att delta i denna konversation.
Inga granskare
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-web!114
Ingen beskrivning angiven.