Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
jack
cfa875459d Review fix: record withheld receipts in both tracking sets
Codex P1: the manager-path withheld claim landed only in
PrivateChatManager.sentReadReceipts, while the lifecycle read pass
dedups against ChatViewModel's persisted set — enabling receipts
before the next lifecycle pass could send a receipt for a message
read while the setting was off. The withheld branch now records into
the owner's persisted set too (markReceiptHandled, wired in the
bootstrapper); test strengthened to require both sets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 10:03:01 +02:00
jack
3e46829f32 Add a read-receipt toggle
Read receipts were unconditional: on chat open and on didBecomeActive,
twice. A receipt is a presence oracle — it says the person is awake,
holding their phone, and opened the app at that exact moment, which is
precisely the metadata the stated threat model says someone should be
able to withhold. Every mainstream messenger ships this switch.

- ReadReceiptSettings (default ON = existing behavior), toggle in the
  settings privacy section, reset on panic wipe.
- All four origination paths gated: mesh/nostr routing, direct mesh
  receipts, geohash receipts, and PrivateChatManager's read pass.
  Withheld receipts are still recorded locally as sent, so re-enabling
  the setting never fires a retroactive burst disclosing past reads.
- Only outbound receipts are affected; receipts from others display.
- 2 strings x 30 locales; tests pin the default, the reset, and that
  nothing reaches the transport while off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 09:17:55 +02:00
8 changed files with 526 additions and 1 deletions