[verify] Thread read receipts must honour "Private Read Receipts" #141

Closed
opened 2026-09-17 16:26:17 -04:00 by jared · 1 comment
Owner

Per-thread read tracking (P3-8, m.read with thread_id) landed after the private-receipts toggle. Verify the thread receipt path sends m.read.private when privateReadReceipts is on (same for the fully-read marker); if it sends public m.read, that is a privacy leak. Unit-test the receipt-type selection.

Per-thread read tracking (P3-8, `m.read` with `thread_id`) landed after the private-receipts toggle. Verify the thread receipt path sends `m.read.private` when `privateReadReceipts` is on (same for the fully-read marker); if it sends public `m.read`, that is a privacy leak. Unit-test the receipt-type selection.
jared added this to the Features 2026-Q4 milestone 2026-09-17 16:26:17 -04:00
jared added the bugpriority: highsecurityarea: threads labels 2026-09-17 16:26:17 -04:00
jared self-assigned this 2026-09-17 16:26:17 -04:00
Author
Owner

Verified on the local dev homeserver with Playwright (scripts/dev-homeserver.sh), capturing every request to /typing/ and /receipt/ while typing in the main composer and in the thread composer (thread opened from the root's "2 replies" chip), then receiving a new thread reply while the panel was open. Settings toggled through the real Settings UI, persisted across a reload.

setting main typing PUTs thread typing PUTs receipts
privacy off 1 2 m.read ×4 (all thread_id scoped)
Hide Typing & Read Receipts + Private Read Receipts on 0 0 m.read.private ×4 (all thread_id scoped)

Thread receipts go through markThreadAsRead(mx, thread, hideActivity || privateReadReceipts) (ThreadPanel.tsx:162) and are m.read.private when either setting is on. Verified — closing.

Side observation (not a privacy issue, noted on #165): every thread receipt is sent twice for the same event (4 requests for 2 events in both runs) — markRead is invoked on both ThreadEvent.NewReply and RoomEvent.Timeline, or the panel effect runs for two thread instances. Harmless, but worth a dedupe in the threads audit.

Verified on the local dev homeserver with Playwright (`scripts/dev-homeserver.sh`), capturing every request to `/typing/` and `/receipt/` while typing in the **main** composer and in the **thread** composer (thread opened from the root's "2 replies" chip), then receiving a new thread reply while the panel was open. Settings toggled through the real Settings UI, persisted across a reload. | setting | main typing PUTs | thread typing PUTs | receipts | |---|---|---|---| | privacy off | 1 | 2 | `m.read` ×4 (all `thread_id` scoped) | | **Hide Typing & Read Receipts + Private Read Receipts on** | **0** | **0** | **`m.read.private` ×4** (all `thread_id` scoped) | Thread receipts go through `markThreadAsRead(mx, thread, hideActivity || privateReadReceipts)` (`ThreadPanel.tsx:162`) and are `m.read.private` when either setting is on. Verified — closing. Side observation (not a privacy issue, noted on #165): every thread receipt is sent **twice** for the same event (4 requests for 2 events in both runs) — `markRead` is invoked on both `ThreadEvent.NewReply` and `RoomEvent.Timeline`, or the panel effect runs for two thread instances. Harmless, but worth a dedupe in the threads audit.
jared closed this issue 2026-09-18 00:37:19 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#141