You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
matrix-spec-proposals/proposals/4103-threaded-read-receipt-...

4.7 KiB

MSC4103: Make threaded read receipts opt-in in /sync

Problem

When we added threaded RRs in MSC3771 we broke existing (i.e. non-thread-aware) clients from reliably being able to calculate whether a room is unread, given threaded read receipts are interpreted as unthreaded ones.

For instance, let's assume Alice is logged in on two clients: a desktop client which is thread aware, and a mobile client which is not.

  • Alice has a bunch of unread messages on her main timeline and a bunch of unread threads.
  • The threads are newer than the main timeline msgs.
  • She reads the most recently active thread on desktop, which sends a threaded RR.
  • Mobile interprets this as a normal RR, and so marks the room as read (despite she hasn't read main timeline msgs)
  • Desktop however correctly shows the room as still unread.

The key thing is that just because Alice happened to read a thread on a thread-aware client doesn't mean semantically that she's also read the main timeline.

Non-thread-aware clients simply do not care what messages users may have read on threads; they just care where the user has read up to in the main timeline (which for them, is a linearized view of the whole room, and for threaded clients is the main timeline).

Another way of thinking of it which may or may not be helpful:

  • If Alice reads a thread on desktop, then her mobile doesn't care.
  • If Alice reads a main timeline msg on desktop, then her mobile will want to show the flattened timeline as read up to that msg.
  • If Alice reads a given msg on mobile, then she sends an unthreaded RR, which can mark all threads prior to that point as read (for threaded clients). Non-thread-aware clients will be synced to the same RR point, as you'd expect.

Proposal

Non-thread-aware clients should only act on unthreaded RRs.

Prior to this MSC, there is no way for non-thread-aware clients to determine which RRs are unthreaded, other than seeing if the thread_id field is missing on the RR or present and equal to main.

As a result, to get sensible room read state semantics for non-thread-aware clients, they would all need to become aware of the new thread_id field, breaking existing clients, even if they don't care about threads. This feels contradictory and suboptimal.

We could solve this by making threaded RRs a different event type (e.g. m.read_thread), so non-thread-aware clients would automatically ignore them. However, this would require threaded clients to send double the read receipts whenever the user reads the main thread - both an m.read and an m.read_thread, which feels inefficient. (It would however mean that MSC4102 would not be necessary, as the RR types would be clearly distinct).

Instead, we propose that they continue to follow MSC3771 (i.e. putting a thread_id on the m.read RR). However, we give threaded clients the ability to explicitly opt-in to threaded read receipts in the form of a new sync filter param. If clients explicitly include threaded_read_receipts: true on their sync filter, then the server will send them m.read events as received by the server. If this filter is missing or false, then the server must only send the client m.read events whose thread_id is missing or main.

This means that non-thread-aware clients are not spammed or confused with irrelevant threaded read receipts, and can calculate read state purely based on main timeline activity.

Alternatives

We could split the event types into m.read and m.read_thread but instead have the server synthesise m.read events out of m.read_thread events with thread_id: 'main' when the threaded_read_receipts sync filter is false or absent. This would end up with the same end result, and would be cleaner in terms of type safety (and avoid the need for MSC4102), but would mean having to completely change the event type in every thread-aware client rather than just fixing it in servers, and back it out of the spec. So for pragmatism, this MSC proposes leaving MSC3771 as is, and instead let thread-aware clients opt-in via sync filters to avoid breaking existing clients. This can be done incrementally by thread-aware clients when they find themselves talking to a server which implements this MSC, thus avoiding breaking existing thread-aware clients.

Security considerations

If we ever encrypt RRs, then the server won't be able to filter them out. But that's not planned currently, and we can always expose threadedness as metadata to aid this if it were to happen.

Unstable prefix

threaded_read_receipts should be namespaced as org.matrix.msc4103.threaded_read_receipts while unstable.