Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC3771: Read receipts for threads #3771

Merged
merged 29 commits into from
Sep 25, 2022
Merged
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d330ba9
Add initial MSC for read receipts for threads.
clokep Apr 15, 2022
42d7daf
Fix events in diagram.
clokep Apr 20, 2022
4c5382d
Add sync response.
clokep Apr 20, 2022
b5dac99
Link to the spec.
clokep Apr 26, 2022
9445049
Clarify sentence.
clokep Apr 26, 2022
0e42ec3
Some clarifications.
clokep May 24, 2022
e74c989
Simplification.
clokep Jul 6, 2022
65fa009
Fix JSON key format.
clokep Jul 22, 2022
1f0d6dd
Add information on clearing notifications.
clokep Jul 22, 2022
03ac0e0
Fix example.
clokep Aug 3, 2022
e3e677a
Update with current understanding.
clokep Aug 8, 2022
5540252
Clarify introduction.
clokep Aug 16, 2022
bbdb23e
MSC3773 is not yet accepted.
clokep Aug 16, 2022
2678f3c
Updates from feedback.
clokep Aug 16, 2022
ea77632
Update from learnings from the proof of concept.
clokep Aug 31, 2022
1c60533
Add link to the current spec.
clokep Sep 6, 2022
347d332
Clarify that false positives are deliberate in the design.
clokep Sep 6, 2022
5d73531
Receipts must move forward.
clokep Sep 6, 2022
ef736e4
More info on unthreaded receipts.
clokep Sep 6, 2022
de7ba49
Reflow.
clokep Sep 8, 2022
e1ab8e5
Clarify the proposal to explain why both threaded and unthreaded rece…
clokep Sep 8, 2022
6e71034
Add information about validating that an event is part of a thread.
clokep Sep 12, 2022
2b857b7
Remove section on second-order relations.
clokep Sep 12, 2022
d423542
Use proper syntax highlighting.
clokep Sep 13, 2022
ecbd6c4
Clarify unthreaded vs. main timeline receipts.
clokep Sep 20, 2022
1a5b5ae
Fix typos.
clokep Sep 20, 2022
7471923
Clarify wording.
clokep Sep 20, 2022
b171a31
Clarify example.
clokep Sep 20, 2022
668c9de
Fix alternatives section.
clokep Sep 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions proposals/3771-read-receipts-for-threads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# MSC3771: Read receipts for threads
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
clokep marked this conversation as resolved.
Show resolved Hide resolved

Currently, each room has a single read receipt per user. This is used to sync the
clokep marked this conversation as resolved.
Show resolved Hide resolved
read status of a room across clients and calculate the number of unread messages.

Unfortunately a client displaying threads may show a subset of a room's messages
at a time, causing the read receipt to be misleading.

This might better be described by an example. Given a room with the following
DAG of events (note that the dotted lines are a thread relation, as specified by
[MSC3440](https://github.com/matrix-org/matrix-doc/pull/3440)):

```mermaid
flowchart RL
F-->E
E-->D
D-->C
C-->B
B-->A
D-.->A
F-.->A
```

A client might interpret this as:

```mermaid
flowchart RL
subgraph Main timeline
C-->B
B-->A
F-->C
end
subgraph Thread timeline
D-->A
E-->D
end

style A fill:cyan,stroke:#333,stroke-width:2px
style E fill:orange,stroke:#333,stroke-width:2px
```

While viewing the "main" timeline of the room, a client might move the read
receipt from event `A` to event `E` without ever showing events `D` and `F`. The
user then reads the thread, the client has no way to mark `F` as read.
clokep marked this conversation as resolved.
Show resolved Hide resolved

## Proposal

This MSC proposes adding a new receipt type of `m.read.thread.private` is to be
added. This receipt type is used for clients to sync the read status of each
clokep marked this conversation as resolved.
Show resolved Hide resolved
thread in a room.

The `/receipt` endpoint gains a new optional path part and becomes:
clokep marked this conversation as resolved.
Show resolved Hide resolved

`POST /_matrix/client/v3/rooms/{roomId}/receipt/{receiptType}/{eventId}/{context}`

The `context` contains the thread that the read receipts belongs to (i.e. it should
match the `event_id` contained within the `m.relates_to` of the event represented
by `eventId`). This updates the unique tuple for receipts from
`(room ID, user ID, receipt type)` to `(room ID, user ID, receipt type, context)`.
clokep marked this conversation as resolved.
Show resolved Hide resolved
For backwards compatibility, a missing `context` is equivalent to an empty context.

Given a threaded message:

```json
{
"event_id": "$thread_reply",
"room_id": "!room:example.org",
"content": {
"m.relates_to": {
"rel_type": "m.thread",
"event_id": "$thread_root"
}
}
}
```

A client could mark this as read by sending a request:

```
POST /_matrix/client/r0/rooms/!room:example.org/receipt/m.read.thread.private/$thread_reply/$thread_root

{}
```

Similarly to the hidden read receipts from [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285),
homeservers are not to send the receipt to any other users except the sender nor
over federation.

## Potential issues

For long-lived rooms or rooms with many threads there could be a significant number
of receipts. This has a few downsides:

* The size of the `/sync` response would increase without bound.
* The effort to generate and process the receipts for each room would increase
without bound.
dbkr marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a plan for this? I don't think this is a viable situation long-term, though happy enough to punt it for now. (might be worth looping in people thinking about sliding sync?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a plan for this? I don't think this is a viable situation long-term, though happy enough to punt it for now. (might be worth looping in people thinking about sliding sync?)

I don't have a long-term plan for this, no. I'm unsure how much of a concern this really is -- it would depend how often folks sending threaded read receipts, although "without bound" might have been a bit of hyperbole.

I think from this we'll end up processing more receipts, but they'll be over smaller chunks of events, so I hope the actual effort isn't too much worse.


Due to both of the above, this proposal is limited to a *private* read receipt for
threads. This limits the impact to a user's own read receipts for threads, but
does not completely solve the issue.

[MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285) suggests
that the `/read_markers` endpoint should become a generic bulk receipt endpoint.
This is not compatible with the additional `context` parameter in this MSC.

## Alternatives

Instead of adding the thread ID as a new path part, it could be added to the body
clokep marked this conversation as resolved.
Show resolved Hide resolved
of the receipt. There may be a small backwards compatibility benefit to this, but
it seems clearer to put it as part of the URL.

Similarly, instead of adding a new receipt type, the read status of each thread in
a room could be added to the body of the `m.read.private` receipt. This could
cause data integrity issues if multiple clients attempt to update the receipt
without first reading it.

Instead of adding the thread ID as a new path part, it could be encoded as a suffix
on the receipt, e.g. `m.thread.read.private-$abcd`. This has the benefit of not
changing the current receipt mechanisms, but seems sub-par and requires additional
parsing of opaque identifiers.

## Security considerations

There is potential for abuse by allowing clients to specify a unique `context`.
A mitigation could be to ensure that it is the related event of the thread, ensuring
that each thread only has a single context.

## Future extensions

Future extensions will tackle how the thread read receipts impact notification counts.

## Unstable prefix

During implementation the receipt type shall be `org.matrix.mscXXXX`.
clokep marked this conversation as resolved.
Show resolved Hide resolved

To avoid receipts that will never be updated, after stabilization, homeservers are
expected to reject the unstable receipt type and purge all receipts using the
unstable receipt type.

To detect server support, clients can either rely on the spec version (when stable)
or the presence of a `org.matrix.msc3771` flag in `unstable_features` on `/versions`.

## Dependencies

This MSC depends on the following MSCs, which at the time of writing have not yet
been accepted into the spec:

* [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285): Hidden read receipts

As well as the following MSCs, which have been accepted into the spec, but have
yet to be released:

* [MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674): Event Relationships
* [MSC3440](https://github.com/matrix-org/matrix-spec-proposals/pull/3440): Threading via `m.thread` relation