natsmsg: key the DLQ copy on the stored message, and vouch only for provenance it wrote
Commit

Both findings hold. Verified against the pinned nats-server 2.14.3 and nats.go 1.52.0 rather than taken on report.
THE DEDUPE KEY COULD STILL COLLAPSE DISTINCT MESSAGES. Keyed on stream and sequence alone it identified a message only within one INCARNATION of one stream. Delete a stream and recreate it and numbering restarts at 1, so a DLQ that outlives the source — the ordinary case, since the DLQ is what the source's messages are rescued into — sees one stream/sequence pair naming two unrelated messages. Inside the duplicate window the second collapsed onto the first, and because a duplicate PubAck reports SUCCESS the caller acked an original whose copy was never stored. The same silent loss the previous commit set out to close, reached by a different route. MsgMetadata.Domain was ignored too, so same-named streams in different JetStream domains collided as well, and the comment claiming the key "names it uniquely across every stream in the deployment" was simply false.
The key is now domain, stream, stream sequence and store timestamp. Both required properties are now argued from the components rather than asserted: STABLE for one stored message, since every part is a fixed property of the message as stored (NumDelivered is deliberately excluded — it changes per delivery, and including it would turn the intended collapse into a pile of near-duplicates); and DISTINCT across messages, since the timestamp separates incarnations that reuse a sequence and the domain separates same-named streams. The dot separator is unambiguous rather than merely tidy: domain and stream arrive as single tokens of the dot-delimited $JS.ACK subject, so neither can contain a dot, and the other two are decimal digits. An absent domain is written "_", mirroring the wire sentinel. A zero store time is written 0 rather than letting UnixNano report its undefined value for the zero Time, which reads like a real timestamp.
PROVENANCE CLAIMED A CHAIN IT DID NOT KEEP. Retaining inbound Nats-Dlq- headers was supposed to accumulate a hop chain. It did not: every field is overwritten for the current hop, including Origin-Msg-Id, which on a re-capture took the previous copy's synthesized DLQ key and buried the first publisher's. So the retention preserved nothing — while letting any producer hand a replay tool forged provenance that looks like this library's, since a Nats-Dlq- header set upstream was kept verbatim.
Both problems have one cause, so one fix: nothing inbound in the reserved namespace survives, this package's own prefix included. Every Nats-Dlq- header on a copy was written by the capture that produced it. That is an authorship rule — a header this package did not write is one whose truth it cannot vouch for.
The chain is still walkable, as a chain of RECORDS: origin stream and sequence name the exact stored message a copy was made from, so the previous hop is one GetMsg away and its provenance names the hop before it. The cost is stated rather than hidden — the walk needs each intermediate record to still exist, so a link can dangle where a replay tool acks records off a work-queue DLQ. Callers needing first-publisher identity across arbitrary hops should carry it in their own header, outside the reserved namespace, where it is theirs to keep. Origin-Msg-Id now documents what it actually claims: the Nats-Msg-Id the captured message carried, which is the producer's on a first hop and the previous copy's on a re-capture, and is message-reported either way.
Broker-backed, per this module's rule: a really deleted and recreated source stream, both messages stored at sequence 1, asserting two DLQ records. Against the previous key the DLQ holds ONE — the finding, measured. Unit tests pin the key's stability across redeliveries of one message, its distinctness across incarnations and domains, and that forged inbound provenance is dropped.
Refs ENT-1601, ENT-1535, ENT-1492, COR-1257.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com Claude-Session: https://claude.ai/code/session_018qXhU99D2Sxaa32yZWwYRP Entire-Checkpoint: 6e9d12b94705