#19: fix F1-F3 defects found auditing the phases against PLAN.md

Every fix here is a case where the code and the plan disagreed, and the
code was wrong.

Certificates (section 8.2, 8.4). The parser documented that it skipped
expired entries but never checked, so an expired certificate would be
served until every handshake failed. LastReload() only advanced when
acme.json changed - and it does not change for sixty days - which made
the timestamp risk R2 monitors indistinguishable from a watcher gone
blind. Expiry is now checked on every pass, LastReload records the check
and LastChange the swap, and the <7d/<24h alerts of section 8.4 exist.

ARC (section 5). buildArcSealer resolved the DKIM key at startup, before
runDnsReconciler had provisioned one. On a first boot ARC was disabled
until someone restarted the process, which the log even admitted while
offering no way for it to start. A LazySealer binds on first use instead.

Health (section 8.4). There was no degraded state at all. /health now
reports one - still 200, because restarting cannot fix a certificate
Traefik never issued.

Bounces. DSNs were sent From postmaster@lambdamail.local, a domain that
does not resolve, so the notification was itself undeliverable. They now
carry the configured mail host, a Message-ID, and Auto-Submitted so a
vacation responder cannot loop against them.

Fan-out atomicity (section 6.1). Persist ran one transaction per
recipient, so a failure halfway through an alias fan-out left earlier
recipients with a copy while the sender was told to retry - and the retry
delivered to them twice. PersistAll commits the whole delivery or none of
it. Verified against Postgres by reverting the repository to the old
behaviour and confirming the new test fails on it.

SMTP replies. The use case encoded replies in error text ("550 5.7.1 ...")
which the adapter re-parsed by prefix, so rewording a message could turn a
permanent refusal into a retry. SmtpRejection carries the decision and the
adapter renders it.

Repository tests. These only run with TEST_DATABASE_URL, which CI does
set - so the job was red: copy and expunge seeded a 68-character digest
into a CHAR(64) column, and three others used fixed digests that collide
with the UNIQUE index on any re-run. A shared testBlobDigest() helper
fixes both; the suite now passes twice in a row against one database.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Opus 5 committed 2026-08-02 07:57:22 -03:00
1 parent 2e0e65b167
commit 33d91fa247
23 files changed
+906 -139

No files matched your search

@@ -13,14 +13,32 @@ const (
DsnActionDelayed DsnAction = "delayed"
)
// BuildDsnReport constructs an RFC 3464 compliant delivery status notification report payload.
// Returns (payload, isLoopPrevented). If envelopeFrom is empty or "<>", isLoopPrevented is true and payload is nil.
func BuildDsnReport(action DsnAction, envelopeFrom string, envelopeTo string, messageID string, reason string) ([]byte, bool) {
// defaultReportingMTA is the last resort when no mail host was configured. It
// is deliberately obviously wrong rather than plausible, so a misconfiguration
// shows up in the report instead of hiding in it.
const defaultReportingMTA = "mail.lambdamail.local"
// BuildDsnReport constructs an RFC 3464 compliant delivery status notification
// report payload.
//
// reportingMTA is this server's own mail host. It has to be the real one: a
// notification whose From and Reporting-MTA name a domain that does not
// resolve is itself undeliverable at the next hop, which loses exactly the
// message the sender most needs to see.
//
// Returns (payload, isLoopPrevented). If envelopeFrom is empty or "<>",
// isLoopPrevented is true and payload is nil.
func BuildDsnReport(action DsnAction, reportingMTA string, envelopeFrom string, envelopeTo string, messageID string, reason string) ([]byte, bool) {
cleanFrom := strings.Trim(strings.TrimSpace(envelopeFrom), "<>")
if cleanFrom == "" {
return nil, true // Loop prevention per RFC 5321 section 4.5.5
}
reportingMTA = strings.TrimSpace(reportingMTA)
if reportingMTA == "" {
reportingMTA = defaultReportingMTA
}
boundary := fmt.Sprintf("=_Boundary_DSN_%d", time.Now().UnixNano())
nowStr := time.Now().Format(time.RFC1123Z)
@@ -32,10 +50,15 @@ func BuildDsnReport(action DsnAction, envelopeFrom string, envelopeTo string, me
}
var sb strings.Builder
sb.WriteString(fmt.Sprintf("From: Mail Delivery Subsystem <postmaster@lambdamail.local>\r\n"))
sb.WriteString(fmt.Sprintf("From: Mail Delivery Subsystem <postmaster@%s>\r\n", reportingMTA))
sb.WriteString(fmt.Sprintf("To: <%s>\r\n", cleanFrom))
sb.WriteString(fmt.Sprintf("Subject: %s\r\n", subject))
sb.WriteString(fmt.Sprintf("Date: %s\r\n", nowStr))
sb.WriteString(fmt.Sprintf("Message-ID: <dsn-%s@%s>\r\n", messageID, reportingMTA))
// RFC 3834 section 5: an automatic notification has to say so, or a
// vacation responder on the other side answers it and the two bounce back
// and forth forever.
sb.WriteString("Auto-Submitted: auto-replied\r\n")
sb.WriteString(fmt.Sprintf("Content-Type: multipart/report; report-type=delivery-status; boundary=\"%s\"\r\n\r\n", boundary))
// Part 1: Human-readable narrative
@@ -50,7 +73,7 @@ func BuildDsnReport(action DsnAction, envelopeFrom string, envelopeTo string, me
// Part 2: Machine-readable delivery-status
sb.WriteString(fmt.Sprintf("--%s\r\n", boundary))
sb.WriteString("Content-Type: message/delivery-status\r\n\r\n")
sb.WriteString("Reporting-MTA: dns; mail.lambdamail.local\r\n")
sb.WriteString(fmt.Sprintf("Reporting-MTA: dns; %s\r\n", reportingMTA))
sb.WriteString(fmt.Sprintf("Arrival-Date: %s\r\n\r\n", nowStr))
sb.WriteString(fmt.Sprintf("Final-Recipient: rfc822; %s\r\n", envelopeTo))
@@ -6,19 +6,19 @@ import (
)
func TestBuildDsnReport_PreventsBounceLoopForEmptySender(t *testing.T) {
_, isLoop := BuildDsnReport(DsnActionFailed, "", "user@external.test", "msg123", "550 User unknown")
_, isLoop := BuildDsnReport(DsnActionFailed, "mail.local.test", "", "user@external.test", "msg123", "550 User unknown")
if !isLoop {
t.Errorf("expected loop prevention for empty sender")
}
_, isLoop = BuildDsnReport(DsnActionFailed, "<>", "user@external.test", "msg123", "550 User unknown")
_, isLoop = BuildDsnReport(DsnActionFailed, "mail.local.test", "<>", "user@external.test", "msg123", "550 User unknown")
if !isLoop {
t.Errorf("expected loop prevention for <> sender")
}
}
func TestBuildDsnReport_GeneratesValidMIME(t *testing.T) {
payload, isLoop := BuildDsnReport(DsnActionFailed, "sender@local.test", "recipient@remote.test", "msg123", "550 User unknown")
payload, isLoop := BuildDsnReport(DsnActionFailed, "mail.local.test", "sender@local.test", "recipient@remote.test", "msg123", "550 User unknown")
if isLoop || payload == nil {
t.Fatalf("unexpected loop signal for valid sender")
}
@@ -34,3 +34,31 @@ func TestBuildDsnReport_GeneratesValidMIME(t *testing.T) {
t.Errorf("missing Diagnostic-Code: %s", raw)
}
}
// A notification whose From and Reporting-MTA name a domain that does not
// resolve is itself undeliverable, which loses the very message the sender
// most needs to see.
func TestBuildDsnReport_UsesTheConfiguredMailHost(t *testing.T) {
payload, _ := BuildDsnReport(DsnActionFailed, "mail.example.test", "sender@example.test", "recipient@remote.test", "job-1", "550 User unknown")
raw := string(payload)
if !strings.Contains(raw, "From: Mail Delivery Subsystem <postmaster@mail.example.test>") {
t.Errorf("From does not use the configured mail host: %s", raw)
}
if !strings.Contains(raw, "Reporting-MTA: dns; mail.example.test") {
t.Errorf("Reporting-MTA does not use the configured mail host: %s", raw)
}
if strings.Contains(raw, "lambdamail.local") {
t.Errorf("the placeholder host leaked into the report: %s", raw)
}
}
// RFC 3834 section 5: without Auto-Submitted a vacation responder answers the
// bounce and the two loop against each other.
func TestBuildDsnReport_MarksItselfAutomatic(t *testing.T) {
payload, _ := BuildDsnReport(DsnActionDelayed, "mail.example.test", "sender@example.test", "recipient@remote.test", "job-1", "451 deferred")
if !strings.Contains(string(payload), "Auto-Submitted: auto-replied") {
t.Errorf("the report is not marked as automatic: %s", payload)
}
}