|
| 1 | +# MSC2674: Event relationships |
| 2 | + |
| 3 | +It's common to want to send events in Matrix which relate to existing events - |
| 4 | +for instance, reactions, edits and even replies/threads. |
| 5 | + |
| 6 | +This proposal is one in a series of proposals that defines a mechanism for |
| 7 | +events to relate to each other. Together, these proposals replace |
| 8 | +[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849). |
| 9 | + |
| 10 | +* This proposal defines a standard shape for indicating events which relate to |
| 11 | + other events. |
| 12 | +* [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) defines APIs to |
| 13 | + let the server calculate the aggregations on behalf of the client, and so |
| 14 | + bundle the related events with the original event where appropriate. |
| 15 | +* [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676) defines how |
| 16 | + users can edit messages using this mechanism. |
| 17 | +* [MSC2677](https://github.com/matrix-org/matrix-doc/pull/2677) defines how |
| 18 | + users can annotate events, such as reacting to events with emoji, using this |
| 19 | + mechanism. |
| 20 | +* [MSC3267](https://github.com/matrix-org/matrix-doc/pull/3267) defines how events |
| 21 | + can make a reference to other events. |
| 22 | +* [MSC3389](https://github.com/matrix-org/matrix-doc/pull/3389) defines changes to |
| 23 | + the redaction algorithm, to preserve the type and target id of a relation. |
| 24 | + |
| 25 | + |
| 26 | +## Proposal |
| 27 | + |
| 28 | +This proposal introduces the concept of relations, which can be used to |
| 29 | +associate new information with an existing event. |
| 30 | + |
| 31 | +A relationship is an object with a field `rel_type`, which is a string describing the type of relation, |
| 32 | +and a field `event_id`, which is a string that represents the event_id of the target event of this relation. |
| 33 | +The target event must exist in the same room as the relating event is sent. |
| 34 | +Both of those fields are required. An event is said to contain a relationship if its `content` contains |
| 35 | +a relationship with all the required fields under the `m.relates_to` key. If any of these conditions is not met, |
| 36 | +clients and servers should treat the event as if it does not contain a relationship. |
| 37 | +Servers should reject events not meeting these conditions with an HTTP 400 error when |
| 38 | +they are received via the client-server API. |
| 39 | + |
| 40 | +Here's a (partial) example of an event relating to another event: |
| 41 | + |
| 42 | +```json |
| 43 | +{ |
| 44 | + "content": { |
| 45 | + "m.relates_to": { |
| 46 | + "rel_type": "m.replace", |
| 47 | + "event_id": "$abc:server.tld" |
| 48 | + } |
| 49 | + } |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +All the information about the relationship lives under the `m.relates_to` key. |
| 54 | + |
| 55 | +If it helps, you can think of relations as a "subject verb object" triple, |
| 56 | +where the subject is the relation event itself; the verb is the `rel_type` |
| 57 | +field of the `m.relates_to` and the object is the `event_id` field. |
| 58 | + |
| 59 | +We consciously do not support multiple different relations within a single event, |
| 60 | +in order to keep the API simple. This means that if event A relates to event B |
| 61 | +in two different ways you would send two events to describe the two relations, |
| 62 | +rather than bundling them into a single event. Another MSC, |
| 63 | +like [MSC 3051](https://github.com/matrix-org/matrix-doc/pull/3051), |
| 64 | +can propose a change to add support for multiple relations if it turns out that |
| 65 | +this would facilitate certain use cases. |
| 66 | + |
| 67 | +Relations do not yet replace the |
| 68 | +[reply mechanism currently defined in the spec](https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies). |
| 69 | + |
| 70 | +### Relation types |
| 71 | + |
| 72 | +Any values for `rel_type` should abide the |
| 73 | +[general guidelines for identifiers](https://github.com/matrix-org/matrix-doc/pull/3171). |
| 74 | + |
| 75 | +The `rel_type` property determines how an event relates to another and can be used |
| 76 | +by clients to determine how and in what context a relation should be displayed. |
| 77 | + |
| 78 | +[MSC 2675](https://github.com/matrix-org/matrix-doc/pull/2675) proposes to also interpret the `rel_type` server-side. |
| 79 | + |
| 80 | +It is left up to the discretion of other MSCs building on this one whether they introduce |
| 81 | +`rel_type`s that are specific to their use case or that can serve a broad range |
| 82 | +of use cases. MSCs may define additional properties on the relation object for a given `rel_type`. |
| 83 | + |
| 84 | +Currently, a few `rel_type`s are already proposed. Here's a non-exhaustive list: |
| 85 | + |
| 86 | + - `m.replace` in [MSC 2676](https://github.com/matrix-org/matrix-doc/pull/2676). |
| 87 | + - `m.annotation` in [MSC 2677](https://github.com/matrix-org/matrix-doc/pull/2677). |
| 88 | + - `m.reference` in [MSC 3267](https://github.com/matrix-org/matrix-doc/pull/3267). |
| 89 | + - `m.thread` in [MSC 3440](https://github.com/matrix-org/matrix-doc/pull/3440). |
| 90 | + |
| 91 | + |
| 92 | +### Sending relations |
| 93 | + |
| 94 | +Related events are normal Matrix events, and can be sent by the normal `/send` |
| 95 | +API. |
| 96 | + |
| 97 | +The server should postprocess relations if needed before sending them into a |
| 98 | +room, as defined by the relationship type. For example, a relationship type |
| 99 | +might only allow a user to send one related message to a given event. |
| 100 | + |
| 101 | +### Receiving relations |
| 102 | + |
| 103 | +Relations are received like other non-state events, with `/sync`, |
| 104 | +`/messages` and `/context`, as normal discrete Matrix events. As explained |
| 105 | +in the limitations, clients may be unaware of some relations using just these endpoints. |
| 106 | + |
| 107 | +[MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) defines ways in |
| 108 | +which the server may aid clients in processing relations by aggregating the |
| 109 | +events. |
| 110 | + |
| 111 | +### Redactions |
| 112 | + |
| 113 | +Events with a relation may be redacted like any other event. |
| 114 | + |
| 115 | +[MSC3389](https://github.com/matrix-org/matrix-doc/pull/3389) proposes that |
| 116 | +the redaction algorithm should preserve the type and target id of a relation. |
| 117 | + |
| 118 | +However, event relationships can still be used in existing room versions, but |
| 119 | +the user experience may be worse if redactions are performed. |
| 120 | + |
| 121 | +## Potential issues |
| 122 | + |
| 123 | +### Federation considerations |
| 124 | + |
| 125 | +We have a problem with resynchronising relations after a gap in federation: |
| 126 | +We have no way of knowing that an edit happened in the gap to one of the events |
| 127 | +we already have. So, we'll show inconsistent data until we backfill the gap. |
| 128 | + * We could write this off as a limitation. |
| 129 | + * Or we could make *ALL* relations a DAG, so we can spot holes at the next |
| 130 | + relation, and go walk the DAG to pull in the missing relations? Then, the |
| 131 | + next relation for an event could pull in any of the missing relations. |
| 132 | + Socially this probably doesn't work as reactions will likely drop off over |
| 133 | + time, so by the time your server comes back there won't be any more reactions |
| 134 | + pulling the missing ones in. |
| 135 | + * Could we also ask the server, after a gap, to provide all the relations which |
| 136 | + happened during the gap to events whose root preceeded the gap. |
| 137 | + * "Give me all relations which happened between this set of |
| 138 | + forward-extremities when I lost sync, and the point i've rejoined the DAG, |
| 139 | + for events which preceeded the gap"? |
| 140 | + * Would be hard to auth all the relations which this api coughed up. |
| 141 | + * We could auth them based only the auth events of the relation, except we |
| 142 | + lose the context of the nearby DAG which we'd have if it was a normal |
| 143 | + backfilled event. |
| 144 | + * As a result it would be easier for a server to retrospectively lie about |
| 145 | + events on behalf of its users. |
| 146 | + * This probably isn't the end of the world, plus it's more likely to be |
| 147 | + consistent than if we leave a gap. |
| 148 | + * i.e. it's better to consistent with a small chance of being maliciously |
| 149 | + wrong, than inconsistent with a guaranteed chance of being innocently |
| 150 | + wrong. |
| 151 | + * We'd need to worry about pagination. |
| 152 | + * This is probably the best solution, but can also be added as a v2. |
| 153 | + * In practice this seems to not be an issue, which is worth complicating |
| 154 | + the s-s API over. Clients very rarely jump over the federation gap to an edit. |
| 155 | + In most cases they scroll up, which backfills the server and we have all the |
| 156 | + edits, when we reach the event before the gap. |
| 157 | + |
| 158 | +## Limitations |
| 159 | + |
| 160 | +Based solely on this MSC, relations are only received as discrete events in |
| 161 | +the timeline, so clients may only have an incomplete image of all the relations |
| 162 | +with an event if they do not fill gaps (syncs with a since token that have |
| 163 | +`limited: true` set in the sync response for a room) in the timeline. |
| 164 | + |
| 165 | +In practice, this has proven not to be too big of a problem, as reactions |
| 166 | +(as proposed in [MSC 2677](https://github.com/matrix-org/matrix-doc/pull/2677)) |
| 167 | +tend to be posted close after the target event in the timeline. |
| 168 | + |
| 169 | +A more complete solution to this has been deferred to |
| 170 | +[MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675). |
| 171 | + |
| 172 | +## Tradeoffs |
| 173 | + |
| 174 | +### Event shape |
| 175 | + |
| 176 | +Shape of |
| 177 | + |
| 178 | +```json |
| 179 | +"content": { |
| 180 | + "m.relates_to": { |
| 181 | + "m.reference": { |
| 182 | + "event_id": "$another:event.com" |
| 183 | + } |
| 184 | + } |
| 185 | +} |
| 186 | +``` |
| 187 | +versus |
| 188 | + |
| 189 | +```json |
| 190 | +"content": { |
| 191 | + "m.relates_to": { |
| 192 | + "rel_type": "m.reference", |
| 193 | + "event_id": "$another:event.com" |
| 194 | + } |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +The reasons to go with `rel_type` is: |
| 199 | + * This format is now in use in the wider matrix ecosystem without a prefix, |
| 200 | + in spite of the original MSC 1849 not being merged. This situation is not ideal |
| 201 | + but we still don't want to break compatibility with several clients. |
| 202 | + * We don't need the extra indirection to let multiple relations apply to a given pair of |
| 203 | + events, as that should be expressed as separate relation events. |
| 204 | + * If we want 'adverbs' to apply to 'verbs' in the subject-verb-object triples which |
| 205 | + relations form, then we apply it as mixins to the relation data itself rather than trying |
| 206 | + to construct subject-verb-verb-object sentences. |
| 207 | + * We decided to not adopt the format used by `m.in_reply_to` as it allows for multiple relations |
| 208 | + and is hence overly flexible. Also, the relation type of `m.in_reply_to` is also overly specific |
| 209 | + judged by the guidelines for `rel_type`s laid out in this MSC. Having replies use the same |
| 210 | + format as relations is postponed to a later MSC, but it would likely involve replies |
| 211 | + adopting the relation format with a more broadly useful `rel_type` (possibly the `m.reference` |
| 212 | + type proposed in [MSC3267](https://github.com/matrix-org/matrix-doc/pull/3267)), |
| 213 | + rather than relations adopting the replies format. |
| 214 | + |
| 215 | +## Historical context |
| 216 | + |
| 217 | +pik's MSC441 has: |
| 218 | + |
| 219 | +Define the JSON schema for the aggregation event, so the server can work out |
| 220 | +which fields should be aggregated. |
| 221 | + |
| 222 | +```json |
| 223 | +"type": "m.room._aggregation.emoticon", |
| 224 | +"content": { |
| 225 | + "emoticon": "::smile::", |
| 226 | + "msgtype": "?", |
| 227 | + "target_id": "$another:event.com" |
| 228 | +} |
| 229 | +``` |
| 230 | + |
| 231 | +These would then be aggregated, based on target_id, and returned as annotations on |
| 232 | +the source event in an `aggregation_data` field: |
| 233 | + |
| 234 | +```json |
| 235 | +"content": { |
| 236 | + ... |
| 237 | + "aggregation_data": { |
| 238 | + "m.room._aggregation.emoticon": { |
| 239 | + "aggregation_data": [ |
| 240 | + { |
| 241 | + "emoticon": "::smile::", |
| 242 | + "event_id": "$14796538949JTYis:pik-test", |
| 243 | + "sender": "@pik:pik-test" |
| 244 | + } |
| 245 | + ], |
| 246 | + "latest_event_id": "$14796538949JTYis:pik-test" |
| 247 | + } |
| 248 | + } |
| 249 | +} |
| 250 | +``` |
0 commit comments