Merge branch 'old_master' into travis/msc/inline-widgets
commit
2a26597c50
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1 @@
|
||||
Extend `/_matrix/client/r0/login` to accept a `m.login.appservice`, as per [MSC2778](https://github.com/matrix-org/matrix-doc/pull/2778).
|
@ -0,0 +1,151 @@
|
||||
# MSC2285: Private read receipts
|
||||
|
||||
Currently users must send read receipts in order to affect their notification
|
||||
counts, which alerts other people that the user has read their message. For
|
||||
primarily privacy reasons, it may be desirable to users to not advertise to
|
||||
others that they've read a message.
|
||||
|
||||
## Proposal
|
||||
|
||||
This MSC proposes adding a new `receiptType` (see [the receipts
|
||||
spec](https://spec.matrix.org/v1.3/client-server-api/#receipts)) of
|
||||
`m.read.private`. This `receiptType` is used when the user wants to affect their
|
||||
notification count but doesn't want other users to see their read receipt.
|
||||
|
||||
To move the user's private read receipt to `$123` the client can make a POST
|
||||
request to the [`/receipt`
|
||||
endpoint](https://spec.matrix.org/v1.3/client-server-api/#post_matrixclientv3roomsroomidreceiptreceipttypeeventid).
|
||||
For example:
|
||||
|
||||
```HTTP
|
||||
POST /_matrix/client/v3/rooms/!a:example.org/receipt/m.read.private/$123
|
||||
{}
|
||||
```
|
||||
|
||||
The MSC also proposes adding `m.fully_read` and `m.read.private` as a possible
|
||||
`receiptType` for `/receipt` to make this endpoint consistent with
|
||||
`/read_markers`. (we have two endpoints that do essentially the same thing, so
|
||||
it would make sense for them to be consistent)
|
||||
|
||||
Alternatively, the client can move the user's `m.fully_read` marker and/or
|
||||
`m.read` receipt at the same time as `m.read.private` by making a POST request
|
||||
to the [`/read_markers`
|
||||
endpoint](https://spec.matrix.org/v1.3/client-server-api/#post_matrixclientv3roomsroomidread_markers).
|
||||
For example:
|
||||
|
||||
```HTTP
|
||||
POST /_matrix/client/r0/rooms/!a:example.org/read_markers
|
||||
{
|
||||
"m.fully_read": "$123",
|
||||
"m.read": "$123",
|
||||
"m.read.private": "$123"
|
||||
}
|
||||
```
|
||||
|
||||
Both `m.read` and `m.read.private` clear notifications in the same way. If the
|
||||
user sent two receipts into a room, the later one should be the one that decides
|
||||
the notification count.
|
||||
|
||||
The receipt that is more "ahead" of the other takes precedence when considering
|
||||
notifications and a client's rendering of read receipts. This means that given
|
||||
an ordered set of events A, B, C, and D the public read receipt could be at
|
||||
point C, private at point A. If the user moves the private receipt from A to B
|
||||
then the user's notification count is still considered from point C as the public
|
||||
receipt is further ahead, still. Other users would also see the user's public read
|
||||
receipt as not having moved. The user can then move the private read receipt
|
||||
to point D, hopping over the public receipt, to change their notification count.
|
||||
|
||||
For clarity, if the public receipt is "fast forwarded" to be at the same position
|
||||
as the private receipt then the public receipt is broadcast to other users, even
|
||||
if previously considered private.
|
||||
|
||||
Note that like regular read receipts today, neither receipt can cause a backwards
|
||||
movement: both receipts can only move forwards, but do not have to be ahead of
|
||||
each other. It's valid to, for example, update a public read receipt which lags
|
||||
20 messages behind the private one.
|
||||
|
||||
The `m.fully_read` property is now optional for the [`/read_markers`
|
||||
endpoint](https://spec.matrix.org/v1.3/client-server-api/#post_matrixclientv3roomsroomidread_markers)
|
||||
as sometimes we only want to send `m.read.private`.
|
||||
|
||||
The MSC proposes that from now on, not all things sent over `/receipt` are
|
||||
federated. Servers MUST NOT send receipts of `receiptType` `m.read.private` to
|
||||
any other user than the sender. Servers also MUST NOT send receipts of
|
||||
`receiptType` `m.read.private` to any server over federation.
|
||||
|
||||
## Security considerations
|
||||
|
||||
Servers could act as if `m.read.private` is the same as `m.read` so the user
|
||||
must already trust the homeserver to a degree however, and the methods of
|
||||
notifying the user to the problem are difficult to implement. Users can always
|
||||
run their own homeservers to ensure it behaves correctly.
|
||||
|
||||
## Potential issues
|
||||
|
||||
Clients which support read receipts would end up rendering the user's receipt as
|
||||
jumping down when they send a message. This is no different from how IRC and
|
||||
similarly bridged users are perceived today.
|
||||
|
||||
## Alternatives
|
||||
|
||||
It has been suggested to use account data to store the setting that controls
|
||||
whether read receipts should be private on a per-account/per-room basis. While
|
||||
this might have some benefits, it is much less flexible.
|
||||
|
||||
Previous iterations of this MSC additionally suggested that having an `m.hidden`
|
||||
flag on existing read receipts could work, however this feels like assigning too
|
||||
much responsibility to an existing structure.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
While this MSC is not considered stable, implementations should use
|
||||
`org.matrix.msc2285` as a namespace.
|
||||
|
||||
|Stable (post-FCP)|Unstable |
|
||||
|-----------------|---------------------------------|
|
||||
|`m.read.private` |`org.matrix.msc2285.read.private`|
|
||||
|
||||
Clients should check for server support before sending private read receipts:
|
||||
if the server does not support them, then a private read receipt will not clear
|
||||
any notifications for the user.
|
||||
|
||||
The presence of `org.matrix.msc2285` or `org.matrix.msc2285.stable` in
|
||||
`unstable_features` is a reliable indication that a server supports private read
|
||||
receipts; however the converse is not true: their absence does not necessarily
|
||||
mean that the server does *not* support private read receipts. In particular,
|
||||
the server may have been updated to a future spec version which includes
|
||||
private read receipts, and hence removed the `unstable_features` entry.
|
||||
|
||||
Therefore, if a client has this feature enabled, but the server does not advertise
|
||||
support for this MSC in `unstable_features`, the client should either keep sending
|
||||
private read receipts with the risk that notifications will not be clearing, or it
|
||||
should warn the user and start sending public read receipts instead.
|
||||
|
||||
To mitigate this problem, once this MSC gets merged and once it becomes a part of a
|
||||
spec version, clients should update their implementations as fast as possible to
|
||||
accommodate the fact that the way of detecting server support will change: clients
|
||||
will now be looking for that spec version in `/versions`.
|
||||
|
||||
### While the MSC is unstable
|
||||
|
||||
During this period, to detect server support clients should check for the
|
||||
presence of the `org.matrix.msc2285` flag in `unstable_features` on `/versions`.
|
||||
Clients are also required to use the unstable prefixes (see [unstable
|
||||
prefix](#unstable-prefix)) during this time.
|
||||
|
||||
### Once the MSC is merged but not in a spec version
|
||||
|
||||
Once this MSC is merged, but is not yet part of the spec, clients should rely on
|
||||
the presence of the `org.matrix.msc2285.stable` flag in `unstable_features` to
|
||||
determine server support. If the flag is present, clients are required to use
|
||||
stable prefixes (see [unstable prefix](#unstable-prefix)).
|
||||
|
||||
### Once the MSC is in a spec version
|
||||
|
||||
Once this MSC becomes a part of a spec version, clients should rely on the
|
||||
presence of the spec version, that supports the MSC, in `versions` on
|
||||
`/versions`, to determine support. Servers are encouraged to keep the
|
||||
`org.matrix.msc2285.stable` flag around for a reasonable amount of time
|
||||
to help smooth over the transition for clients. "Reasonable" is intentionally
|
||||
left as an implementation detail, however the MSC process currently recommends
|
||||
*at most* 2 months from the date of spec release.
|
@ -0,0 +1,250 @@
|
||||
# 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,320 @@
|
||||
# MSC2675: Serverside aggregations of message relationships
|
||||
|
||||
It's common to want to send events in Matrix which relate to existing events -
|
||||
for instance, reactions, edits and even replies/threads.
|
||||
|
||||
Clients typically need to track the related events alongside the original
|
||||
event they relate to, in order to correctly display them. For instance,
|
||||
reaction events need to be aggregated together by summing and be shown next to
|
||||
the event they react to; edits need to be aggregated together by replacing the
|
||||
original event and subsequent edits, etc.
|
||||
|
||||
It is possible to treat relations as normal events and aggregate them
|
||||
clientside, but to do so comprehensively could be very resource intensive, as
|
||||
the client would need to spider all possible events in a room to find
|
||||
relationships and maintain a correct view.
|
||||
|
||||
Instead, this proposal seeks to solve this problem by defining APIs to let the
|
||||
server calculate the aggregations on behalf of the client, and so bundle the
|
||||
aggregated data with the original event where appropriate. It also proposes an
|
||||
API to let clients paginate through all relations of an event.
|
||||
|
||||
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).
|
||||
|
||||
* [MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674) defines a
|
||||
standard shape for indicating events which relate to other events.
|
||||
* This proposal defines APIs to let the server calculate the aggregations on
|
||||
behalf of the client, and so bundle the aggregated data 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.
|
||||
|
||||
## Proposal
|
||||
|
||||
### Aggregations
|
||||
|
||||
Relation events can be aggregated per relation type by the server.
|
||||
The format of the aggregated value (hereafter called "aggregation")
|
||||
depends on the relation type.
|
||||
|
||||
Some relation types might group the aggregations by the `key` property
|
||||
in the relation and aggregate to an array, while
|
||||
others might aggregate to a single object or any other value really.
|
||||
|
||||
Here are some non-normative examples of what aggregations can look like:
|
||||
|
||||
Example aggregation for [`m.thread`](https://github.com/matrix-org/matrix-doc/pull/3440) (which
|
||||
aggregates all relations into a single object):
|
||||
```
|
||||
{
|
||||
"latest_event": {
|
||||
"content": { ... },
|
||||
...
|
||||
},
|
||||
"count": 7,
|
||||
"current_user_participated": true
|
||||
}
|
||||
```
|
||||
|
||||
Example aggregation for [`m.annotation`](https://github.com/matrix-org/matrix-doc/pull/2677) (which
|
||||
aggregates relations into a list of objects, grouped by `key`).
|
||||
```
|
||||
[
|
||||
{
|
||||
"key": "👍",
|
||||
"origin_server_ts": 1562763768320,
|
||||
"count": 3
|
||||
},
|
||||
{
|
||||
"key": "👎",
|
||||
"origin_server_ts": 1562763768320,
|
||||
"count": 2
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Bundling
|
||||
|
||||
Other than during non-gappy incremental syncs, timeline events that have other
|
||||
events relate to them should include the aggregation of those related events
|
||||
in the `m.relations` property of their unsigned data. This process is
|
||||
referred to as "bundling", and the aggregated relations included via
|
||||
this mechanism are called "bundled aggregations".
|
||||
|
||||
By sending a summary of the relations, bundling
|
||||
avoids us having to always send lots of individual relation events
|
||||
to the client.
|
||||
|
||||
Aggregations are never bundled into state events. This is a current
|
||||
implementation detail that could be revisited later,
|
||||
rather than a specific design decision.
|
||||
|
||||
The following client-server APIs should bundle aggregations
|
||||
with events they return:
|
||||
|
||||
- `GET /rooms/{roomId}/messages`
|
||||
- `GET /rooms/{roomId}/context/{eventId}`
|
||||
- `GET /rooms/{roomId}/event/{eventId}`
|
||||
- `GET /sync`, only for room sections in the response where `limited` field
|
||||
is `true`; this amounts to all rooms in the response if
|
||||
the `since` request parameter was not passed, also known as an initial sync.
|
||||
- `GET /relations`, as proposed in this MSC.
|
||||
|
||||
Deprecated APIs like `/initialSync` and `/events/{eventId}` are *not* required
|
||||
to bundle aggregations.
|
||||
|
||||
The bundled aggregations are grouped according to their relation type.
|
||||
The format of `m.relations` (here with *non-normative* examples of
|
||||
the [`m.replace`](https://github.com/matrix-org/matrix-doc/pull/2676) and
|
||||
[`m.annotation`](https://github.com/matrix-org/matrix-doc/pull/2677) relation
|
||||
types) is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_id": "abc",
|
||||
"unsigned": {
|
||||
"m.relations": {
|
||||
"m.annotation": {
|
||||
"key": "👍",
|
||||
"origin_server_ts": 1562763768320,
|
||||
"count": 3
|
||||
},
|
||||
"m.replace": {
|
||||
"event_id": "$edit_event_id",
|
||||
"origin_server_ts": 1562763768320,
|
||||
"sender": "@alice:localhost"
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Client-side aggregation
|
||||
|
||||
Bundled aggregations on an event give a snapshot of what relations were known
|
||||
at the time the event was received. When relations are received through `/sync`,
|
||||
clients should locally aggregate (as they might have done already before
|
||||
supporting this MSC) the relation on top of any bundled aggregation the server
|
||||
might have sent along previously with the target event, to get an up to date
|
||||
view of the aggregations for the target event. The aggregation algorithm is the
|
||||
same as the one described here for the server.
|
||||
|
||||
### Querying relations
|
||||
|
||||
A single event can have lots of associated relations, and we do not want to
|
||||
overload the client by, for example, including them all bundled with the
|
||||
related-to event. Instead, we also provide a new `/relations` API in
|
||||
order to paginate over the relations, which behaves in a similar way to
|
||||
`/messages`, except using `next_batch` and `prev_batch` names
|
||||
(in line with `/sync` API). Tokens from `/sync` or `/messages` can be
|
||||
passed to `/relations` to only get relating events from a section of
|
||||
the timeline.
|
||||
|
||||
The `/relations` API returns the discrete relation events
|
||||
associated with an event that the server is aware of
|
||||
in standard topological order. Note that events may be missing,
|
||||
see [limitations](#servers-might-not-be-aware-of-all-relations-of-an-event).
|
||||
You can optionally filter by a given relation type and the event type of the
|
||||
relating event:
|
||||
|
||||
```
|
||||
GET /_matrix/client/v1/rooms/{roomID}/relations/{event_id}[/{rel_type}[/{event_type}]][?from=token][&to=token][&limit=amount]
|
||||
```
|
||||
|
||||
```
|
||||
{
|
||||
"chunk": [
|
||||
{
|
||||
"type": "m.reaction",
|
||||
"sender": "...",
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.annotation",
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"prev_batch": "some_token",
|
||||
"next_batch": "some_token",
|
||||
}
|
||||
```
|
||||
|
||||
The endpoint does not have any trailing slashes. It requires authentication
|
||||
and is not rate-limited.
|
||||
|
||||
The `from` and `limit` query parameters are used for pagination, and work
|
||||
just like described for the `/messages` endpoint.
|
||||
|
||||
Note that [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676)
|
||||
adds the related-to event in `original_event` property of the response.
|
||||
This way the full history (e.g. also the first, original event) of the event
|
||||
is obtained without further requests. See that MSC for further details.
|
||||
|
||||
### End to end encryption
|
||||
|
||||
Since the server has to be able to aggregate relation events, structural
|
||||
information about relations must be visible to the server, and so the
|
||||
`m.relates_to` field must be included in the plaintext.
|
||||
|
||||
A future MSC may define a method for encrypting certain parts of the
|
||||
`m.relates_to` field that may contain sensitive information.
|
||||
|
||||
### Redactions
|
||||
|
||||
Redacted relations should not be taken into consideration in
|
||||
bundled aggregations, nor should they be returned from `/relations`.
|
||||
|
||||
Requesting `/relations` on a redacted event should
|
||||
still return any existing relation events.
|
||||
This is in line with other APIs like `/context` and `/messages`.
|
||||
|
||||
### Local echo
|
||||
|
||||
For the best possible user experience, clients should also include unsent
|
||||
relations into the client-side aggregation. When adding a relation to the send
|
||||
queue, clients should locally aggregate it into the relations of the target
|
||||
event, ideally regardless of the target event having received an `event_id`
|
||||
already or still being pending. If the client gives up on sending the relation
|
||||
for some reason, the relation should be de-aggregated from the relations of
|
||||
the target event. If the client offers the user a possibility of manually
|
||||
retrying to send the relation, it should be re-aggregated when the user does so.
|
||||
|
||||
De-aggregating a relation refers to rerunning the aggregation for a given
|
||||
target event while not considering the de-aggregated event any more.
|
||||
|
||||
Upon receiving the remote echo for any relations, a client is likely to remove
|
||||
the pending event from the send queue. Here, it should also de-aggregate the
|
||||
pending event from the target event's relations, and re-aggregate the received
|
||||
remote event from `/sync` to make sure the client-side aggregation happens with
|
||||
the same event data as on the server.
|
||||
|
||||
When adding a redaction for a relation to the send queue, the relation
|
||||
referred to should be de-aggregated from the relations of the target of the
|
||||
relation. Similar to a relation, when the sending of the redaction fails or
|
||||
is cancelled, the relation should be aggregated again.
|
||||
|
||||
If the target event is still pending and hasn't received its `event_id` yet,
|
||||
clients can locally relate relation events to their target by using
|
||||
`transaction_id` like they already do for detecting remote echos when sending
|
||||
events.
|
||||
|
||||
## Edge cases
|
||||
|
||||
### How do you handle ignored users?
|
||||
|
||||
* Information about relations sent from ignored users must never be sent to
|
||||
the client, either in aggregations or discrete relation events.
|
||||
This is to let you block someone from harassing you with emoji reactions
|
||||
(or using edits as a side-channel to harass you). Therefore, it is possible
|
||||
that different users will see different aggregations (a different last edit,
|
||||
or a different reaction count) on an event.
|
||||
|
||||
## Limitations
|
||||
|
||||
### Relations can be missed while not being in the room
|
||||
|
||||
Relation events behave no different from other events in terms of room history visibility,
|
||||
which means that some relations might not be visible to a user while they are not invited
|
||||
or have not joined the room. This can cause a user to see an incomplete edit history or reaction count
|
||||
based on discrete relation events upon (re)joining a room.
|
||||
|
||||
Ideally the server would not include these events in aggregations, as it would mean breaking
|
||||
the room history visibility rules, but this MSC defers addressing this limitation and
|
||||
specifying the exact server behaviour to [MSC3570](https://github.com/matrix-org/matrix-doc/pull/3570).
|
||||
|
||||
### Servers might not be aware of all relations of an event
|
||||
|
||||
The response of `/relations` might be incomplete because the homeserver
|
||||
potentially doesn't have the full DAG of the room. The federation API doens't
|
||||
have an equivalent of the `/relations` API, so has no way but to fetch the
|
||||
full DAG over federation to assure itself that it is aware of all relations.
|
||||
|
||||
[MSC2836](https://github.com/matrix-org/matrix-doc/pull/2836) provided a proposal for following relationships over federation in the "Querying relationships over federation" section via a `/_matrix/federation/v1/event_relationships` API
|
||||
|
||||
### Event type based aggregation and filtering won't work well in encrypted rooms
|
||||
|
||||
The `/relations` endpoint allows filtering by event type,
|
||||
which for encrypted rooms will be `m.room.encrypted`, rendering this filtering
|
||||
less useful for encrypted rooms. Aggregation algorithms that take the type of
|
||||
the relating events they aggregate into account will suffer from the same
|
||||
limitation.
|
||||
|
||||
## Future extensions
|
||||
|
||||
### Handling limited (gappy) syncs
|
||||
|
||||
For the special case of a gappy incremental sync, many relations (particularly
|
||||
reactions) may have occurred during the gap. It would be inefficient to send
|
||||
each one individually to the client, but it would also be inefficient to send
|
||||
all possible bundled aggregations to the client.
|
||||
|
||||
The server could tell the client the event IDs of events which
|
||||
predate the gap which received relations during the gap. This means that the
|
||||
client could invalidate its copy of those events (if any) and then requery them
|
||||
(including their bundled relations) from the server if/when needed,
|
||||
for example using an extension of the `/event` API for batch requests.
|
||||
|
||||
The server could do this with a new `stale_events` field of each room object
|
||||
in the sync response. The `stale_events` field would list all the event IDs
|
||||
prior to the gap which had updated relations during the gap. The event IDs
|
||||
would be grouped by relation type,
|
||||
and paginated as per the normal Matrix pagination model.
|
||||
|
||||
This was originally part of this MSC but left out to limit the scope
|
||||
to what is implemented at the time of writing.
|
||||
|
||||
## Prefix
|
||||
|
||||
While this MSC is not considered stable, the endpoints become:
|
||||
|
||||
- `GET /_matrix/client/unstable/rooms/{roomID}/relations/{eventID}[/{relationType}[/{eventType}]]`
|
||||
|
||||
None of the newly introduced identifiers should use a prefix though, as this MSC
|
||||
tries to document relation support already being used in
|
||||
the wider matrix ecosystem.
|
@ -0,0 +1,407 @@
|
||||
# MSC2676: Message editing
|
||||
|
||||
Users may wish to edit previously sent messages, for example to correct typos.
|
||||
This can be done by sending a new message with an indication that it replaces
|
||||
the previously sent message.
|
||||
|
||||
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).
|
||||
|
||||
* [MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674) 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.
|
||||
* This proposal 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.
|
||||
|
||||
## Background
|
||||
|
||||
Element-Web (then Riot-Web) and Synapse both implemented initial support for
|
||||
message editing, following the proposals of MSC1849, in May 2019
|
||||
([matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk/pull/2952),
|
||||
[synapse](https://github.com/matrix-org/synapse/pull/5209)). Element-Android
|
||||
and Element-iOS also added implementations around that time. Unfortunately,
|
||||
those implementations presented the feature as "production-ready", despite it
|
||||
not yet having been adopted into the Matrix specification.
|
||||
|
||||
The current situation is therefore that client or server implementations hoping
|
||||
to interact with Element users must simply follow the examples of that
|
||||
implementation. In other words, message edits form part of the *de-facto* spec
|
||||
despite not being formalised in the written spec. This is clearly a regrettable
|
||||
situation. Hopefully, processes have improved over the last three years so that
|
||||
this situation will not arise again. Nevertheless there is little we can do
|
||||
now other than formalise the status quo.
|
||||
|
||||
This MSC, along with the others mentioned above, therefore seeks primarily to
|
||||
do that. 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.
|
||||
|
||||
## Proposal
|
||||
|
||||
### `m.replace` event relationship type
|
||||
|
||||
A new `rel_type` of `m.replace` is defined for use with the `m.relates_to`
|
||||
field as defined in
|
||||
[MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674). This is
|
||||
intended primarily for handling edits, and lets you define an event which
|
||||
replaces an existing event.
|
||||
|
||||
Such an event, with `rel_type: m.replace`, is referred to as a "message edit event".
|
||||
|
||||
### `m.new_content` property
|
||||
|
||||
The `content` of a message edit event must contain a `m.new_content` property
|
||||
which defines the replacement content. (This allows the normal `body` fields to
|
||||
be used for a fallback for clients who do not understand replacement events.)
|
||||
|
||||
For instance, an `m.room.message` which replaces an existing event might look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"content": {
|
||||
"body": "* Hello! My name is bar",
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
"body": "Hello! My name is bar",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": "$some_event_id"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `m.new_content` can include any properties that would normally be found in
|
||||
an event's `content` property, such as `formatted_body`.
|
||||
|
||||
#### Encrypted events
|
||||
|
||||
If the original event was encrypted, the replacement should be too. In that
|
||||
case, `m.new_content` is placed in the `content` of the encrypted payload. The
|
||||
`m.relates_to` property remains unencrypted, as required by the
|
||||
[relationships](https://spec.matrix.org/v1.3/client-server-api/#forming-relationships-between-events)
|
||||
section of the Client-Server API specification.
|
||||
|
||||
For example, an encrypted replacement event might look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.room.encrypted",
|
||||
"content": {
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": "$some_event_id"
|
||||
},
|
||||
"algorithm": "m.megolm.v1.aes-sha2",
|
||||
"sender_key": "<sender_curve25519_key>",
|
||||
"device_id": "<sender_device_id>",
|
||||
"session_id": "<outbound_group_session_id>",
|
||||
"ciphertext": "<encrypted_payload_base_64>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
... and, once decrypted, the payload might look like this:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.room.<event_type>",
|
||||
"room_id": "!some_room_id",
|
||||
"content": {
|
||||
"body": "* Hello! My name is bar",
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
"body": "Hello! My name is bar",
|
||||
"msgtype": "m.text"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that:
|
||||
* There is no `m.relates_to` property in the encrypted payload. (Any such
|
||||
property would be ignored.)
|
||||
* There is no `m.new_content` property in the cleartext `content` of the
|
||||
`m.room.encrypted` event. (Again, any such property would be ignored.)
|
||||
|
||||
For clarity: the payload must be encrypted as normal, ratcheting the Megolm session
|
||||
as normal. The original Megolm ratchet entry should **not** be re-used.
|
||||
|
||||
#### Applying `m.new_content`
|
||||
|
||||
When applying a replacement, the `content` property of the original event is
|
||||
replaced entirely by the `m.new_content`, with the exception of `m.relates_to`,
|
||||
which is left *unchanged*. Any `m.relates_to` property within `m.new_content`
|
||||
is ignored.
|
||||
|
||||
For example, given a pair of events:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_id": "$original_event",
|
||||
"type": "m.room.message",
|
||||
"content": {
|
||||
"body": "I *really* like cake",
|
||||
"msgtype": "m.text",
|
||||
"formatted_body": "I <em>really</em> like cake",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"event_id": "$edit_event",
|
||||
"type": "m.room.message",
|
||||
"content": {
|
||||
"body": "* I *really* like *chocolate* cake",
|
||||
"msgtype": "m.text",
|
||||
"m.new_content": {
|
||||
"body": "I *really* like *chocolate* cake",
|
||||
"msgtype": "m.text",
|
||||
"com.example.extension_property": "chocolate"
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": "$original_event_id"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
... then the end result is an event as shown below. Note that `formatted_body`
|
||||
is now absent, because it was absent in the replacement event, but
|
||||
`m.relates_to` remains unchanged (ie, absent).
|
||||
|
||||
```json
|
||||
{
|
||||
"event_id": "$original_event",
|
||||
"type": "m.room.message",
|
||||
"content": {
|
||||
"body": "I *really* like *chocolate* cake",
|
||||
"msgtype": "m.text",
|
||||
"com.example.extension_property": "chocolate"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that the `msgtype` property of `m.room.message` events need not be the
|
||||
same as in the original event. For example, if a user intended to send a
|
||||
message beginning with "/me", but their client sends an `m.emote` event
|
||||
instead, they could edit the message to send be an `m.text` event as they had
|
||||
originally intended.
|
||||
|
||||
### Validity of message edit events
|
||||
|
||||
Some message edit events are defined to be invalid. To be considered valid, all
|
||||
of the following criteria must be satisfied:
|
||||
|
||||
* The replacement and original events must have the same `type`.
|
||||
* Neither the replacement nor original events can be state events (ie, neither
|
||||
may have a `state_key`).
|
||||
* The original event must not, itself, have a `rel_type` of `m.replace`.
|
||||
* The original event and replacement event must have the same `sender`.
|
||||
* The replacement event (once decrypted, if appropriate) must have an
|
||||
`m.new_content` property.
|
||||
|
||||
The original event and replacement event must also have the same `room_id`, as
|
||||
required by the
|
||||
[relationships](https://spec.matrix.org/v1.3/client-server-api/#forming-relationships-between-events)
|
||||
section of the Client-Server API specification.
|
||||
|
||||
If any of these criteria are not satisfied, implementations should ignore the
|
||||
replacement event (the content of the original should not be replaced, and the
|
||||
edit should not be included in the server-side aggregation).
|
||||
|
||||
### Server behaviour
|
||||
|
||||
#### Server-side aggregation of `m.replace` relationships
|
||||
|
||||
Note that there can be multiple events with an `m.replace` relationship to a
|
||||
given event (for example, if an event is edited multiple times). These should
|
||||
be [aggregated](https://spec.matrix.org/v1.3/client-server-api/#aggregations)
|
||||
by the homeserver.
|
||||
|
||||
The format of the aggregation for `m.replace` simply gives gives the
|
||||
`event_id`, `origin_server_ts`, and `sender` of the most recent replacement
|
||||
event (as determined by `origin_server_ts`, falling back to a lexicographic
|
||||
ordering of `event_id`).
|
||||
|
||||
This aggregation is bundled into the `unsigned/m.relations` property of any
|
||||
event that is the target of an `m.replace` relationship. For example:
|
||||
|
||||
```json5
|
||||
|
||||
{
|
||||
"event_id": "$original_event_id",
|
||||
// ...
|
||||
"unsigned": {
|
||||
"m.relations": {
|
||||
"m.replace": {
|
||||
"event_id": "$latest_edit_event_id",
|
||||
"origin_server_ts": 1649772304313,
|
||||
"sender": "@editing_user:localhost"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the original event is redacted, any `m.replace` relationship should **not**
|
||||
be bundled with it (whether or not any subsequent edits are themselves
|
||||
redacted). Note that this behaviour is specific to the `m.replace`
|
||||
relationship.
|
||||
|
||||
#### Server-side replacement of content
|
||||
|
||||
Whenever an `m.replace` is to be bundled with an event as above, the server should
|
||||
also modify the `content` of the original event according
|
||||
to the `m.new_content` of the most recent edit (determined as above).
|
||||
|
||||
An exception applies to [`GET
|
||||
/_matrix/client/v3/rooms/{roomId}/event/{eventId}`](https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomideventeventid),
|
||||
which should return the *unmodified* event (though the relationship should
|
||||
still be bundled, as described above).
|
||||
|
||||
The endpoints where this behaviour takes place is the same as those where
|
||||
aggregations are bundled, with the exception of
|
||||
`/room/{roomId}/event/{eventId}`. This includes:
|
||||
|
||||
* `GET /rooms/{roomId}/messages`
|
||||
* `GET /rooms/{roomId}/context/{eventId}`
|
||||
* `GET /rooms/{roomId}/relations/{eventId}`
|
||||
* `GET /rooms/{roomId}/relations/{eventId}/{relType}`
|
||||
* `GET /rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
||||
* `GET /sync` when the relevant section has a `limited` value of `true`
|
||||
* `POST /search` for any matching events under `room_events`.
|
||||
|
||||
### Client behaviour
|
||||
|
||||
Clients can often ignore message edit events, since any events the server
|
||||
returns via the C-S API will be updated by the server to account for subsequent
|
||||
edits.
|
||||
|
||||
However, clients should apply the replacement themselves when the server is
|
||||
unable to do so. This happens in the following situations:
|
||||
|
||||
1. The client has already received and stored the original event before the message
|
||||
edit event arrives.
|
||||
|
||||
2. The original event (and hence its replacement) are encrypted.
|
||||
|
||||
Client authors are reminded to take note of the requirements for [Validity of
|
||||
message edit events](#validity-of-message-edit-events), and to ignore any
|
||||
invalid edit events that may be received.
|
||||
|
||||
### Permalinks
|
||||
|
||||
Permalinks to edited events should capture the event ID that the creator of the
|
||||
permalink is viewing at that point (which might be a message edit event).
|
||||
|
||||
The client viewing the permalink should resolve this ID to the original event
|
||||
ID, and then display the most recent version of that event.
|
||||
|
||||
### Redactions
|
||||
|
||||
When a message using a `rel_type` of `m.replace` is redacted, it removes that
|
||||
edit revision. This has little effect if there were subsequent edits, however
|
||||
if it was the most recent edit, the event is in effect reverted to its content
|
||||
before the redacted edit.
|
||||
|
||||
Redacting the original message in effect removes the message, including all
|
||||
subsequent edits, from the visible timeline. In this situation, homeservers
|
||||
will return an empty `content` for the original event as with any other
|
||||
redacted event. It must be noted that, although they are not immediately
|
||||
visible in Element, subsequent edits remain unredacted and can be seen via API
|
||||
calls. See [Future considerations](#future-considerations).
|
||||
|
||||
### Edits of replies
|
||||
|
||||
Some particular constraints apply to events which replace a
|
||||
[reply](https://spec.matrix.org/v1.3/client-server-api/#rich-replies). In
|
||||
particular:
|
||||
|
||||
* There should be no `m.in_reply_to` property in the the `m.relates_to`
|
||||
object, since it would be redundant (see [Applying
|
||||
`m.new_content`](#applying-mnew_content) above, which notes that the original
|
||||
event's `m.relates_to` is preserved), as well as being contrary to the
|
||||
spirit of
|
||||
[MSC2674](https://github.com/matrix-org/matrix-spec-proposals/pull/2674)
|
||||
which expects only one relationship per event.
|
||||
|
||||
* `m.new_content` should **not** contain any ["reply
|
||||
fallback"](https://spec.matrix.org/v1.3/client-server-api/#fallbacks-for-rich-replies),
|
||||
since it is assumed that any client which can handle edits can also
|
||||
display replies natively.
|
||||
|
||||
An example of an edit to a reply is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"content": {
|
||||
"body": "> <@richvdh:sw1v.org> ab\n\n * ef",
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!qOZKfwKPirAoSosXrf:matrix.org/$1652807718765kOVDf:sw1v.org?via=matrix.org&via=sw1v.org\">In reply to</a> <a href=\"https://matrix.to/#/@richvdh:sw1v.org\">@richvdh:sw1v.org</a><br>ab</blockquote></mx-reply> * ef",
|
||||
"m.new_content": {
|
||||
"body": "ef",
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "ef"
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": "$original_reply_event"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Future considerations
|
||||
|
||||
### Ordering of edits
|
||||
|
||||
In future we may wish to consider ordering replacements (or relations in
|
||||
general) via a DAG rather than using `origin_server_ts` to determine ordering -
|
||||
particularly to mitigate potential abuse of edits applied by moderators.
|
||||
Whatever, care must be taken by the server to ensure that if there are multiple
|
||||
replacement events, the server must consistently choose the same one as all
|
||||
other servers.
|
||||
|
||||
### Redaction of edits
|
||||
|
||||
It is highly unintuitive that redacting the original event leaves subsequent
|
||||
edits visible to curious eyes even though they are hidden from the
|
||||
timeline. This is considered a bug which this MSC makes no attempt to
|
||||
resolve. See also
|
||||
[element-web#11978](https://github.com/vector-im/element-web/issues/11978) and
|
||||
[synapse#5594](https://github.com/matrix-org/synapse/issues/5594).
|
||||
|
||||
### Edits to state events
|
||||
|
||||
There are various issues which would need to be resolved before edits to state
|
||||
events could be supported. In particular, we would need to consider how the
|
||||
semantically-meaningful fields of the content of a state event relate to
|
||||
`m.new_content`. Variation between implementations could easily lead to
|
||||
security problems (See
|
||||
[element-web#21851](https://github.com/vector-im/element-web/issues/21851) for
|
||||
example.)
|
||||
|
||||
### Editing other users' events
|
||||
|
||||
There is a usecase for users with sufficient power-level to edit other peoples'
|
||||
events. For now, no attempt is made to support this. If it is supported in the
|
||||
future, we would need to find a way to make it clear in the timeline.
|
@ -0,0 +1,28 @@
|
||||
# MSC2832: Homeserver -> Application Service authorization header
|
||||
Most of the auth tokens in the spec are passed in the `Authorization` header,
|
||||
with the `access_token` query parameter supported for backwards-compatibility.
|
||||
For some reason, the application service spec was not updated in the same way
|
||||
and it still requires using the archaic query parameter when the homeserver
|
||||
pushes transactions to the appservice.
|
||||
|
||||
## Proposal
|
||||
The `access_token` query parameter is removed from all requests made by the
|
||||
homeserver to appservice and is replaced with the `Authorization` header with
|
||||
`Bearer <token>` as the value.
|
||||
|
||||
### Backwards-compatibility
|
||||
Homeservers which want to support old spec versions in the appservice API may
|
||||
send both the query parameter and header. Similarly, appservices may accept the
|
||||
token from either source.
|
||||
|
||||
## Security considerations
|
||||
Not fixing this causes access tokens to be logged in many bridges.
|
||||
|
||||
## Alternatives
|
||||
We could add a way for appservices to explicitly specify which spec version
|
||||
they want in order to implement backwards-compatibility without sending both
|
||||
tokens.
|
||||
|
||||
## Unstable prefix
|
||||
The authorization header is already used in the client-server spec, and an
|
||||
unstable prefix would just unnecessarily complicate things.
|
@ -0,0 +1,389 @@
|
||||
# MSC2946: Spaces Summary
|
||||
|
||||
This MSC depends on [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772), which
|
||||
describes why a Space is useful:
|
||||
|
||||
> Collecting rooms together into groups is useful for a number of purposes. Examples include:
|
||||
>
|
||||
> * Allowing users to discover different rooms related to a particular topic: for example "official matrix.org rooms".
|
||||
> * Allowing administrators to manage permissions across a number of rooms: for example "a new employee has joined my company and needs access to all of our rooms".
|
||||
> * Letting users classify their rooms: for example, separating "work" from "personal" rooms.
|
||||
>
|
||||
> We refer to such collections of rooms as "spaces".
|
||||
|
||||
This MSC attempts to solve how a member of a space discovers rooms in that space. This
|
||||
is useful for quickly exposing a user to many aspects of an entire community, using the
|
||||
examples above, joining the "official matrix.org rooms" space might suggest joining a few
|
||||
rooms:
|
||||
|
||||
* A room to discuss development of the Matrix Spec.
|
||||
* An announcements room for news related to matrix.org.
|
||||
* An off-topic room for members of the space.
|
||||
|
||||
## Proposal
|
||||
|
||||
A new client-server API (and corresponding server-server API) is added which allows
|
||||
for querying for the rooms and spaces contained within a space. This allows a client
|
||||
to efficiently display a hierarchy of rooms to a user (i.e. without having
|
||||
to walk the full state of each room).
|
||||
|
||||
### Client-server API
|
||||
|
||||
An endpoint is provided to walk the space tree, starting at the provided room ID
|
||||
("the root room"), and visiting other rooms/spaces found via `m.space.child`
|
||||
events. It recurses into the children and into their children, etc.
|
||||
|
||||
Any child room that the user is joined or is potentially joinable (per
|
||||
[MSC3173](https://github.com/matrix-org/matrix-doc/pull/3173)) is included in
|
||||
the response. When a room with a `type` of `m.space` is found, it is searched
|
||||
for valid `m.space.child` events to recurse into.
|
||||
|
||||
In order to provide a consistent experience, the space tree should be walked in
|
||||
a depth-first manner, e.g. whenever a space is found it should be recursed into
|
||||
by sorting the children rooms and iterating through them.
|
||||
|
||||
There could be loops in the returned child events; clients and servers should
|
||||
handle this gracefully. Similarly, note that a child room might appear multiple
|
||||
times (e.g. also be a grandchild). Clients and servers should handle this
|
||||
appropriately.
|
||||
|
||||
This endpoint requires authentication and is subject to rate-limiting.
|
||||
|
||||
#### Request format
|
||||
|
||||
```text
|
||||
GET /_matrix/client/v1/rooms/{roomID}/hierarchy
|
||||
```
|
||||
|
||||
Query Parameters:
|
||||
|
||||
* **`suggested_only`**: Optional. If `true`, return only child events and rooms
|
||||
where the `m.space.child` event has `suggested: true`. Must be a boolean,
|
||||
defaults to `false`.
|
||||
|
||||
This applies transitively, i.e. if a `suggested_only` is `true` and a space is
|
||||
not suggested then it should not be searched for children. The inverse is also
|
||||
true, if a space is suggested, but a child of that space is not then the child
|
||||
should not be included.
|
||||
* **`limit`**: Optional: a client-defined limit to the maximum
|
||||
number of rooms to return per page. Must an integer greater than zero.
|
||||
|
||||
Server implementations should impose a maximum value to avoid resource
|
||||
exhaustion.
|
||||
* **`max_depth`**: Optional: The maximum depth in the tree (from the root room)
|
||||
to return. The deepest depth returned will not include children events. Defaults
|
||||
to no-limit. Must be a non-negative integer.
|
||||
|
||||
Server implementations may wish to impose a maximum value to avoid resource
|
||||
exhaustion.
|
||||
* **`from`**: Optional. Pagination token given to retrieve the next set of rooms.
|
||||
|
||||
Note that if a pagination token is provided, then the parameters given for
|
||||
`suggested_only` and `max_depth` must be the same.
|
||||
|
||||
#### Response Format
|
||||
|
||||
* **`rooms`**: `[object]` For each room/space, starting with the root room, a
|
||||
summary of that room. The fields are the same as those returned by
|
||||
`/publicRooms` (see
|
||||
[spec](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-publicrooms)),
|
||||
with the addition of:
|
||||
* **`room_type`**: the value of the `m.type` field from the room's
|
||||
`m.room.create` event, if any.
|
||||
* **`children_state`**: The stripped state of the `m.space.child` events of
|
||||
the room per [MSC3173](https://github.com/matrix-org/matrix-doc/pull/3173).
|
||||
In addition to the standard stripped state fields, the following is included:
|
||||
* **`origin_server_ts`**: `integer`. The `origin_server_ts` field from the
|
||||
room's `m.space.child` event. This is required for sorting of rooms as
|
||||
specified below.
|
||||
* **`next_batch`**: Optional `string`. The token to supply in the `from` param
|
||||
of the next `/hierarchy` request in order to request more rooms. If this is absent,
|
||||
there are no more results.
|
||||
|
||||
#### Example request:
|
||||
|
||||
```text
|
||||
GET /_matrix/client/v1/rooms/%21ol19s%3Ableecker.street/hierarchy?
|
||||
limit=30&
|
||||
suggested_only=true&
|
||||
max_depth=4
|
||||
```
|
||||
|
||||
#### Example response:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"rooms": [
|
||||
{
|
||||
"room_id": "!ol19s:bleecker.street",
|
||||
"avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
|
||||
"guest_can_join": false,
|
||||
"name": "CHEESE",
|
||||
"num_joined_members": 37,
|
||||
"topic": "Tasty tasty cheese",
|
||||
"world_readable": true,
|
||||
"join_rules": "public",
|
||||
"room_type": "m.space",
|
||||
"children_state": [
|
||||
{
|
||||
"type": "m.space.child",
|
||||
"state_key": "!efgh:example.com",
|
||||
"content": {
|
||||
"via": ["example.com"],
|
||||
"suggested": true
|
||||
},
|
||||
"room_id": "!ol19s:bleecker.street",
|
||||
"sender": "@alice:bleecker.street",
|
||||
"origin_server_ts": 1432735824653
|
||||
},
|
||||
{ ... }
|
||||
]
|
||||
},
|
||||
{ ... }
|
||||
],
|
||||
"next_batch": "abcdef"
|
||||
}
|
||||
```
|
||||
|
||||
#### Errors:
|
||||
|
||||
An HTTP response with a status code of 403 and an error code of `M_FORBIDDEN`
|
||||
should be returned if the user doesn't have permission to view/peek the root room.
|
||||
This should also be returned if that room does not exist, which matches the
|
||||
behavior of other room endpoints (e.g.
|
||||
[`/_matrix/client/r0/rooms/{roomID}/aliases`](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-rooms-roomid-aliases))
|
||||
to not divulge that a room exists which the user doesn't have permission to view.
|
||||
|
||||
An HTTP response with a status code of 400 and an error code of `M_INVALID_PARAM`
|
||||
should be returned if the `from` token provided is unknown to the server or if
|
||||
the `suggested_only` or `max_depth` parameters are modified during pagination.
|
||||
|
||||
#### Server behaviour
|
||||
|
||||
The server should generate the response as discussed above, by doing a depth-first
|
||||
search (starting at the "root" room) for any `m.space.child` events. Any
|
||||
`m.space.child` with an invalid `via` are discarded (invalid is defined as in
|
||||
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772): missing, not an
|
||||
array or an empty array).
|
||||
|
||||
In the case of the homeserver not having access to the state of a room, the
|
||||
server-server API (see below) can be used to query for this information over
|
||||
federation from one of the servers provided in the `via` key of the
|
||||
`m.space.child` event. It is recommended to cache the federation response for a
|
||||
period of time. The federation results may contain information on a room
|
||||
that the requesting server is already participating in; the requesting server
|
||||
should use its local data for such rooms rather than the data returned over
|
||||
federation.
|
||||
|
||||
When the current response page is full, the current state should be persisted
|
||||
and a pagination token should be generated (if there is more data to return).
|
||||
To prevent resource exhaustion, the server may expire persisted data that it
|
||||
deems to be stale.
|
||||
|
||||
The persisted state will include:
|
||||
|
||||
* The processed rooms.
|
||||
* Rooms to process (in depth-first order with rooms at the same depth
|
||||
ordered [according to MSC1772, as updated to below](#msc1772-ordering)).
|
||||
* Room information from federation responses for rooms which have yet to be
|
||||
processed.
|
||||
|
||||
### Server-server API
|
||||
|
||||
The Server-Server API has a similar interface to the Client-Server API, but a
|
||||
simplified response. It is used when a homeserver is not participating in a room
|
||||
(and cannot summarize room due to not having the state).
|
||||
|
||||
The main difference is that it does *not* recurse into spaces and does not support
|
||||
pagination. This is somewhat equivalent to a Client-Server request with a `max_depth=1`.
|
||||
|
||||
Additional federation requests are made to recurse into sub-spaces. This allows
|
||||
for trivially caching responses for a short period of time (since it is not
|
||||
easily known the room summary might have changed).
|
||||
|
||||
Since the server-server API does not know the requesting user, the response should
|
||||
divulge information based on if any member of the requesting server could join
|
||||
the room. The requesting server is trusted to properly filter this information
|
||||
using the `world_readable`, `join_rules`, and `allowed_room_ids` fields from the
|
||||
response.
|
||||
|
||||
If the target server is not a member of some children rooms (so would have to send
|
||||
another request over federation to inspect them), no attempt is made to recurse
|
||||
into them. They are simply omitted from the `children` key of the response.
|
||||
(Although they will still appear in the `children_state`key of the `room`.)
|
||||
|
||||
Similarly, if a server-set limit on the size of the response is reached, additional
|
||||
rooms are not added to the response and can be queried individually.
|
||||
|
||||
#### Request format
|
||||
|
||||
```text
|
||||
GET /_matrix/federation/v1/hierarchy/{roomID}
|
||||
```
|
||||
|
||||
Query Parameters:
|
||||
|
||||
* **`suggested_only`**: The same as the Client-Server API.
|
||||
|
||||
#### Response format
|
||||
|
||||
The response format is similar to the Client-Server API:
|
||||
|
||||
* **`room`**: `object` The summary of the requested room, see below for details.
|
||||
* **`children`**: `[object]` For each room/space, a summary of that room, see
|
||||
below for details.
|
||||
* **`inaccessible_children`**: Optional `[string]`. A list of room IDs which are
|
||||
children of the requested room, but are inaccessible to the requesting server.
|
||||
Assuming the target server is non-malicious and well-behaved, then other
|
||||
non-malicious servers should respond with the same set of inaccessible rooms.
|
||||
Thus the requesting server can consider the rooms inaccessible from everywhere.
|
||||
|
||||
This is used to differentiate between rooms which the requesting server does
|
||||
not have access to from those that the target server cannot include in the
|
||||
response (which will simply be missing in the response).
|
||||
|
||||
For both the `room` and `children` fields the summary of the room/space includes
|
||||
the fields returned by `/publicRooms` (see [spec](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-publicrooms)),
|
||||
with the addition of:
|
||||
|
||||
* **`room_type`**: the value of the `m.type` field from the room's `m.room.create`
|
||||
event, if any.
|
||||
* **`allowed_room_ids`**: A list of room IDs which give access to this room per
|
||||
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083).<sup id="a1">[1](#f1)</sup>
|
||||
|
||||
#### Example request:
|
||||
|
||||
```jsonc
|
||||
GET /_matrix/federation/v1/hierarchy/{roomID}?
|
||||
suggested_only=true
|
||||
```
|
||||
|
||||
#### Errors:
|
||||
|
||||
An HTTP response with a status code of 404 and an error code of `M_NOT_FOUND` is
|
||||
returned if the target server is not a member of the requested room or the
|
||||
requesting server is not allowed to access the room.
|
||||
|
||||
### MSC1772 Ordering
|
||||
|
||||
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772) defines the ordering
|
||||
of "default ordering of siblings in the room list" using the `order` key:
|
||||
|
||||
> Rooms are sorted based on a lexicographic ordering of the Unicode codepoints
|
||||
> of the characters in `order` values. Rooms with no `order` come last, in
|
||||
> ascending numeric order of the `origin_server_ts` of their `m.room.create`
|
||||
> events, or ascending lexicographic order of their `room_id`s in case of equal
|
||||
> `origin_server_ts`. `order`s which are not strings, or do not consist solely
|
||||
> of ascii characters in the range `\x20` (space) to `\x7F` (~), or consist of
|
||||
> more than 50 characters, are forbidden and the field should be ignored if
|
||||
> received.
|
||||
|
||||
Unfortunately there are situations when a homeserver comes across a reference to
|
||||
a child room that is unknown to it and must decide the ordering. Without being
|
||||
able to see the `m.room.create` event (which it might not have permission to see)
|
||||
no proper ordering can be given.
|
||||
|
||||
Consider the following case of a space with 3 child rooms:
|
||||
|
||||
```
|
||||
Space A
|
||||
|
|
||||
+--------+--------+
|
||||
| | |
|
||||
Room B Room C Room D
|
||||
```
|
||||
|
||||
HS1 has users in Space A, Room B, and Room C, while HS2 has users in Room D. HS1 has no users
|
||||
in Room D (and thus has no state from it). Room B, C, and D do not have an
|
||||
`order` field set (and default to using the ordering rules above).
|
||||
|
||||
When a user asks HS1 for the space summary with a `limit` equal to `2` it cannot
|
||||
fulfill this request since it is unsure how to order Room B, Room C, and Room D,
|
||||
but it can only return 2 of them. It *can* reach out over federation to HS2 and
|
||||
request a space summary for Room D, but this is undesirable:
|
||||
|
||||
* HS1 might not have the permissions to know any of the state of Room D, so might
|
||||
receive a 404 error.
|
||||
* If we expand the example above to many rooms than this becomes expensive to
|
||||
query a remote server simply for ordering.
|
||||
|
||||
This proposes changing the ordering rules from MSC1772 to the following:
|
||||
|
||||
> Rooms are sorted based on a lexicographic ordering of the Unicode codepoints
|
||||
> of the characters in `order` values. Rooms with no `order` come last, in
|
||||
> ascending numeric order of the `origin_server_ts` of their `m.space.child`
|
||||
> events, or ascending lexicographic order of their `room_id`s in case of equal
|
||||
> `origin_server_ts`. `order`s which are not strings, or do not consist solely
|
||||
> of ascii characters in the range `\x20` (space) to `\x7E` (~), or consist of
|
||||
> more than 50 characters, are forbidden and the field should be ignored if
|
||||
> received.
|
||||
|
||||
This modifies the clause for calculating the order to use the `origin_server_ts`
|
||||
of the `m.space.child` event instead of the `m.room.create` event. This allows
|
||||
for a defined sorting of siblings based purely on the information available in
|
||||
the state of the space while still allowing for a natural ordering due to the
|
||||
age of the relationship.
|
||||
|
||||
## Potential issues
|
||||
|
||||
A large flat space (a single room with many `m.space.child` events) could cause
|
||||
a large federation response.
|
||||
|
||||
Room version upgrades of rooms in a space are unsolved and left to a future MSC.
|
||||
When upgrading a room it is unclear if the old room should be removed (in which
|
||||
case users who have not yet joined the new room will no longer see it in the space)
|
||||
or leave the old room (in which case users who have joined the new room will see
|
||||
both). The current recommendation is for clients de-duplicate rooms which are
|
||||
known old versions of rooms in the space.
|
||||
|
||||
## Alternatives
|
||||
|
||||
Peeking to explore the room state could be used to build the tree of rooms/spaces,
|
||||
but this would be significantly more expensive for both clients and servers. It
|
||||
would also require peeking over federation (which is explored in
|
||||
[MSC2444](https://github.com/matrix-org/matrix-doc/pull/2444)).
|
||||
|
||||
## Security considerations
|
||||
|
||||
A space with many sub-spaces and rooms on different homeservers could cause
|
||||
a large number of federation requests. A carefully crafted space with inadequate
|
||||
server enforced limits could be used in a denial of service attack. Generally
|
||||
this is mitigated by enforcing server limits and caching of responses.
|
||||
|
||||
The requesting server over federation is trusted to filter the response for the
|
||||
requesting user. The alternative, where the requesting server sends the requesting
|
||||
`user_id`, and the target server does the filtering, is unattractive because it
|
||||
rules out a caching of the result. This does not decrease security since a server
|
||||
could lie and make a request on behalf of a user in the proper space to see the
|
||||
given information. I.e. the calling server must be trusted anyway.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
During development of this feature it will be available at unstable endpoints.
|
||||
|
||||
The client-server API will be:
|
||||
`/_matrix/client/unstable/org.matrix.msc2946/rooms/{roomID}/hierarchy`
|
||||
|
||||
The server-server API will be:
|
||||
`/_matrix/federation/unstable/org.matrix.msc2946/hierarchy/{roomID}`
|
||||
|
||||
## Footnotes
|
||||
|
||||
<a id="f1"/>[1]: As a worked example, in the context of
|
||||
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083), consider that Alice
|
||||
and Bob share a server; Alice is a member of a space, but Bob is not. A remote
|
||||
server will not know whether the request is on behalf of Alice or Bob (and hence
|
||||
whether it should share details of restricted rooms within that space).
|
||||
|
||||
Consider if the space is modified to include a restricted room on a different server
|
||||
which allows access from the space. When summarizing the space, the homeserver must make
|
||||
a request over federation for information on the room. The response should include
|
||||
the room (since Alice is able to join it). Without additional information the
|
||||
calling server does not know *why* they received the room and cannot properly
|
||||
filter the returned results.
|
||||
|
||||
Note that there are still potential situations where each server individually
|
||||
doesn't have enough information to properly return the full summary, but these
|
||||
do not seem reasonable in what is considered a normal structure of spaces. (E.g.
|
||||
in the above example, if the remote server is not in the space and does not know
|
||||
whether the server is in the space or not it cannot return the room.)[↩](#a1)
|
@ -0,0 +1,286 @@
|
||||
# MSC3030: Jump to date API endpoint
|
||||
|
||||
Add an API that makes it easy to find the closest messages for a given
|
||||
timestamp.
|
||||
|
||||
The goal of this change is to have clients be able to implement a jump to date
|
||||
feature in order to see messages back at a given point in time. Pick a date from
|
||||
a calender, heatmap, or paginate next/previous between days and view all of the
|
||||
messages that were sent on that date.
|
||||
|
||||
Alongside the [roadmap of feature parity with
|
||||
Gitter](https://github.com/vector-im/roadmap/issues/26), we're also interested
|
||||
in using this for a new better static Matrix archive. Our idea is to server-side
|
||||
render [Hydrogen](https://github.com/vector-im/hydrogen-web) and this new
|
||||
endpoint would allow us to jump back on the fly without having to paginate and
|
||||
keep track of everything in order to display the selected date.
|
||||
|
||||
Also useful for archiving and backup use cases. This new endpoint can be used to
|
||||
slice the messages by day and persist to file.
|
||||
|
||||
Related issue: [*URL for an arbitrary day of history and navigation for next and
|
||||
previous days*
|
||||
(vector-im/element-web#7677)](https://github.com/vector-im/element-web/issues/7677)
|
||||
|
||||
|
||||
## Problem
|
||||
|
||||
These types of use cases are not supported by the current Matrix API because it
|
||||
has no way to fetch or filter older messages besides a manual brute force
|
||||
pagination from the most recent event in the room. Paginating is time-consuming
|
||||
and expensive to process every event as you go (not practical for clients).
|
||||
Imagine wanting to get a message from 3 years ago 😫
|
||||
|
||||
|
||||
## Proposal
|
||||
|
||||
Add new client API endpoint `GET
|
||||
/_matrix/client/v1/rooms/{roomId}/timestamp_to_event?ts=<timestamp>&dir=[f|b]`
|
||||
which fetches the closest `event_id` to the given timestamp `ts` query parameter
|
||||
in the direction specified by the `dir` query parameter. The direction `dir`
|
||||
query parameter accepts `f` for forward-in-time from the timestamp and `b` for
|
||||
backward-in-time from the timestamp. This endpoint also returns
|
||||
`origin_server_ts` to make it easy to do a quick comparison to see if the
|
||||
`event_id` fetched is too far out of range to be useful for your use case.
|
||||
|
||||
When an event can't be found in the given direction, the endpoint throws a 404
|
||||
`"errcode":"M_NOT_FOUND",` (example error message `"error":"Unable to find event
|
||||
from 1672531200000 in direction f"`).
|
||||
|
||||
In order to solve the problem where a homeserver does not have all of the history in a
|
||||
room and no suitably close event, we also add a server API endpoint `GET
|
||||
/_matrix/federation/v1/timestamp_to_event/{roomId}?ts=<timestamp>?dir=[f|b]` which other
|
||||
homeservers can use to ask about their closest `event_id` to the timestamp. This
|
||||
endpoint also returns `origin_server_ts` to make it easy to do a quick comparison to see
|
||||
if the remote `event_id` fetched is closer than the local one. After the local
|
||||
homeserver receives a response from the federation endpoint, it probably should
|
||||
try to backfill this event via the federation `/event/<event_id>` endpoint so that it's
|
||||
available to query with `/context` from a client in order to get a pagination token.
|
||||
|
||||
The heuristics for deciding when to ask another homeserver for a closer event if
|
||||
your homeserver doesn't have something close, are left up to the homeserver
|
||||
implementation, although the heuristics will probably be based on whether the
|
||||
closest event is a forward/backward extremity indicating it's next to a gap of
|
||||
events which are potentially closer.
|
||||
|
||||
A good heuristic for which servers to try first is to sort by servers that have
|
||||
been in the room the longest because they're most likely to have anything we ask
|
||||
about.
|
||||
|
||||
These endpoints are authenticated and should be rate-limited like similar client
|
||||
and federation endpoints to prevent resource exhaustion abuse.
|
||||
|
||||
```
|
||||
GET /_matrix/client/v1/rooms/<roomID>/timestamp_to_event?ts=<timestamp>&dir=<direction>
|
||||
{
|
||||
"event_id": ...
|
||||
"origin_server_ts": ...
|
||||
}
|
||||
```
|
||||
|
||||
Federation API endpoint:
|
||||
```
|
||||
GET /_matrix/federation/v1/timestamp_to_event/<roomID>?ts=<timestamp>&dir=<direction>
|
||||
{
|
||||
"event_id": ...
|
||||
"origin_server_ts": ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
In order to paginate `/messages`, we need a pagination token which we can get
|
||||
using `GET /_matrix/client/r0/rooms/{roomId}/context/{eventId}?limit=0` for the
|
||||
`event_id` returned by `/timestamp_to_event`.
|
||||
|
||||
We can always iterate on `/timestamp_to_event` later and return a pagination
|
||||
token directly in another MSC ⏩
|
||||
|
||||
|
||||
## Potential issues
|
||||
|
||||
### Receiving a rogue random delayed event ID
|
||||
|
||||
Since `origin_server_ts` is not enforcably accurate, we can only hope that an event's
|
||||
`origin_server_ts` is relevant enough to its `prev_events` and descendants.
|
||||
|
||||
If you ask for "the message with `origin_server_ts` closest to Jan 1st 2018" you
|
||||
might actually get a rogue random delayed one that was backfilled from a
|
||||
federated server, but the human can figure that out by trying again with a
|
||||
slight variation on the date or something.
|
||||
|
||||
Since there isn't a good or fool-proof way to combat this, it's probably best to just go
|
||||
with `origin_server_ts` and not let perfect be the enemy of good.
|
||||
|
||||
|
||||
### Receiving an unrenderable event ID
|
||||
|
||||
Another issue is that clients could land on an event they can't/won't render,
|
||||
such as a reaction, then they'll be forced to desperately seek around the
|
||||
timeline until they find an event they can do something with.
|
||||
|
||||
Eg:
|
||||
- Client wants to jump to January 1st, 2022
|
||||
- Server says there's an event on January 2nd, 2022 that is close enough
|
||||
- Client finds out there's a ton of unrenderable events like memberships, poll responses, reactions, etc at that time
|
||||
- Client starts paginating forwards, finally finding an event on January 27th it can render
|
||||
- Client wasn't aware that the actual nearest neighbouring event was backwards on December 28th, 2021 because it didn't paginate in that direction
|
||||
- User is confused that they are a month past the target date when the message is *right there*.
|
||||
|
||||
Clients can be smarter here though. Clients can see when events were sent as
|
||||
they paginate and if they see they're going more than a couple days out, they
|
||||
can also try the other direction before going further and further away.
|
||||
|
||||
Clients can also just explain to the user what happened with a little toast: "We
|
||||
were unable to find an event to display on January 1st, 2022. The closest event
|
||||
after that date is on January 27th."
|
||||
|
||||
|
||||
### Abusing the `/timestamp_to_event` API to get the `m.room.create` event
|
||||
|
||||
Although it's possible to jump to the start of the room and get the first event in the
|
||||
room (`m.room.create`) with `/timestamp_to_event?dir=f&ts=0`, clients should still use
|
||||
`GET /_matrix/client/v3/rooms/{roomId}/state/m.room.create/` to get the room creation
|
||||
event.
|
||||
|
||||
In the future, with things like importing history via
|
||||
[MSC2716](https://github.com/matrix-org/matrix-spec-proposals/pull/2716), the first
|
||||
event you encounter with `/timestamp_to_event?dir=f&ts=0` could be an imported event before
|
||||
the room was created.
|
||||
|
||||
|
||||
## Alternatives
|
||||
|
||||
We chose the current `/timestamp_to_event` route because it sounded like the
|
||||
easist path forward to bring it to fruition and get some real-world experience.
|
||||
And was on our mind during the [initial discussion](https://docs.google.com/document/d/1KCEmpnGr4J-I8EeaVQ8QJZKBDu53ViI7V62y5BzfXr0/edit#bookmark=id.qu9k9wje9pxm) because there was some prior art with a [WIP
|
||||
implementation](https://github.com/matrix-org/synapse/pull/9445/commits/91b1b3606c9fb9eede0a6963bc42dfb70635449f)
|
||||
from @erikjohnston. The alternatives haven't been thrown out for a particular
|
||||
reason and we could still go down those routes depending on how people like the
|
||||
current design.
|
||||
|
||||
|
||||
### Paginate `/messages?around=<timestamp>` from timestamp
|
||||
|
||||
Add the `?around=<timestamp>` query parameter to the `GET
|
||||
/_matrix/client/r0/rooms/{roomId}/messages` endpoint. This will start the
|
||||
response at the message with `origin_server_ts` closest to the provided `around`
|
||||
timestamp. The direction is determined by the existing `?dir` query parameter.
|
||||
|
||||
Use topological ordering, just as Element would use if you follow a permalink.
|
||||
|
||||
This alternative could be confusing to the end-user around how this plays with
|
||||
the existing query parameters
|
||||
`/messages?from={paginationToken}&to={paginationToken}` which also determine
|
||||
what part of the timeline to query. Those parameters could be extended to accept
|
||||
timestamps in addition to pagination tokens but then could get confusing again
|
||||
when you start mixing timestamps and pagination tokens. The homeserver also has
|
||||
to disambiguate what a pagination token looks like vs a unix timestamp. Since
|
||||
pagination tokens don't follow a certain convention, some homeserver
|
||||
implementations may already be using arbitrary number tokens already which would
|
||||
be impossible to distinguish from a timestamp.
|
||||
|
||||
A related alternative is to use `/messages` with a `from_time`/`to_time` (or
|
||||
`from_ts`/`to_ts`) query parameters that only accept timestamps which solves the
|
||||
confusion and disambigution problem of trying to re-use the existing `from`/`to`
|
||||
query paramters. Re-using `/messages` would reduce the number of round-trips and
|
||||
potentially client-side implementations for the use case where you want to fetch
|
||||
a window of messages from a given time. But has the same round-trip problem if
|
||||
you want to use the returned `event_id` with `/context` or another endpoint
|
||||
instead.
|
||||
|
||||
|
||||
### Filter by date in `RoomEventFilter`
|
||||
|
||||
Extend `RoomEventFilter` to be able to specify a timestamp or a date range. The
|
||||
`RoomEventFilter` can be passed via the `?filter` query param on the `/messages`
|
||||
endpoint.
|
||||
|
||||
This suffers from the same confusion to the end-user of how it plays with how
|
||||
this plays with `/messages?from={paginationToken}&to={paginationToken}` which
|
||||
also determines what part of the timeline to query.
|
||||
|
||||
|
||||
### Return the closest event in any direction
|
||||
|
||||
We considered omitting the `dir` parameter (or allowing `dir=c`) to have the server
|
||||
return the closest event to the timestamp, regardless of direction. However, this seems
|
||||
to offer little benefit.
|
||||
|
||||
Firstly, for some usecases (such as archive viewing, where we want to show all the
|
||||
messages that happened on a particular day), an explicit direction is important, so this
|
||||
would have to be optional behaviour.
|
||||
|
||||
For a regular messaging client, "directionless" search also offers little benefit: it is
|
||||
easy for the client to repeat the request in the other direction if the returned event
|
||||
is "too far away", and in any case it needs to manage an iterative search to handle
|
||||
unrenderable events, as discussed above.
|
||||
|
||||
Implementing a directionless search on the server carries a performance overhead, since
|
||||
it must search both forwards and backwards on every request. In short, there is little
|
||||
reason to expect that a single `dir=c` request would be any more efficient than a pair of
|
||||
requests with `dir=b` and `dir=f`.
|
||||
|
||||
### New `destination_server_ts` field
|
||||
|
||||
Add a new field and index on messages called `destination_server_ts` which
|
||||
indicates when the message was received from federation. This gives a more
|
||||
"real" time for how someone would actually consume those messages.
|
||||
|
||||
The contract of the API is "show me messages my server received at time T"
|
||||
rather than the messy confusion of showing a delayed message which happened to
|
||||
originally be sent at time T.
|
||||
|
||||
We've decided against this approach because the backfill from federated servers
|
||||
could be horribly late.
|
||||
|
||||
---
|
||||
|
||||
Related issue around `/sync` vs `/messages`,
|
||||
https://github.com/matrix-org/synapse/issues/7164
|
||||
|
||||
> Sync returns things in the order they arrive at the server; backfill returns
|
||||
> them in the order determined by the event graph.
|
||||
>
|
||||
> *-- @richvdh, https://github.com/matrix-org/synapse/issues/7164#issuecomment-605877176*
|
||||
|
||||
> The general idea is that, if you're following a room in real-time (ie,
|
||||
> `/sync`), you probably want to see the messages as they arrive at your server,
|
||||
> rather than skipping any that arrived late; whereas if you're looking at a
|
||||
> historical section of timeline (ie, `/messages`), you want to see the best
|
||||
> representation of the state of the room as others were seeing it at the time.
|
||||
>
|
||||
> *-- @richvdh , https://github.com/matrix-org/synapse/issues/7164#issuecomment-605953296*
|
||||
|
||||
|
||||
## Security considerations
|
||||
|
||||
We're only going to expose messages according to the existing message history
|
||||
setting in the room (`m.room.history_visibility`). No extra data is exposed,
|
||||
just a new way to sort through it all.
|
||||
|
||||
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
While this MSC is not considered stable, the endpoints are available at `/unstable/org.matrix.msc3030` instead of their `/v1` description from above.
|
||||
|
||||
```
|
||||
GET /_matrix/client/unstable/org.matrix.msc3030/rooms/<roomID>/timestamp_to_event?ts=<timestamp>&dir=<direction>
|
||||
{
|
||||
"event_id": ...
|
||||
"origin_server_ts": ...
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
GET /_matrix/federation/unstable/org.matrix.msc3030/timestamp_to_event/<roomID>?ts=<timestamp>&dir=<direction>
|
||||
{
|
||||
"event_id": ...
|
||||
"origin_server_ts": ...
|
||||
}
|
||||
```
|
||||
|
||||
Servers will indicate support for the new endpoint via a non-empty value for feature flag
|
||||
`org.matrix.msc3030` in `unstable_features` in the response to `GET
|
||||
/_matrix/client/versions`.
|
@ -0,0 +1,92 @@
|
||||
# MSC3267: reference relationships
|
||||
|
||||
## Proposal
|
||||
|
||||
This proposal defines a relation type (using
|
||||
[MSC2674 relations](https://github.com/matrix-org/matrix-doc/pull/2674))
|
||||
for events to make a reference to another event.
|
||||
|
||||
A `rel_type` of `m.reference` is defined as a generic way to associate an
|
||||
event with another event. As a bundle, `m.reference` relations appear as
|
||||
an object with a single `chunk` field. The `chunk` is an array of objects
|
||||
with a single `event_id` field for all the child events which `m.reference`
|
||||
the parent.
|
||||
|
||||
There are no implied semantics by a reference relation: the feature or event
|
||||
type which makes use of the `rel_type` should specify what sort of semantic
|
||||
behaviour there is, if any. For example, describing that a poll response event
|
||||
*references* the poll start event, or that a location update *references* a
|
||||
previous location update.
|
||||
|
||||
Reference relations are used by [MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)
|
||||
to tie all events together for the same verification request.
|
||||
|
||||
For instance, an `m.room.message` which references an existing event
|
||||
would look like:
|
||||
|
||||
```json5
|
||||
{
|
||||
// Unimportant fields omitted
|
||||
"type": "m.room.message",
|
||||
"content": {
|
||||
"msgtype": "m.text",
|
||||
"body": "i <3 shelties",
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.reference",
|
||||
"event_id": "$another_event_id"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Server aggregation
|
||||
|
||||
[MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674) states
|
||||
that values for `rel_type` should define how the server should aggregate the
|
||||
`rel_type`. For `m.reference`, child events are bundled to appear as follows
|
||||
under `m.relations`:
|
||||
|
||||
```json5
|
||||
{
|
||||
"m.reference": {
|
||||
"chunk": [
|
||||
{"event_id": "$one"},
|
||||
{"event_id": "$two"},
|
||||
{"event_id": "$three"}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that currently only the `event_id` is noted in the chunk, however a future
|
||||
MSC might add more fields.
|
||||
|
||||
## Limitations
|
||||
|
||||
Different subtypes of references could be defined through additional fields on
|
||||
the `m.relates_to` object, to distinguish between other forms of semantic behaviour
|
||||
independent of type (hypothetical threads, replies, etc if we didn't have a system
|
||||
for them already). This MSC doesn't attempt to define these subtypes.
|
||||
|
||||
This relation cannot be used in conjunction with another relation due to `rel_type`
|
||||
being a single value. This is known and unfortunately not resolved by this MSC.
|
||||
A future MSC might address the concern.
|
||||
|
||||
## Edge Cases
|
||||
|
||||
Can you reference an event which doesn't have logical meaning? Eg to a [reaction](https://github.com/matrix-org/matrix-doc/pull/2677)?
|
||||
* Yes, at the protocol level. But you shouldn't expect clients to do anything
|
||||
useful with it.
|
||||
* The relationship is effectively pointless, so the event would appear as though
|
||||
there was no reference relationship.
|
||||
|
||||
Do we need to support retrospective references?
|
||||
* For something like "m.duplicate" to retrospectively declare that one event
|
||||
dupes another, we might need to bundle two-levels deep (subject+ref and then
|
||||
ref+target). We can cross this bridge when we get there though, as another
|
||||
aggregation type
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
Unfortunately not applicable - this MSC was used in production and appears in the
|
||||
specified version of the [key verification framework](https://spec.matrix.org/v1.2/client-server-api/#key-verification-framework).
|
@ -0,0 +1,46 @@
|
||||
# MSC3283: Expose enable_set_displayname, enable_set_avatar_url and enable_3pid_changes in capabilities response
|
||||
|
||||
Some home servers like [Synapse](https://github.com/matrix-org/synapse/blob/756fd513dfaebddd28bf783eafa95b4505ce8745/docs/sample_config.yaml#L1207)
|
||||
can be configured to `enable_set_displayname: false`, `enable_set_avatar_url: false` or `enable_3pid_changes: false`.
|
||||
To enable clients to handle that gracefully in the UI this setting should be exposed.
|
||||
|
||||
## Proposal
|
||||
|
||||
The `/_matrix/client/r0/capabilities` endpoint should be decorated to provide more information on capabilities.
|
||||
```jsonc
|
||||
{
|
||||
"capabilities": {
|
||||
"m.set_displayname": { "enabled": false },
|
||||
"m.set_avatar_url": { "enabled": false },
|
||||
"m.3pid_changes": { "enabled": false },
|
||||
"m.room_versions": {...},
|
||||
}
|
||||
}
|
||||
```
|
||||
As part of this MSC, a capability for each setting will be added that exposes the server setting:
|
||||
- `m.set_displayname`
|
||||
|
||||
Whether users are allowed to change their displayname after it has been initially set.
|
||||
Useful when provisioning users based on the contents of a third-party directory.
|
||||
|
||||
- `m.set_avatar_url`
|
||||
|
||||
Whether users are allowed to change their avatar after it has been initially set.
|
||||
Useful when provisioning users based on the contents of a third-party directory.
|
||||
|
||||
- `m.3pid_changes`
|
||||
|
||||
Whether users can change the 3PIDs associated with their accounts
|
||||
(email address and msisdn).
|
||||
Useful when provisioning users based on the contents of a third-party directory.
|
||||
|
||||
## Client recommendations
|
||||
When presenting profile settings, clients should use capabilities in order to display the correct UI.
|
||||
|
||||
Capability should always be present.
|
||||
Servers should always send these capabilities. If they aren't (because the server does not support
|
||||
a new enough spec version or for any other reason), clients should behave as if they were present and set to true.
|
||||
|
||||
## Unstable prefix
|
||||
|
||||
While this MSC is not considered stable, implementations should use `org.matrix.msc3283.` in place of `m.` throughout this proposal.
|
@ -0,0 +1,51 @@
|
||||
# Proposal to add timestamp massaging to the spec
|
||||
Bridges often want to override message timestamps to preserve the timestamps from
|
||||
the remote network. The spec used to have a concept of [timestamp massaging], but
|
||||
it was excluded from the release due to not being properly specified. Synapse
|
||||
still implements it and it is widely used in bridges.
|
||||
|
||||
[MSC2716] was originally going to add timestamp massaging to the spec, but it
|
||||
pivoted to focusing solely on batch sending history. This MSC simply copies the
|
||||
proposed `ts` query param from the [original MSC2716].
|
||||
|
||||
[timestamp massaging]: https://matrix.org/docs/spec/application_service/r0.1.2#timestamp-massaging
|
||||
[MSC2716]: https://github.com/matrix-org/matrix-doc/pull/2716
|
||||
[original MSC2716]: https://github.com/matrix-org/matrix-doc/blob/94514392b118dfae8ee6840b13b83d2f8ce8fcfc/proposals/2716-importing-history-into-existing-rooms.md
|
||||
|
||||
## Proposal
|
||||
As per the original version of MSC2716:
|
||||
|
||||
> We let the AS API override ('massage') the `origin_server_ts` timestamp
|
||||
> applied to sent events. We do this by adding a `ts` querystring parameter on
|
||||
> the `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}`
|
||||
> and `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}`
|
||||
> endpoints, specifying the value to apply to `origin_server_ts` on the event
|
||||
> (UNIX epoch milliseconds).
|
||||
>
|
||||
> We consciously don't support the `ts` parameter on the various helper
|
||||
> syntactic-sugar APIs like /kick and /ban. If a bridge/bot is smart enough to
|
||||
> be faking history, it is already in the business of dealing with raw events,
|
||||
> and should not be using the syntactic sugar APIs.
|
||||
|
||||
The spec should also make it clear that the `ts` query param won't affect DAG
|
||||
ordering, and MSC2716's batch sending should be used when the intention is to
|
||||
insert history somewhere else than the end of the room.
|
||||
|
||||
## Potential issues
|
||||
None.
|
||||
|
||||
## Alternatives
|
||||
The new MSC2716 could technically be considered an alternative, but it can only
|
||||
be used for history, while this proposal also supports overriding timestamps of
|
||||
new messages. In practice, bridges will likely use both: Batch sending for
|
||||
filling history and timestamp massaging for new messages.
|
||||
|
||||
## Security considerations
|
||||
Timestamps should already be considered untrusted over federation, and
|
||||
application services are trusted server components, so allowing appservices
|
||||
to override timestamps does not create any new security considerations.
|
||||
|
||||
## Unstable prefix
|
||||
`org.matrix.msc3316.ts` may be used as the query parameter. However, the `ts`
|
||||
parameter is already used in production for the `/send` endpoint, which means
|
||||
the unstable prefix should only be used for the `/state` endpoint.
|
Loading…
Reference in New Issue