From 08ff470a2b6c20e862729588d3f45db3e13cb4fd Mon Sep 17 00:00:00 2001 From: Mark Mennell Date: Mon, 10 Aug 2026 09:36:14 +1000 Subject: [PATCH] send: don't block replies on third-party parent deliveries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A received parent's recipient rows for other domains carry fmsgd's local sentinel response codes (-2 recorded from an exchange — another host's delivery; -1 no response, retryable). The reply pre-flight treated any recorded code as a failed delivery, so replying-all to a received multi-domain message was always refused with 409 even though the originating host had delivered the parent everywhere. Summarise the sentinels distinctly: -2 marks the domain as another host's delivery (allowed — the wire's parent-not-found rejection stays the arbiter) and -1 as pending. Only genuinely recorded delivery failures by this host still block. Co-Authored-By: Claude Fable 5 --- README.md | 14 +++++++++----- internal/handlers/messages.go | 22 ++++++++++++++++++++-- internal/handlers/messages_test.go | 4 ++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d361d67..c2d5a00 100644 --- a/README.md +++ b/README.md @@ -613,11 +613,15 @@ For a reply (a draft with `pid`), the route first verifies that every remote recipient domain can actually accept it: per the fmsg spec a host rejects a reply whose parent it has not stored (response code 6), so if the parent was never addressed to a recipient's domain — or every delivery attempt of the -parent to that domain concluded in rejection — the send is refused with `409` -naming the domain(s) and the remedy (add the recipients to the parent via -add-to, or start a new thread). Domains where the parent's delivery is still -in flight are allowed; the reply's parent's own originating domain always -passes (it retains its outgoing messages). Local recipients are unaffected. +parent to that domain by this host concluded in rejection — the send is +refused with `409` naming the domain(s) and the remedy (add the recipients to +the parent via add-to, or start a new thread). Domains where the parent's +delivery is still in flight are allowed, as are domains whose delivery was +another host's responsibility (a received parent): this host cannot know a +third-party delivery's outcome, so the reply is attempted and the wire's +"parent not found" rejection remains the arbiter. The reply's parent's own +originating domain always passes (it retains its outgoing messages). Local +recipients are unaffected. **Response:** `200 OK` with `{"id": , "time": }`. diff --git a/internal/handlers/messages.go b/internal/handlers/messages.go index aca07df..f32e789 100644 --- a/internal/handlers/messages.go +++ b/internal/handlers/messages.go @@ -139,9 +139,19 @@ func (h *MessageHandler) resolveLocalDelivery(ctx context.Context, table string, // parentDomainDelivery summarizes the parent message's recorded delivery for // one recipient domain. +// Local sentinels fmsgd records in response_code (see fmsgd dd.sql): +// -1 marks a delivery attempt that got no response (retryable), -2 marks a +// recipient recorded from a received exchange — that delivery was another +// host's responsibility and this host has no knowledge of its outcome. +const ( + localResponseCodeNoResponse = -1 + localResponseCodeNotOurDelivery = -2 +) + type parentDomainDelivery struct { delivered bool // at least one recipient there was delivered (parent is stored) pending bool // at least one recipient there has no outcome recorded yet + otherHost bool // delivery there was another host's responsibility (parent was received) codes []int // failure response codes recorded for the domain } @@ -164,8 +174,12 @@ func undeliverableReplyDomains(replyDomains []string, parentFromDomain string, b switch { case !ok: blocked = append(blocked, fmt.Sprintf("%s: the message being replied to was never addressed to this domain", d)) - case s.delivered || s.pending: - // stored there, or still in flight + case s.delivered || s.pending || s.otherHost: + // Stored there, still in flight, or another host's delivery + // (a received parent): this host cannot know a third-party + // delivery's outcome, so the reply is attempted — the wire's + // "parent not found" rejection (code 6) remains the arbiter + // in the rare case the originating host in fact failed. default: blocked = append(blocked, fmt.Sprintf("%s: delivery of the message being replied to failed there (response code(s) %v)", d, s.codes)) } @@ -229,6 +243,10 @@ func (h *MessageHandler) parentDeliveryByDomain(ctx context.Context, parentID in switch { case delivered: s.delivered = true + case code != nil && *code == localResponseCodeNotOurDelivery: + s.otherHost = true + case code != nil && *code == localResponseCodeNoResponse: + s.pending = true // retryable: the sender will attempt again case code != nil: s.codes = append(s.codes, *code) default: diff --git a/internal/handlers/messages_test.go b/internal/handlers/messages_test.go index da7afcb..a7ac464 100644 --- a/internal/handlers/messages_test.go +++ b/internal/handlers/messages_test.go @@ -291,6 +291,8 @@ func TestUndeliverableReplyDomains(t *testing.T) { "failed.example": {codes: []int{6}}, "mixed.example": {delivered: true, codes: []int{100}}, "partfail.example": {pending: true, codes: []int{6}}, + "otherhost.example": {otherHost: true}, + "otherfail.example": {otherHost: true, codes: []int{4}}, } cases := []struct { name string @@ -302,6 +304,8 @@ func TestUndeliverableReplyDomains(t *testing.T) { {"in-flight parent passes", []string{"pending.example"}, "origin.example", 0}, {"partially failed but still pending passes", []string{"partfail.example"}, "origin.example", 0}, {"delivered outweighs a failed sibling", []string{"mixed.example"}, "origin.example", 0}, + {"another host's delivery passes", []string{"otherhost.example"}, "origin.example", 0}, + {"another host's delivery outweighs a failed sibling", []string{"otherfail.example"}, "origin.example", 0}, {"originating domain always passes", []string{"origin.example"}, "origin.example", 0}, {"originating domain passes case-insensitively", []string{"Origin.Example"}, "origin.example", 0}, {"never-addressed domain blocked", []string{"stranger.example"}, "origin.example", 1},