-
Notifications
You must be signed in to change notification settings - Fork 399
MSC2677: Annotations and reactions #2677
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
Merged
turt2live
merged 14 commits into
matrix-org:old_master
from
uhoreg:aggregations-reactions
Mar 26, 2023
Merged
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
4d6ce1b
initial version of reactions proposal
uhoreg 8762524
fix MSC numbers
uhoreg 2e75639
add security consideration
uhoreg 5852b28
remove event type from aggregation grouping criteria because of e2ee
bwindels 121ca14
Apply suggestions from code review
richvdh a0ed8e7
Update intro and add background
richvdh 657a746
Corrections and clarifications to the main text
richvdh dad2b07
Clarify counting rules and interactions with edits
richvdh fba3948
Error code for deduplicating annotations
richvdh 4031ad4
Clarify eligible target events
richvdh 0d146ca
Notes on encryption
richvdh c28c385
Clarify variation-16
richvdh fdbb745
Update 2677-reactions.md
richvdh 749198f
No server-side aggregation for reactions
richvdh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,337 @@ | ||
# MSC2677: Annotations and Reactions | ||
|
||
Users sometimes wish to respond to a message using emojis. When such responses | ||
are grouped visually below the message being reacted to, this provides a | ||
(visually) light-weight way for users to react to messages. | ||
|
||
This proposal was originally part of [MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849). | ||
|
||
## Background | ||
|
||
As with [message | ||
edits](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/2676-message-editing.md#background), | ||
support for reactions were landed in the Element clients and Synapse in May | ||
2019, following the proposals of | ||
[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849) and then | ||
presented as being "production-ready", despite them not yet having been adopted | ||
into the Matrix specification. | ||
|
||
Again as with edits, the current situation is therefore that client or server | ||
implementations hoping to interact with Element users must simply follow the | ||
examples of that implementation. | ||
|
||
To rectify the situation, this MSC therefore seeks primarily to formalise the | ||
status quo. Although there is plenty of scope for improvement, we consider | ||
that better done in *future* MSCs, based on a shared understanding of the | ||
*current* implementation. | ||
|
||
In short, this MSC prefers fidelity to the current implementations over | ||
elegance of design. | ||
|
||
On the positive side: this MSC is the last part of the former MSC1849 to be | ||
formalised, and is by far the most significant feature implemented by the | ||
Element clients which has yet to be specified. | ||
|
||
## Proposal | ||
|
||
### `m.annotation` event relationship type | ||
|
||
A new [event relationship type](https://spec.matrix.org/v1.6/client-server-api/#relationship-types) | ||
with a `rel_type` of `m.annotation`. | ||
|
||
This relationship type is intended primarily for handling emoji reactions, allowing clients to | ||
send an event which annotates an existing event. | ||
|
||
Another potential usage of annotations is for bots, which could use them to | ||
report the success/failure or progress of a command. | ||
|
||
Along with the normal properties `event_id` and `rel_type`, the | ||
[`m.relates_to`](https://spec.matrix.org/v1.6/client-server-api/#definition-mrelates_to) | ||
property should contains a `key` that indicates the annotation being | ||
applied. For example, when reacting with emojis, the `key` contains the emoji | ||
being used. | ||
|
||
An event annotating another with the thumbs-up emoji would therefore have the following `m.relates_to` propperty: | ||
|
||
```json | ||
"m.relates_to": { | ||
"rel_type": "m.annotation", | ||
"event_id": "$some_event_id", | ||
"key": "👍" | ||
} | ||
``` | ||
|
||
When sending emoji reactions, the `key` property should include the unicode | ||
[emoji presentation | ||
selector](https://www.unicode.org/reports/tr51/#def_emoji_presentation_selector) | ||
(`\uFE0F`) for codepoints which allow it (see the [emoji variation sequences | ||
list](https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-variation-sequences.txt)). | ||
|
||
Any `type` of event is eligible for an annotation, though note that, since | ||
state events do not currently receive bundled aggregations (see | ||
[aggregations](https://spec.matrix.org/v1.6/client-server-api/#aggregations)), | ||
the results of annotating a state event may be inconsistent. | ||
|
||
### `m.reaction` event type | ||
|
||
A new message type `m.reaction` is proposed to indicate that a user is reacting | ||
to a message. No `content` properties are defined for this event type: it serves | ||
only to hold a relationship to another event. | ||
|
||
For example, an `m.reaction` event which annotates an existing event with a 👍 | ||
looks like: | ||
|
||
```json | ||
{ | ||
"type": "m.reaction", | ||
"content": { | ||
"m.relates_to": { | ||
"rel_type": "m.annotation", | ||
"event_id": "$some_event_id", | ||
"key": "👍" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Since they contain no `content` other than `m.relates_to`, `m.reaction` events | ||
are normally not encrypted, as there would be no benefit in doing so. | ||
|
||
### Interation with edited events | ||
|
||
It is not considered valid to send an annotation for a [replacement | ||
event](https://spec.matrix.org/v1.6/client-server-api/#event-replacements) | ||
(i.e., a message edit event): any reactions should refer to the original | ||
event. Annotations of replacement events will be ignored according to the rules | ||
for [counting annotations](#counting-annotations). | ||
|
||
As an aside, note that it is not possible to edit a reaction, since replacement | ||
events do not change `m.relates_to` (see [Applying | ||
`m.new_content`](https://spec.matrix.org/v1.6/client-server-api/#applying-mnew_content)), | ||
and there is no other meaningful content within `m.reaction`. If a user wishes | ||
to change their reaction, the original reaction should be redacted and a new | ||
one sent in its place. | ||
|
||
### Counting annotations | ||
|
||
The intention of annotations is that they are counted up, rather than being displayed individually. | ||
|
||
Clients must keep count of the number of annotations with a given event `type` | ||
and annotation `key` they observe for each event; these counts are typically | ||
presented alongside the event in the timeline. | ||
|
||
Servers must perform a similar operation to calculate the relationship | ||
aggregation (see | ||
[below](#server-side-aggregation-of-mannotation-relationships)). | ||
|
||
When performing this count: | ||
|
||
* Servers must count each event `type` and annotation `key` | ||
separately. Clients will normally choose to do so to though this is an | ||
implementation decision. | ||
|
||
* Annotation events sent by [ignored users](https://spec.matrix.org/v1.6/client-server-api/#ignoring-users) | ||
should be excluded from the count. | ||
|
||
* Multiple identical annotations (i.e., with the same event `type` and | ||
annotation `key`) from the same user (i.e., events with the same `sender`) should | ||
be treated as a single annotation. | ||
|
||
* It is not considered valid to annotate an event which itself has an | ||
`m.relates_to` with `rel_type: m.annotation` or `rel_type: | ||
m.replace`. Implementations should ignore any such annotation events. | ||
|
||
* When an annotation is redacted, it is removed from the count. | ||
|
||
### Push rules | ||
|
||
Since reactions are considered "metadata" that annotate an existing event, they | ||
should not by default trigger notifications. Thus a new [default override | ||
rule](https://spec.matrix.org/v1.6/client-server-api/#default-override-rules) | ||
is to be added that ignores reaction events: | ||
|
||
```json | ||
{ | ||
"rule_id": ".m.rule.reaction", | ||
"default": true, | ||
"enabled": true, | ||
"conditions": [ | ||
{ | ||
"kind": "event_match", | ||
"key": "type", | ||
"pattern": "m.reaction" | ||
} | ||
], | ||
"actions": [] | ||
} | ||
``` | ||
|
||
The rule is added between `.m.rule.tombstone` and `.m.rule.room.server_acl`. | ||
|
||
(Synapse implementation: [base_rules.rs](https://github.com/matrix-org/synapse/blob/157c571f3e9d3d09cd763405b6a9eb967f2807e7/rust/src/push/base_rules.rs#L216-L229)) | ||
|
||
### Server support | ||
|
||
#### Avoiding duplicate annotations | ||
|
||
Homeservers should prevent users from sending a second annotation for a given | ||
event with identical event `type` and annotation `key` (unless the first event | ||
has been redacted). | ||
|
||
Attempts to send such an annotation should be rejected with a 400 error and an | ||
error code of `M_DUPLICATE_ANNOTATION`. | ||
|
||
Note that this does not guarantee that duplicate annotations will not arrive | ||
over federation. Clients and servers are responsible for deduplicating received | ||
annotations when [counting annotations](#counting-annotations). | ||
|
||
#### Server-side aggregation of `m.annotation` relationships | ||
|
||
Homeservers should | ||
[aggregate](https://spec.matrix.org/v1.6/client-server-api/#aggregations) | ||
events with an `m.annotation` relationship to a given event. | ||
|
||
When aggregating `m.annotation` events, homeservers should group events | ||
together based on their event `type` and `key`, and count the number of each | ||
distinct `type`/`key`. An example aggregation is as follows: | ||
|
||
```json5 | ||
{ | ||
"event_id": "$original_event_id", | ||
// irrelevant fields not shown | ||
"unsigned": { | ||
"m.relations": { | ||
"m.annotation": { | ||
"chunk": [ | ||
{ | ||
"type": "m.reaction", | ||
"key": "👍", | ||
"count": 3 | ||
}, | ||
{ | ||
"type": "example.com.test", | ||
"key": "👍", | ||
"count": 1 | ||
}, | ||
{ | ||
"type": "m.reaction", | ||
"key": "👎", | ||
"count": 2 | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
This event has received three thumbsup reactions, two thumbsdown reactions, and | ||
a thumbsup annotation with an event type of `example.com.test` (which will be | ||
ignored by clients which don't understand the `example.com.test` event type). | ||
|
||
As the example shows, the aggregation format has a `chunk` property at the top | ||
level. (This is indended to allow pagination of reactions in a future | ||
extension, though that is not currently specified.) Each entry in the `chunk` is | ||
an object with properties: | ||
|
||
* `type`: the event `type` of the aggregated events. | ||
* `key`: the `key` from the `m.relates_to` properties of the aggregated events. | ||
* `count`: the number of unredacted events with this event `type` and annotation `key`. | ||
|
||
When evaluating `count`, servers should respect the guidelines above about | ||
[counting annotations](#counting-annotations). | ||
|
||
## Alternatives | ||
|
||
### Encrypted reactions | ||
|
||
[matrix-spec#660](https://github.com/matrix-org/matrix-spec/issues/660) | ||
discusses the possibility of encrypting message relationships. | ||
|
||
### Extended annotation use case | ||
|
||
In future it might be useful to be able to annotate events with more | ||
information, some examples include: | ||
|
||
* Annotate commit/PR notification messages with their associated CI state, e.g. | ||
pending/passed/failed. | ||
* If a user issues a command to a bot, e.g. `!deploy-site` the bot could | ||
annotate that event with current state, like "acknowledged", | ||
"redeploying...", "success", "failed", etc. | ||
* Other use cases...? | ||
|
||
However, this doesn't really work with the proposed grouping, as the aggregation | ||
key wouldn't contain the right information needed to display it (unlike for | ||
reactions). | ||
|
||
One way to potentially support this is to include the events (or a subset of the | ||
event) when grouping, so that clients have enough information to render them. | ||
However this dramatically inceases the size of the parent event if we bundle the | ||
full events inside, even if limit the number we bundle in. To reduce the | ||
overhead the annotation event could include a `m.result` field which gets | ||
included. | ||
|
||
This would look something like the following, where the annotation is: | ||
|
||
```json | ||
{ | ||
"type": "m.bot_command_response", | ||
"content": { | ||
"m.result": { | ||
"state": "success", | ||
}, | ||
"m.relates_to": { | ||
"type": "m.annotation", | ||
"key": "" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
and gets bundled into an event like: | ||
|
||
```json | ||
{ | ||
"unsigned": { | ||
"m.relations": { | ||
"m.annotation": [ | ||
{ | ||
"type": "m.bot_command_response", | ||
"key": "", | ||
"count": 1, | ||
"chunk": [ | ||
{ | ||
"m.result": { | ||
"state": "success", | ||
}, | ||
} | ||
], | ||
"limited": false, | ||
} | ||
] | ||
} | ||
} | ||
} | ||
``` | ||
|
||
This is something that could be added later on. A few issues with this are: | ||
|
||
* How does this work with E2EE? How do we encrypt the `m.result`? | ||
* We would end up including old annotations that had been superceded, should | ||
these be done via edits instead? | ||
|
||
## Security considerations | ||
|
||
Clients should render reactions that have a long `key` field in a sensible | ||
manner. For example, clients can elide overly-long reactions. | ||
bwindels marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
If using reactions for upvoting/downvoting purposes we would almost certainly want to anonymise the | ||
reactor, at least from other users if not server admins, to avoid retribution problems. | ||
This gives an unfair advantage to people who run their own servers however and | ||
can cheat and deanonymise (and publish) reactor details. In practice, reactions may | ||
not be best used for upvote/downvote as at the unbundled level they are intrinsically | ||
private data. | ||
|
||
Or in a MSC1228 world... we could let users join the room under an anonymous | ||
persona from a big public server in order to vote? However, such anonymous personae | ||
would lack any reputation data. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.