Skip to content

Resolve reply parents referencing add-to batch hashes - #39

Open
markmnl wants to merge 3 commits into
fix/add-to-batch-hash-identityfrom
fix/batch-hash-reply-parents
Open

Resolve reply parents referencing add-to batch hashes#39
markmnl wants to merge 3 commits into
fix/add-to-batch-hash-identityfrom
fix/batch-hash-reply-parents

Conversation

@markmnl

@markmnl markmnl commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Stacked on #35 (needs msg_add_to_batch.sha256) — review/merge #35 first; this PR's base is fix/add-to-batch-hash-identity.

Implements the acceptance side of the sibling-branch ruling: a reply to an add-to batch references the batch message's hash via pid, and per SPEC §11 the receiving host can verify it because the batch message is reconstructible from what it already holds — the stored shared message plus the batch's sender, recipients and wire time — even though it never downloaded the message data again.

Changes

  • Faithful batch reconstruction: the receive path now records the batch's wire time in msg_add_to_batch.time_added (previously the local record time). This matches the outbound path, where time_added already serves as the batch header's timestamp (addToHeader), and is required for the batch hash to be recomputable from stored fields.
  • getMsgByBatchHash: resolves a batch hash to the reconstructed batch wire header via the existing loadMsgFields/loadAddToBatches/addToHeader machinery. validatePidReplyPath falls back to it when pid matches no canonical msg.sha256; time-travel and participant checks then run against the batch header (whose participants are the original's plus that batch's add to — exactly the batch message's participant set).
  • Relational linking: txParentLinkStore.lookupParentID also resolves batch hashes to the shared message row, so a stored batch-reply's pid links into the thread.
  • Own batches: the sender persists a locally originated batch's hash at first delivery (ensureBatchHash, mirroring ensureSharedHash; the challenge response reuses the cached computation). Without this, a remote reply referencing our own batch could not resolve here — SPEC §11's "sent by the host" clause applies to batches too.
  • Challenge participation: threadHasFromDomain's root lookup also matches batch hashes, so HAS_NOT_PARTICIPATED mode doesn't wrongly challenge batch replies.
  • handleAddToPath keeps the canonical-only lookup deliberately (now commented): batches do not chain, so an add-to whose pid is another batch's hash must not resolve.

Not in this PR (needs coordinated fmsg-webapi change)

  • Tightening replies to the original: added recipients are participants of their batch only, so a reply from an added recipient carrying the original's hash should be rejected code 1. fmsgd currently still accepts it (loadMsg merges all batch recipients into the parent's participant set) — deliberately left, because fmsg-webapi today sends replies with the shared hash as pid for all participants. Once webapi replies with the batch hash for added recipients, the merged participant set in loadMsg can be narrowed.
  • fmsg-webapi needs to expose batch hashes so clients reply to the batch.

🤖 Generated with Claude Code

dd.sql trigger fix (647c36e)

Integration testing caught that the populate_msg_psha256_from_pid trigger rejected stored batch replies: once the relational pid links to the shared row, it required psha256 to equal the parent's canonical sha256. It now also accepts a psha256 matching one of the parent's recorded add-to batch hashes.

Verification (fmsg-docker integration suite)

New test 009-reply-to-add-to-batch.sh (pushed to fmsg-docker main) covers the full scenario: receiver records the batch hash reconstructed without re-downloading data, originator persists the identical hash, and a reply carrying the batch hash as pid is delivered cross-instance and relationally linked.

  • fmsgd main: tests 001–008 pass, 009 fails ("did not record the add-to batch hash").
  • This branch (fresh stacks, synced dd): 9/9 pass.

After merge: run scripts/update-dd.sh in fmsg-docker and commit the dd sync (merging the dd sync before this branch would break fmsgd main's add-to path — the old code's ON CONFLICT (msg_id, addr) needs the constraint the new schema drops).

markmnl and others added 2 commits August 11, 2026 07:37
A reply to an add-to batch message carries the batch hash as pid. Per
SPEC SS11 the batch message is reconstructible from what the host
already holds - the stored shared message plus the batch's sender,
recipients and wire time - even though the batch's data was never
downloaded again. Reply validation now falls back to reconstructing
the batch wire form when pid matches msg_add_to_batch.sha256, and the
relational parent link and challenge-participation thread walk resolve
batch hashes to the shared message row.

For that reconstruction to be faithful the receive path now records
the batch's wire time in time_added, matching the outbound path where
time_added already serves as the batch header's timestamp.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The receive path stores a batch's hash when recording it, but batches
this host originates never had theirs stored - so a remote reply
referencing our own batch could not resolve here (SPEC SS11: a host
verifies messages it sent, not only ones it received). Mirror
ensureSharedHash: compute once at first delivery (the challenge
response reuses the cached value) and persist when null.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The populate-psha256 trigger required psha256 to equal the relational
parent's canonical sha256, rejecting stored replies that reference one
of the parent's add-to batch messages by batch hash (SPEC SS12).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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