SIGN IN SIGN UP

security: close the four signed-ACK gaps without breaking the wire (#109)

Follow-up to #100. Each gap below was reported against the strict variant in #104
and is closed here compatibly, keeping the two-stage rollout.

1. Durable replay protection. AckReceiptStore + an ack_receipts table in
   SQLiteDedupeOutboxStore give claim-once semantics on (sender_agent_id, nonce)
   that survive a restart. The in-memory Set remains only as an explicit fallback,
   and the daemon now logs a warning when it is the one in use.

2. Fast-ACK race. applyAckTransition accepts 'pending' alongside 'sent', so an ACK
   arriving between publish() and markSent() is applied instead of being rejected
   into a spurious retry. markSent() refuses to downgrade a terminal status, so the
   late call cannot resurrect a settled row.

3. A2A raw-NACK sink. The bridge no longer resolves a pending task from an unsigned
   {msgId, status} object. Only a SignedAckV1 verified against the pinned signing key
   of its claimed sender is honoured; signingPublicKeys was added to the config.

4. WebSocket ACK path. processAckFrame verified nothing and called markAcked/markFailed
   straight from the frame. It now performs the same checks as the NATS path — record
   lookup, digest, conversation, recipient, peer, ack-subject binding, signature and
   nonce claim — with unsigned frames still accepted until requireSignedAcks is set.

Also aligns five packages that declared @murmurv2/core ^0.2.0: npm could not satisfy
that from the workspace and silently installed 0.2.0 from the registry, so bridge-a2a,
bridge-openclaw, bridge-telegram, broker-ws and federation-nats were building and
testing against a core two minor versions behind.

224/224 tests pass, including 11 new regressions in tests/ack-hardening.test.mjs.

Reported by @fedoseevstanislav.
J
JARVIS committed
13ffba28bc3d055098cd4cd8c561f89a22588501
Parent: edacac2