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.
251 lines
10 KiB
Markdown
251 lines
10 KiB
Markdown
3 years ago
|
# MSC2674: Event relationships
|
||
|
|
||
|
It's common to want to send events in Matrix which relate to existing events -
|
||
|
for instance, reactions, edits and even replies/threads.
|
||
|
|
||
|
This proposal is one in a series of proposals that defines a mechanism for
|
||
|
events to relate to each other. Together, these proposals replace
|
||
|
[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849).
|
||
|
|
||
|
* This proposal defines a standard shape for indicating events which relate to
|
||
|
other events.
|
||
|
* [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) defines APIs to
|
||
|
let the server calculate the aggregations on behalf of the client, and so
|
||
|
bundle the related events with the original event where appropriate.
|
||
|
* [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676) defines how
|
||
|
users can edit messages using this mechanism.
|
||
|
* [MSC2677](https://github.com/matrix-org/matrix-doc/pull/2677) defines how
|
||
|
users can annotate events, such as reacting to events with emoji, using this
|
||
|
mechanism.
|
||
|
* [MSC3267](https://github.com/matrix-org/matrix-doc/pull/3267) defines how events
|
||
|
can make a reference to other events.
|
||
|
* [MSC3389](https://github.com/matrix-org/matrix-doc/pull/3389) defines changes to
|
||
|
the redaction algorithm, to preserve the type and target id of a relation.
|
||
|
|
||
|
|
||
|
## Proposal
|
||
|
|
||
|
This proposal introduces the concept of relations, which can be used to
|
||
|
associate new information with an existing event.
|
||
|
|
||
|
A relationship is an object with a field `rel_type`, which is a string describing the type of relation,
|
||
|
and a field `event_id`, which is a string that represents the event_id of the target event of this relation.
|
||
|
The target event must exist in the same room as the relating event is sent.
|
||
|
Both of those fields are required. An event is said to contain a relationship if its `content` contains
|
||
|
a relationship with all the required fields under the `m.relates_to` key. If any of these conditions is not met,
|
||
|
clients and servers should treat the event as if it does not contain a relationship.
|
||
|
Servers should reject events not meeting these conditions with an HTTP 400 error when
|
||
|
they are received via the client-server API.
|
||
|
|
||
|
Here's a (partial) example of an event relating to another event:
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"content": {
|
||
|
"m.relates_to": {
|
||
|
"rel_type": "m.replace",
|
||
|
"event_id": "$abc:server.tld"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
All the information about the relationship lives under the `m.relates_to` key.
|
||
|
|
||
|
If it helps, you can think of relations as a "subject verb object" triple,
|
||
|
where the subject is the relation event itself; the verb is the `rel_type`
|
||
|
field of the `m.relates_to` and the object is the `event_id` field.
|
||
|
|
||
|
We consciously do not support multiple different relations within a single event,
|
||
|
in order to keep the API simple. This means that if event A relates to event B
|
||
|
in two different ways you would send two events to describe the two relations,
|
||
|
rather than bundling them into a single event. Another MSC,
|
||
|
like [MSC 3051](https://github.com/matrix-org/matrix-doc/pull/3051),
|
||
|
can propose a change to add support for multiple relations if it turns out that
|
||
|
this would facilitate certain use cases.
|
||
|
|
||
|
Relations do not yet replace the
|
||
|
[reply mechanism currently defined in the spec](https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies).
|
||
|
|
||
|
### Relation types
|
||
|
|
||
|
Any values for `rel_type` should abide the
|
||
|
[general guidelines for identifiers](https://github.com/matrix-org/matrix-doc/pull/3171).
|
||
|
|
||
|
The `rel_type` property determines how an event relates to another and can be used
|
||
|
by clients to determine how and in what context a relation should be displayed.
|
||
|
|
||
|
[MSC 2675](https://github.com/matrix-org/matrix-doc/pull/2675) proposes to also interpret the `rel_type` server-side.
|
||
|
|
||
|
It is left up to the discretion of other MSCs building on this one whether they introduce
|
||
|
`rel_type`s that are specific to their use case or that can serve a broad range
|
||
|
of use cases. MSCs may define additional properties on the relation object for a given `rel_type`.
|
||
|
|
||
|
Currently, a few `rel_type`s are already proposed. Here's a non-exhaustive list:
|
||
|
|
||
|
- `m.replace` in [MSC 2676](https://github.com/matrix-org/matrix-doc/pull/2676).
|
||
|
- `m.annotation` in [MSC 2677](https://github.com/matrix-org/matrix-doc/pull/2677).
|
||
|
- `m.reference` in [MSC 3267](https://github.com/matrix-org/matrix-doc/pull/3267).
|
||
|
- `m.thread` in [MSC 3440](https://github.com/matrix-org/matrix-doc/pull/3440).
|
||
|
|
||
|
|
||
|
### Sending relations
|
||
|
|
||
|
Related events are normal Matrix events, and can be sent by the normal `/send`
|
||
|
API.
|
||
|
|
||
|
The server should postprocess relations if needed before sending them into a
|
||
|
room, as defined by the relationship type. For example, a relationship type
|
||
|
might only allow a user to send one related message to a given event.
|
||
|
|
||
|
### Receiving relations
|
||
|
|
||
|
Relations are received like other non-state events, with `/sync`,
|
||
|
`/messages` and `/context`, as normal discrete Matrix events. As explained
|
||
|
in the limitations, clients may be unaware of some relations using just these endpoints.
|
||
|
|
||
|
[MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) defines ways in
|
||
|
which the server may aid clients in processing relations by aggregating the
|
||
|
events.
|
||
|
|
||
|
### Redactions
|
||
|
|
||
|
Events with a relation may be redacted like any other event.
|
||
|
|
||
|
[MSC3389](https://github.com/matrix-org/matrix-doc/pull/3389) proposes that
|
||
|
the redaction algorithm should preserve the type and target id of a relation.
|
||
|
|
||
|
However, event relationships can still be used in existing room versions, but
|
||
|
the user experience may be worse if redactions are performed.
|
||
|
|
||
|
## Potential issues
|
||
|
|
||
|
### Federation considerations
|
||
|
|
||
|
We have a problem with resynchronising relations after a gap in federation:
|
||
|
We have no way of knowing that an edit happened in the gap to one of the events
|
||
|
we already have. So, we'll show inconsistent data until we backfill the gap.
|
||
|
* We could write this off as a limitation.
|
||
|
* Or we could make *ALL* relations a DAG, so we can spot holes at the next
|
||
|
relation, and go walk the DAG to pull in the missing relations? Then, the
|
||
|
next relation for an event could pull in any of the missing relations.
|
||
|
Socially this probably doesn't work as reactions will likely drop off over
|
||
|
time, so by the time your server comes back there won't be any more reactions
|
||
|
pulling the missing ones in.
|
||
|
* Could we also ask the server, after a gap, to provide all the relations which
|
||
|
happened during the gap to events whose root preceeded the gap.
|
||
|
* "Give me all relations which happened between this set of
|
||
|
forward-extremities when I lost sync, and the point i've rejoined the DAG,
|
||
|
for events which preceeded the gap"?
|
||
|
* Would be hard to auth all the relations which this api coughed up.
|
||
|
* We could auth them based only the auth events of the relation, except we
|
||
|
lose the context of the nearby DAG which we'd have if it was a normal
|
||
|
backfilled event.
|
||
|
* As a result it would be easier for a server to retrospectively lie about
|
||
|
events on behalf of its users.
|
||
|
* This probably isn't the end of the world, plus it's more likely to be
|
||
|
consistent than if we leave a gap.
|
||
|
* i.e. it's better to consistent with a small chance of being maliciously
|
||
|
wrong, than inconsistent with a guaranteed chance of being innocently
|
||
|
wrong.
|
||
|
* We'd need to worry about pagination.
|
||
|
* This is probably the best solution, but can also be added as a v2.
|
||
|
* In practice this seems to not be an issue, which is worth complicating
|
||
|
the s-s API over. Clients very rarely jump over the federation gap to an edit.
|
||
|
In most cases they scroll up, which backfills the server and we have all the
|
||
|
edits, when we reach the event before the gap.
|
||
|
|
||
|
## Limitations
|
||
|
|
||
|
Based solely on this MSC, relations are only received as discrete events in
|
||
|
the timeline, so clients may only have an incomplete image of all the relations
|
||
|
with an event if they do not fill gaps (syncs with a since token that have
|
||
|
`limited: true` set in the sync response for a room) in the timeline.
|
||
|
|
||
|
In practice, this has proven not to be too big of a problem, as reactions
|
||
|
(as proposed in [MSC 2677](https://github.com/matrix-org/matrix-doc/pull/2677))
|
||
|
tend to be posted close after the target event in the timeline.
|
||
|
|
||
|
A more complete solution to this has been deferred to
|
||
|
[MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675).
|
||
|
|
||
|
## Tradeoffs
|
||
|
|
||
|
### Event shape
|
||
|
|
||
|
Shape of
|
||
|
|
||
|
```json
|
||
|
"content": {
|
||
|
"m.relates_to": {
|
||
|
"m.reference": {
|
||
|
"event_id": "$another:event.com"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
versus
|
||
|
|
||
|
```json
|
||
|
"content": {
|
||
|
"m.relates_to": {
|
||
|
"rel_type": "m.reference",
|
||
|
"event_id": "$another:event.com"
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The reasons to go with `rel_type` is:
|
||
|
* This format is now in use in the wider matrix ecosystem without a prefix,
|
||
|
in spite of the original MSC 1849 not being merged. This situation is not ideal
|
||
|
but we still don't want to break compatibility with several clients.
|
||
|
* We don't need the extra indirection to let multiple relations apply to a given pair of
|
||
|
events, as that should be expressed as separate relation events.
|
||
|
* If we want 'adverbs' to apply to 'verbs' in the subject-verb-object triples which
|
||
|
relations form, then we apply it as mixins to the relation data itself rather than trying
|
||
|
to construct subject-verb-verb-object sentences.
|
||
|
* We decided to not adopt the format used by `m.in_reply_to` as it allows for multiple relations
|
||
|
and is hence overly flexible. Also, the relation type of `m.in_reply_to` is also overly specific
|
||
|
judged by the guidelines for `rel_type`s laid out in this MSC. Having replies use the same
|
||
|
format as relations is postponed to a later MSC, but it would likely involve replies
|
||
|
adopting the relation format with a more broadly useful `rel_type` (possibly the `m.reference`
|
||
|
type proposed in [MSC3267](https://github.com/matrix-org/matrix-doc/pull/3267)),
|
||
|
rather than relations adopting the replies format.
|
||
|
|
||
|
## Historical context
|
||
|
|
||
|
pik's MSC441 has:
|
||
|
|
||
|
Define the JSON schema for the aggregation event, so the server can work out
|
||
|
which fields should be aggregated.
|
||
|
|
||
|
```json
|
||
|
"type": "m.room._aggregation.emoticon",
|
||
|
"content": {
|
||
|
"emoticon": "::smile::",
|
||
|
"msgtype": "?",
|
||
|
"target_id": "$another:event.com"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
These would then be aggregated, based on target_id, and returned as annotations on
|
||
|
the source event in an `aggregation_data` field:
|
||
|
|
||
|
```json
|
||
|
"content": {
|
||
|
...
|
||
|
"aggregation_data": {
|
||
|
"m.room._aggregation.emoticon": {
|
||
|
"aggregation_data": [
|
||
|
{
|
||
|
"emoticon": "::smile::",
|
||
|
"event_id": "$14796538949JTYis:pik-test",
|
||
|
"sender": "@pik:pik-test"
|
||
|
}
|
||
|
],
|
||
|
"latest_event_id": "$14796538949JTYis:pik-test"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|