Skip to content

Store received messages with complete wire recipient lists - #29

Merged
markmnl merged 2 commits into
mainfrom
store-full-inbound-recipients
Aug 3, 2026
Merged

Store received messages with complete wire recipient lists#29
markmnl merged 2 commits into
mainfrom
store-full-inbound-recipients

Conversation

@markmnl

@markmnl markmnl commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Inbound storage truncated a message's recipients to the locally accepted ones, discarding co-recipients on other domains. That broke the participant record (hosts disagreed on a message's recipients), and worse, broke SPEC §10.3 participant validation: a reply from a co-recipient this host never recorded would be rejected as invalid. It also made faithful reconstruction of the message impossible, which SPEC §11 hash computation depends on.

storeMsgDetail now stores the complete wire to/add to lists in wire order. Recipients this host accepted are marked delivered as before; recipients it rejected keep the per-recipient code it responded; and recipients on other domains carry the new local sentinel response code -2 (not our delivery) so the sender's pending queries never treat them as this host's work. attachAddToRecipients and storeMsgHeaderOnly use the same sentinel (previously code 11).

Received messages additionally persist their exact wire header bytes in the new msg.wire_header column so any hash -- including future add-to batch variants -- can always be recomputed faithfully, immune to re-encode drift. Locally-authored messages leave it null.

Existing rows cannot be backfilled; the discarded recipients were lost at receive time. Upgrade path for existing databases:
alter table msg add column if not exists wire_header bytea;
(no other schema change; the sentinel uses the existing response_code column).

markmnl and others added 2 commits August 3, 2026 19:53
Inbound storage truncated a message's recipients to the locally
accepted ones, discarding co-recipients on other domains. That broke
the participant record (hosts disagreed on a message's recipients),
and worse, broke SPEC §10.3 participant validation: a reply from a
co-recipient this host never recorded would be rejected as invalid.
It also made faithful reconstruction of the message impossible,
which SPEC §11 hash computation depends on.

storeMsgDetail now stores the complete wire to/add to lists in wire
order. Recipients this host accepted are marked delivered as before;
recipients it rejected keep the per-recipient code it responded; and
recipients on other domains carry the new local sentinel response
code -2 (not our delivery) so the sender's pending queries never
treat them as this host's work. attachAddToRecipients and
storeMsgHeaderOnly use the same sentinel (previously code 11).

Received messages additionally persist their exact wire header bytes
in the new msg.wire_header column so any hash -- including future
add-to batch variants -- can always be recomputed faithfully, immune
to re-encode drift. Locally-authored messages leave it null.

Existing rows cannot be backfilled; the discarded recipients were
lost at receive time. Upgrade path for existing databases:
    alter table msg add column if not exists wire_header bytea;
(no other schema change; the sentinel uses the existing
response_code column).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@markmnl
markmnl merged commit 6ec8db3 into main Aug 3, 2026
2 of 3 checks passed
@markmnl
markmnl deleted the store-full-inbound-recipients branch August 3, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant