mirror of
https://github.com/matrix-org/matrix-spec
synced 2026-03-26 04:54:10 +01:00
Merge branch 'main' into travis/ref-rel
This commit is contained in:
commit
b65ad68361
1
changelogs/client_server/newsfragments/1211.feature
Normal file
1
changelogs/client_server/newsfragments/1211.feature
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add `m.replace` relations (event edits), as per [MSC2676](https://github.com/matrix-org/matrix-spec-proposals/pull/2676).
|
||||
|
|
@ -0,0 +1 @@
|
|||
Fix various typos throughout the specification.
|
||||
1
changelogs/client_server/newsfragments/1216.feature.1
Normal file
1
changelogs/client_server/newsfragments/1216.feature.1
Normal file
|
|
@ -0,0 +1 @@
|
|||
Add `m.read.private` receipts, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285).
|
||||
1
changelogs/client_server/newsfragments/1216.feature.2
Normal file
1
changelogs/client_server/newsfragments/1216.feature.2
Normal file
|
|
@ -0,0 +1 @@
|
|||
Make `m.fully_read` optional on `/read_markers`, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285).
|
||||
1
changelogs/client_server/newsfragments/1216.feature.3
Normal file
1
changelogs/client_server/newsfragments/1216.feature.3
Normal file
|
|
@ -0,0 +1 @@
|
|||
Allow `m.fully_read` markers to be set from `/receipts`, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285).
|
||||
|
|
@ -0,0 +1 @@
|
|||
Reinforce the relationship of refreshed access tokens to transaction IDs.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Fix various typos throughout the specification.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Clarify enum values by separating possible values with commas.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Fix various typos throughout the specification.
|
||||
1
changelogs/internal/newsfragments/1230.clarification
Normal file
1
changelogs/internal/newsfragments/1230.clarification
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fix the spacing of mapping types generated from the OpenAPI spec.
|
||||
|
|
@ -213,12 +213,19 @@ See the [Server Notices](#server-notices) module for more information.
|
|||
|
||||
The client-server API typically uses `HTTP PUT` to submit requests with
|
||||
a client-generated transaction identifier. This means that these
|
||||
requests are idempotent. The scope of a transaction identifier is a
|
||||
particular access token. It **only** serves to identify new requests
|
||||
requests are idempotent. It **only** serves to identify new requests
|
||||
from retransmits. After the request has finished, the `{txnId}` value
|
||||
should be changed (how is not specified; a monotonically increasing
|
||||
integer is recommended).
|
||||
|
||||
The scope of a transaction ID is a "client session", where that session
|
||||
is identified by a particular access token. When [refreshing](#refreshing-access-tokens)
|
||||
an access token, the transaction ID's scope is retained. This means that
|
||||
if a client with token `A` uses `TXN1` as their transaction ID, refreshes
|
||||
the token to `B`, and uses `TXN1` again it'll be assumed to be a duplicate
|
||||
request and ignored. If the client logs out and back in between the `A` and
|
||||
`B` tokens, `TXN1` could be used once for each.
|
||||
|
||||
Some API endpoints may allow or require the use of `POST` requests
|
||||
without a transaction ID. Where this is optional, the use of a `PUT`
|
||||
request is strongly recommended.
|
||||
|
|
@ -1955,16 +1962,6 @@ rooms, or the relationship missing properties required by the schema below. Clie
|
|||
handling such invalid relationships should show the events independently of each
|
||||
other, optionally with an error message.
|
||||
|
||||
{{% boxes/note %}}
|
||||
While this specification describes an `m.relates_to` object containing a `rel_type`, there
|
||||
is not currently any relationship type which uses this structure. Replies, described below,
|
||||
form their relationship outside of the `rel_type` as a legacy type of relationship. Future
|
||||
versions of the specification might change replies to better match the relationship structures.
|
||||
|
||||
Custom `rel_type`s can, and should, still use the schema described above for relevant
|
||||
behaviour.
|
||||
{{% /boxes/note %}}
|
||||
|
||||
`m.relates_to` is defined as follows:
|
||||
|
||||
{{% definition path="api/client-server/definitions/m.relates_to" %}}
|
||||
|
|
@ -1974,6 +1971,7 @@ behaviour.
|
|||
This specification describes the following relationship types:
|
||||
|
||||
* [Rich replies](#rich-replies) (**Note**: does not use `rel_type`).
|
||||
* [Event replacements](#event-replacements).
|
||||
* [References](#reference-relations)
|
||||
|
||||
#### Aggregations
|
||||
|
|
@ -2644,4 +2642,5 @@ systems.
|
|||
{{< cs-module name="server_notices" >}}
|
||||
{{< cs-module name="moderation_policies" >}}
|
||||
{{< cs-module name="spaces" >}}
|
||||
{{< cs-module name="reference_relations" >}}
|
||||
{{< cs-module name="event_replacements" >}}
|
||||
{{< cs-module name="reference_relations" >}}
|
||||
|
|
@ -17,10 +17,10 @@ data with the same `type`.
|
|||
#### Events
|
||||
|
||||
The client receives the account data as events in the `account_data`
|
||||
sections of a `/sync`.
|
||||
sections of a [`/sync`](#get_matrixclientv3sync) response.
|
||||
|
||||
These events can also be received in a `/events` response or in the
|
||||
`account_data` section of a room in `/sync`. `m.tag` events appearing in
|
||||
`account_data` section of a room in a `/sync` response. `m.tag` events appearing in
|
||||
`/events` will have a `room_id` with the room the tags are for.
|
||||
|
||||
#### Client Behaviour
|
||||
|
|
|
|||
330
content/client-server-api/modules/event_replacements.md
Normal file
330
content/client-server-api/modules/event_replacements.md
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
---
|
||||
type: module
|
||||
---
|
||||
|
||||
### Event replacements
|
||||
|
||||
{{% added-in v="1.4" %}}
|
||||
|
||||
Event replacements, or "message edit events", are events that use an [event
|
||||
relationship](#forming-relationships-between-events)
|
||||
with a `rel_type` of `m.replace`, which indicates that the original event is
|
||||
intended to be replaced.
|
||||
|
||||
An example of a message edit event might look like this:
|
||||
|
||||
```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"
|
||||
}
|
||||
},
|
||||
// ... other fields required by events
|
||||
}
|
||||
```
|
||||
|
||||
The `content` of the replacement must contain a `m.new_content` property which
|
||||
defines the replacement `content`. The normal `content` properties (`body`,
|
||||
`msgtype` etc.) provide a fallback for clients which do not understand
|
||||
replacement events.
|
||||
|
||||
`m.new_content` can include any properties that would normally be found in
|
||||
an event's content property, such as `formatted_body` (see [`m.room.message`
|
||||
`msgtypes`](#mroommessage-msgtypes)).
|
||||
|
||||
#### Validity of replacement events
|
||||
|
||||
There are a number of requirements on replacement events, which must be satisfied for the replacement to be considered valid:
|
||||
|
||||
* As with all event relationships, the original event and replacement event
|
||||
must have the same `room_id` (i.e. you cannot send an event in
|
||||
one room and then an edited version in a different room).
|
||||
|
||||
* The original event and replacement event must have the same `sender`
|
||||
(i.e. you cannot edit someone else's messages).
|
||||
|
||||
* The replacement and original events must have the same `type` (i.e. you
|
||||
cannot change the original event's type).
|
||||
|
||||
* The replacement and original events must not have a `state_key` property
|
||||
(i.e. you cannot edit state events at all).
|
||||
|
||||
* The original event must not, itself, have a `rel_type` of `m.replace`
|
||||
(i.e. you cannot edit an edit — though you can send multiple edits for a
|
||||
single original event).
|
||||
|
||||
* The replacement event (once decrypted, if appropriate) must have an
|
||||
`m.new_content` property.
|
||||
|
||||
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).
|
||||
|
||||
Note that the [`msgtype`](#mroommessage-msgtypes) property of replacement
|
||||
`m.room.message` events does *not* need to be the same as in the original event. For
|
||||
example, it is legitimate to replace an `m.text` event with an `m.emote`.
|
||||
|
||||
#### Editing encrypted events
|
||||
|
||||
If the original event was [encrypted](#end-to-end-encryption), the replacement
|
||||
should be too. In that case, `m.new_content` is placed in the content of the
|
||||
encrypted payload. As with all event relationships, the `m.relates_to` property
|
||||
must be sent in the unencrypted (cleartext) part of the event.
|
||||
|
||||
For example, a replacement for an encrypted 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>"
|
||||
}
|
||||
// irrelevant fields not shown
|
||||
}
|
||||
```
|
||||
|
||||
... 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. If there was, it would be ignored.
|
||||
* There is no `m.new_content` property in the cleartext content of the `m.room.encrypted` event. As above, if there was then it would be ignored.
|
||||
|
||||
{{% boxes/note %}}
|
||||
The payload of an encrypted replacement event must be encrypted as normal, including
|
||||
ratcheting any [Megolm](#mmegolmv1aes-sha2) session as normal. The original Megolm
|
||||
ratchet entry should **not** be re-used.
|
||||
{{% /boxes/note %}}
|
||||
|
||||
|
||||
#### Applying `m.new_content`
|
||||
|
||||
When applying a replacement, the `content` of the original event is treated as
|
||||
being overwritten entirely by `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.
|
||||
|
||||
{{% boxes/note %}}
|
||||
Note that server implementations must not *actually* overwrite
|
||||
the original event's `content`: instead the server presents it as being overwritten
|
||||
when it is served over the client-server API. See [Server-side replacement of content](#server-side-replacement-of-content)
|
||||
below.
|
||||
{{% /boxes/note %}}
|
||||
|
||||
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 really 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:
|
||||
|
||||
```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 `formatted_body` is now absent, because it was absent in the
|
||||
replacement event.
|
||||
|
||||
#### 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](#aggregations) by the homeserver.
|
||||
|
||||
The aggregation format of `m.replace` relationships gives the `event_id`,
|
||||
`origin_server_ts`, and `sender` of the **most recent** replacement event. The
|
||||
most recent event is determined by comparing `origin_server_ts`; if two or more
|
||||
replacement events have identical `origin_server_ts`, the event with the
|
||||
lexicographically largest `event_id` is treated as more recent.
|
||||
|
||||
This aggregation is bundled under the `unsigned` property as `m.relations` for any
|
||||
event that is the target of an `m.replace` relationship. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_id": "$original_event_id",
|
||||
// irrelevant fields not shown
|
||||
"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](#redactions), any
|
||||
`m.replace` relationship should **not** be bundled with it (whether or not any
|
||||
subsequent replacements are themselves redacted). Note that this behaviour is
|
||||
specific to the `m.replace` relationship. See also [redactions of edited
|
||||
events](#redactions-of-edited-events) below.
|
||||
|
||||
##### 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 replacement event (determined as above).
|
||||
|
||||
An exception applies to [`GET /_matrix/client/v3/rooms/{roomId}/event/{eventId}`](#get_matrixclientv3roomsroomideventeventid),
|
||||
which should return the unmodified event (though the relationship should still
|
||||
be bundled, as described above).
|
||||
|
||||
#### Client behaviour
|
||||
|
||||
Clients can often ignore `m.replace` events, because any events returned
|
||||
by the server to the client 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:
|
||||
|
||||
* The client has already received and stored the original event before the
|
||||
message edit event arrives.
|
||||
|
||||
* 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 are received.
|
||||
|
||||
##### Permalinks
|
||||
|
||||
When creating [links](/appendices/#uris) to events (also known as permalinks),
|
||||
clients build links which reference the event 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 reference to the original
|
||||
event, and then display the most recent version of that event.
|
||||
|
||||
#### Redactions of edited events
|
||||
|
||||
When an event using a `rel_type` of `m.replace` is [redacted](#redactions), 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, and as
|
||||
[above](#server-side-aggregation-of-mreplace-relationships) the replacement
|
||||
events will not be bundled with the original event. Note that the subsequent edits are
|
||||
not actually redacted themselves: they simply serve no purpose within the visible timeline.
|
||||
|
||||
#### Edits of replies
|
||||
|
||||
Some particular constraints apply to events which replace a
|
||||
[reply](#rich-replies). In particular:
|
||||
|
||||
* In contrast to the original reply, 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 the event relationships mechanism which expects only one
|
||||
"parent" per event.
|
||||
|
||||
* `m.new_content` should **not** contain any [reply
|
||||
fallback](#fallbacks-for-rich-replies),
|
||||
since it is assumed that any client which can handle edits can also display
|
||||
replies natively. However, the `content` of the replacement event should provide
|
||||
fallback content for clients which support neither rich replies nor edits.
|
||||
|
||||
An example of an edit to a reply is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.room.message",
|
||||
// irrelevant fields not shown
|
||||
"content": {
|
||||
"body": "> <@alice:example.org> question\n\n* reply",
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!somewhere:example.org/$event:example.org\">In reply to</a> <a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a><br />question</blockquote></mx-reply>* reply",
|
||||
"m.new_content": {
|
||||
"body": "reply",
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "reply"
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": "$original_reply_event"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -107,7 +107,19 @@ determined by the push rules which apply to an event.
|
|||
|
||||
When the user updates their read receipt (either by using the API or by
|
||||
sending an event), notifications prior to and including that event MUST
|
||||
be marked as read.
|
||||
be marked as read. Note that users can send both an `m.read` and
|
||||
`m.read.private` receipt, both of which are capable of clearing notifications.
|
||||
|
||||
If the user has both `m.read` and `m.read.private` set in the room then
|
||||
the receipt which is more recent/ahead must be used to determine where
|
||||
the user has read up to. For example, given an oldest-first set of events A,
|
||||
B, C, and D the `m.read` receipt could be at event C and `m.read.private`
|
||||
at event A - the user is considered to have read up to event C. If the
|
||||
`m.read.private` receipt is then updated to point to B or C, the user's
|
||||
notification state doesn't change (the `m.read` receipt is still more
|
||||
ahead), however if the `m.read.private` receipt were to be updated to
|
||||
event D then the user has read up to D (the `m.read` receipt is now
|
||||
behind the `m.read.private` receipt).
|
||||
|
||||
##### Push Rules
|
||||
|
||||
|
|
@ -768,7 +780,7 @@ per-device using the APIs below.
|
|||
##### Push Rules: Events
|
||||
|
||||
When a user changes their push rules a `m.push_rules` event is sent to
|
||||
all clients in the `account_data` section of their next `/sync` request.
|
||||
all clients in the `account_data` section of their next [`/sync`](#get_matrixclientv3sync) request.
|
||||
The content of the event is the current push rules for the user.
|
||||
|
||||
{{% event event="m.push_rules" %}}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,16 @@ The client cannot update fully read markers by directly modifying the
|
|||
`m.fully_read` account data event. Instead, the client must make use of
|
||||
the read markers API to change the values.
|
||||
|
||||
{{< changed-in v="1.4" >}} `m.read.private` receipts can now be sent from
|
||||
`/read_markers`.
|
||||
|
||||
The read markers API can additionally update the user's read receipt
|
||||
(`m.read`) location in the same operation as setting the fully read
|
||||
marker location. This is because read receipts and read markers are
|
||||
commonly updated at the same time, and therefore the client might wish
|
||||
to save an extra HTTP call. Providing an `m.read` location performs the
|
||||
same task as a request to `/receipt/m.read/$event:example.org`.
|
||||
(`m.read` or `m.read.private`) location in the same operation as setting
|
||||
the fully read marker location. This is because read receipts and read
|
||||
markers are commonly updated at the same time, and therefore the client
|
||||
might wish to save an extra HTTP call. Providing `m.read` and/or
|
||||
`m.read.private` performs the same task as a request to
|
||||
[`/receipt/{receiptType}/{eventId}`](#post_matrixclientv3roomsroomidreceiptreceipttypeeventid).
|
||||
|
||||
{{% http-api spec="client-server" api="read_markers" %}}
|
||||
|
||||
|
|
@ -44,8 +48,9 @@ same task as a request to `/receipt/m.read/$event:example.org`.
|
|||
|
||||
The server MUST prevent clients from setting `m.fully_read` directly in
|
||||
room account data. The server must additionally ensure that it treats
|
||||
the presence of `m.read` in the `/read_markers` request the same as how
|
||||
it would for a request to `/receipt/m.read/$event:example.org`.
|
||||
the presence of `m.read` and `m.read.private` in the `/read_markers`
|
||||
request the same as how it would for a request to
|
||||
[`/receipt/{receiptType}/{eventId}`](#post_matrixclientv3roomsroomidreceiptreceipttypeeventid).
|
||||
|
||||
Upon updating the `m.fully_read` event due to a request to
|
||||
`/read_markers`, the server MUST send the updated account data event
|
||||
|
|
|
|||
|
|
@ -4,10 +4,14 @@ type: module
|
|||
|
||||
### Receipts
|
||||
|
||||
{{< changed-in v="1.4" >}} Added private read receipts.
|
||||
|
||||
This module adds in support for receipts. These receipts are a form of
|
||||
acknowledgement of an event. This module defines a single
|
||||
acknowledgement: `m.read` which indicates that the user has read up to a
|
||||
given event.
|
||||
acknowledgement of an event. This module defines the `m.read` receipt
|
||||
for indicating that the user has read up to a given event, and `m.read.private`
|
||||
to achieve the same purpose without any other user being aware. Primarily,
|
||||
`m.read.private` is intended to clear [notifications](#receiving-notifications)
|
||||
without advertising read-up-to status to others.
|
||||
|
||||
Sending a receipt for each event can result in sending large amounts of
|
||||
traffic to a homeserver. To prevent this from becoming a problem,
|
||||
|
|
@ -59,12 +63,36 @@ following HTTP APIs.
|
|||
|
||||
{{% http-api spec="client-server" api="receipts" %}}
|
||||
|
||||
##### Private read receipts
|
||||
|
||||
{{% added-in v="1.4" %}}
|
||||
|
||||
Some users would like to mark a room as read, clearing their [notification counts](#receiving-notifications),
|
||||
but not give away the fact that they've read a particular message yet. To
|
||||
achieve this, clients can send `m.read.private` receipts instead of `m.read`
|
||||
to do exactly that: clear notifications and not broadcast the receipt to
|
||||
other users.
|
||||
|
||||
Servers MUST NOT send the `m.read.private` receipt to any other user than the
|
||||
one which originally sent it.
|
||||
|
||||
Between `m.read` and `m.read.private`, the receipt which is more "ahead" or
|
||||
"recent" is used when determining the highest read-up-to mark. See the
|
||||
[notifications](#receiving-notifications) section for more information on
|
||||
how this affects notification counts.
|
||||
|
||||
If a client sends an `m.read` receipt which is "behind" the `m.read.private`
|
||||
receipt, other users will see that change happen but the sending user will
|
||||
not have their notification counts rewound to that point in time. While
|
||||
uncommon, it is considered valid to have an `m.read` (public) receipt lag
|
||||
several messages behind the `m.read.private` receipt, for example.
|
||||
|
||||
#### Server behaviour
|
||||
|
||||
For efficiency, receipts SHOULD be batched into one event per room
|
||||
before delivering them to clients.
|
||||
|
||||
Receipts are sent across federation as EDUs with type `m.receipt`. The
|
||||
Some receipts are sent across federation as EDUs with type `m.receipt`. The
|
||||
format of the EDUs are:
|
||||
|
||||
```
|
||||
|
|
@ -80,7 +108,8 @@ format of the EDUs are:
|
|||
```
|
||||
|
||||
These are always sent as deltas to previously sent receipts. Currently
|
||||
only a single `<receipt_type>` should be used: `m.read`.
|
||||
only a single `<receipt_type>` should be used: `m.read`. `m.read.private`
|
||||
MUST NOT appear in this federated `m.receipt` EDU.
|
||||
|
||||
#### Security considerations
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ clients can access, depending on what keys are given to them.
|
|||
##### Key storage
|
||||
|
||||
Each key has an ID, and the description of the key is stored in the
|
||||
user's account\_data using the event type
|
||||
user's account data using the event type
|
||||
`m.secret_storage.key.[key ID]`. The contents of the account data for
|
||||
the key will include an `algorithm` property, which indicates the
|
||||
encryption algorithm used, as well as a `name` property, which is a
|
||||
|
|
@ -51,7 +51,7 @@ Other properties depend on the encryption algorithm, and are described
|
|||
below.
|
||||
|
||||
A key can be marked as the "default" key by setting the user's
|
||||
account\_data with event type `m.secret_storage.default_key` to an
|
||||
account data with event type `m.secret_storage.default_key` to an
|
||||
object that has the ID of the key as its `key` property. The default key
|
||||
will be used to encrypt all secrets that the user would expect to be
|
||||
available on all their clients. Unless the user specifies otherwise,
|
||||
|
|
@ -71,8 +71,8 @@ default key.
|
|||
|
||||
##### Secret storage
|
||||
|
||||
Encrypted data is stored in the user's account\_data using the event
|
||||
type defined by the feature that uses the data. The account\_data will
|
||||
Encrypted data is stored in the user's account data using the event
|
||||
type defined by the feature that uses the data. The account data will
|
||||
have an `encrypted` property that is a map from key ID to an object. The
|
||||
algorithm from the `m.secret_storage.key.[key ID]` data for the given
|
||||
key defines how the other properties are interpreted, though it's
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ securityDefinitions:
|
|||
paths:
|
||||
"/user/{userId}/account_data/{type}":
|
||||
put:
|
||||
summary: Set some account_data for the user.
|
||||
summary: Set some account data for the user.
|
||||
description: |-
|
||||
Set some account_data for the client. This config is only visible to the user
|
||||
that set the account_data. The config will be synced to clients in the
|
||||
top-level `account_data`.
|
||||
Set some account data for the client. This config is only visible to the user
|
||||
that set the account data. The config will be available to clients through the
|
||||
top-level `account_data` field in the homeserver response to
|
||||
[/sync](#get_matrixclientv3sync).
|
||||
operationId: setAccountData
|
||||
security:
|
||||
- accessToken: []
|
||||
|
|
@ -43,7 +44,7 @@ paths:
|
|||
name: userId
|
||||
required: true
|
||||
description: |-
|
||||
The ID of the user to set account_data for. The access token must be
|
||||
The ID of the user to set account data for. The access token must be
|
||||
authorized to make requests for this user ID.
|
||||
x-example: "@alice:example.com"
|
||||
- in: path
|
||||
|
|
@ -51,14 +52,14 @@ paths:
|
|||
name: type
|
||||
required: true
|
||||
description: |-
|
||||
The event type of the account_data to set. Custom types should be
|
||||
The event type of the account data to set. Custom types should be
|
||||
namespaced to avoid clashes.
|
||||
x-example: "org.example.custom.config"
|
||||
- in: body
|
||||
name: content
|
||||
required: true
|
||||
description: |-
|
||||
The content of the account_data
|
||||
The content of the account data.
|
||||
schema:
|
||||
type: object
|
||||
example: {
|
||||
|
|
@ -66,7 +67,7 @@ paths:
|
|||
responses:
|
||||
200:
|
||||
description:
|
||||
The account_data was successfully added.
|
||||
The account data was successfully added.
|
||||
examples:
|
||||
application/json: {}
|
||||
schema:
|
||||
|
|
@ -107,10 +108,10 @@ paths:
|
|||
tags:
|
||||
- User data
|
||||
get:
|
||||
summary: Get some account_data for the user.
|
||||
summary: Get some account data for the user.
|
||||
description: |-
|
||||
Get some account_data for the client. This config is only visible to the user
|
||||
that set the account_data.
|
||||
Get some account data for the client. This config is only visible to the user
|
||||
that set the account data.
|
||||
operationId: getAccountData
|
||||
security:
|
||||
- accessToken: []
|
||||
|
|
@ -120,7 +121,7 @@ paths:
|
|||
name: userId
|
||||
required: true
|
||||
description: |-
|
||||
The ID of the user to get account_data for. The access token must be
|
||||
The ID of the user to get account data for. The access token must be
|
||||
authorized to make requests for this user ID.
|
||||
x-example: "@alice:example.com"
|
||||
- in: path
|
||||
|
|
@ -128,7 +129,7 @@ paths:
|
|||
name: type
|
||||
required: true
|
||||
description: |-
|
||||
The event type of the account_data to get. Custom types should be
|
||||
The event type of the account data to get. Custom types should be
|
||||
namespaced to avoid clashes.
|
||||
x-example: "org.example.custom.config"
|
||||
responses:
|
||||
|
|
@ -157,7 +158,7 @@ paths:
|
|||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Room account data not found."
|
||||
"error": "Account data not found."
|
||||
}
|
||||
schema:
|
||||
$ref: "../client-server/definitions/errors/error.yaml"
|
||||
|
|
@ -165,11 +166,11 @@ paths:
|
|||
- User data
|
||||
"/user/{userId}/rooms/{roomId}/account_data/{type}":
|
||||
put:
|
||||
summary: Set some account_data for the user that is specific to a room.
|
||||
summary: Set some account data for the user that is specific to a room.
|
||||
description: |-
|
||||
Set some account_data for the client on a given room. This config is only
|
||||
visible to the user that set the account_data. The config will be synced to
|
||||
clients in the per-room `account_data`.
|
||||
Set some account data for the client on a given room. This config is only
|
||||
visible to the user that set the account data. The config will be delivered to
|
||||
clients in the per-room entries via [/sync](#get_matrixclientv3sync).
|
||||
operationId: setAccountDataPerRoom
|
||||
security:
|
||||
- accessToken: []
|
||||
|
|
@ -179,7 +180,7 @@ paths:
|
|||
name: userId
|
||||
required: true
|
||||
description: |-
|
||||
The ID of the user to set account_data for. The access token must be
|
||||
The ID of the user to set account data for. The access token must be
|
||||
authorized to make requests for this user ID.
|
||||
x-example: "@alice:example.com"
|
||||
- in: path
|
||||
|
|
@ -187,21 +188,21 @@ paths:
|
|||
name: roomId
|
||||
required: true
|
||||
description: |-
|
||||
The ID of the room to set account_data on.
|
||||
The ID of the room to set account data on.
|
||||
x-example: "!726s6s6q:example.com"
|
||||
- in: path
|
||||
type: string
|
||||
name: type
|
||||
required: true
|
||||
description: |-
|
||||
The event type of the account_data to set. Custom types should be
|
||||
The event type of the account data to set. Custom types should be
|
||||
namespaced to avoid clashes.
|
||||
x-example: "org.example.custom.room.config"
|
||||
- in: body
|
||||
name: content
|
||||
required: true
|
||||
description: |-
|
||||
The content of the account_data
|
||||
The content of the account data.
|
||||
schema:
|
||||
type: object
|
||||
example: {
|
||||
|
|
@ -209,7 +210,7 @@ paths:
|
|||
responses:
|
||||
200:
|
||||
description:
|
||||
The account_data was successfully added.
|
||||
The account data was successfully added.
|
||||
examples:
|
||||
application/json: {}
|
||||
schema:
|
||||
|
|
@ -251,10 +252,10 @@ paths:
|
|||
tags:
|
||||
- User data
|
||||
get:
|
||||
summary: Get some account_data for the user that is specific to a room.
|
||||
summary: Get some account data for the user that is specific to a room.
|
||||
description: |-
|
||||
Get some account_data for the client on a given room. This config is only
|
||||
visible to the user that set the account_data.
|
||||
Get some account data for the client on a given room. This config is only
|
||||
visible to the user that set the account data.
|
||||
operationId: getAccountDataPerRoom
|
||||
security:
|
||||
- accessToken: []
|
||||
|
|
@ -264,7 +265,7 @@ paths:
|
|||
name: userId
|
||||
required: true
|
||||
description: |-
|
||||
The ID of the user to set account_data for. The access token must be
|
||||
The ID of the user to get account data for. The access token must be
|
||||
authorized to make requests for this user ID.
|
||||
x-example: "@alice:example.com"
|
||||
- in: path
|
||||
|
|
@ -272,14 +273,14 @@ paths:
|
|||
name: roomId
|
||||
required: true
|
||||
description: |-
|
||||
The ID of the room to get account_data for.
|
||||
The ID of the room to get account data for.
|
||||
x-example: "!726s6s6q:example.com"
|
||||
- in: path
|
||||
type: string
|
||||
name: type
|
||||
required: true
|
||||
description: |-
|
||||
The event type of the account_data to get. Custom types should be
|
||||
The event type of the account data to get. Custom types should be
|
||||
namespaced to avoid clashes.
|
||||
x-example: "org.example.custom.room.config"
|
||||
responses:
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ properties:
|
|||
}
|
||||
transaction_id:
|
||||
description: |
|
||||
The client-supplied transaction ID, for example, provided via
|
||||
The client-supplied [transaction ID](/client-server-api/#transaction-identifiers), for example, provided via
|
||||
`PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}`,
|
||||
if the client being given the event is the same one which sent it.
|
||||
type: string
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ paths:
|
|||
post:
|
||||
summary: Start the requesting user participating in a particular room.
|
||||
description: |-
|
||||
*Note that this API takes either a room ID or alias, unlike* `/room/{roomId}/join`.
|
||||
*Note that this API takes either a room ID or alias, unlike* `/rooms/{roomId}/join`.
|
||||
|
||||
This API starts a user participating in a particular room, if that user
|
||||
is allowed to participate in that room. After this call, the client is
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ paths:
|
|||
x-example: "@alice:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: The avatar URL for this user.
|
||||
description: The profile information for this user.
|
||||
examples:
|
||||
application/json: {
|
||||
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# Copyright 2018 New Vector Ltd
|
||||
# Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
|
@ -56,6 +57,9 @@ paths:
|
|||
The event ID the read marker should be located at. The
|
||||
event MUST belong to the room.
|
||||
example: "$somewhere:example.org"
|
||||
x-changedInMatrixVersion:
|
||||
1.4: |
|
||||
This property is no longer required.
|
||||
"m.read":
|
||||
type: string
|
||||
description: |-
|
||||
|
|
@ -63,11 +67,18 @@ paths:
|
|||
equivalent to calling `/receipt/m.read/$elsewhere:example.org`
|
||||
and is provided here to save that extra call.
|
||||
example: "$elsewhere:example.org"
|
||||
required: ['m.fully_read']
|
||||
"m.read.private":
|
||||
x-addedInMatrixVersion: "1.4"
|
||||
type: string
|
||||
description: |-
|
||||
The event ID to set the *private* read receipt location at. This
|
||||
equivalent to calling `/receipt/m.read.private/$elsewhere:example.org`
|
||||
and is provided here to save that extra call.
|
||||
example: "$elsewhere:example.org"
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
The read marker, and read receipt if provided, have been updated.
|
||||
The read marker, and read receipt(s) if provided, have been updated.
|
||||
schema:
|
||||
type: object
|
||||
properties: {}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# Copyright 2016 OpenMarket Ltd
|
||||
# Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
|
@ -46,10 +47,19 @@ paths:
|
|||
- in: path
|
||||
type: string
|
||||
name: receiptType
|
||||
description: The type of receipt to send.
|
||||
description: |-
|
||||
The type of receipt to send. This can also be `m.fully_read` as an
|
||||
alternative to [`/read_makers`](/client-server-api/#post_matrixclientv3roomsroomidread_markers).
|
||||
|
||||
Note that `m.fully_read` does not appear under `m.receipt`: this endpoint
|
||||
effectively calls `/read_markers` internally when presented with a receipt
|
||||
type of `m.fully_read`.
|
||||
required: true
|
||||
x-example: "m.read"
|
||||
enum: ["m.read"]
|
||||
x-changedInMatrixVersion:
|
||||
1.4: |
|
||||
Allow `m.read.private` receipts and `m.fully_read` markers to be set.
|
||||
enum: ["m.read", "m.read.private", "m.fully_read"]
|
||||
- in: path
|
||||
type: string
|
||||
name: eventId
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ paths:
|
|||
name: txnId
|
||||
type: string
|
||||
description: |-
|
||||
The transaction ID for this event. Clients should generate a
|
||||
The [transaction ID](/client-server-api/#transaction-identifiers) for this event. Clients should generate a
|
||||
unique ID; it will be used by the server to ensure idempotency of requests.
|
||||
required: true
|
||||
x-example: "37"
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ paths:
|
|||
name: txnId
|
||||
type: string
|
||||
description: |-
|
||||
The transaction ID for this event. Clients should generate an
|
||||
The [transaction ID](/client-server-api/#transaction-identifiers) for this event. Clients should generate an
|
||||
ID unique across requests with the same access token; it will be
|
||||
used by the server to ensure idempotency of requests.
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ paths:
|
|||
name: txnId
|
||||
type: string
|
||||
description: |-
|
||||
The transaction ID for this event. Clients should generate an
|
||||
The [transaction ID](/client-server-api/#transaction-identifiers) for this event. Clients should generate an
|
||||
ID unique across requests with the same access token; it will be
|
||||
used by the server to ensure idempotency of requests.
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
"@rikj:jki.re": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
},
|
||||
"m.read.private": {
|
||||
"@self:example.org": {
|
||||
"ts": 1661384801651
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,54 @@
|
|||
{
|
||||
"type": "object",
|
||||
"title": "Receipt Event",
|
||||
"description": "Informs the client of new receipts.",
|
||||
"allOf": [{
|
||||
"$ref": "core-event-schema/event.yaml"
|
||||
}],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^\\$": {
|
||||
"type": "object",
|
||||
"x-pattern": "$EVENT_ID",
|
||||
"title": "Receipts",
|
||||
"description": "The mapping of event ID to a collection of receipts for this event ID. The event ID is the ID of the event being acknowledged and *not* an ID for the receipt itself.",
|
||||
"properties": {
|
||||
"m.read": {
|
||||
"type": "object",
|
||||
"title": "Users",
|
||||
"description": "A collection of users who have sent `m.read` receipts for this event.",
|
||||
"patternProperties": {
|
||||
"^@": {
|
||||
"type": "object",
|
||||
"title": "Receipt",
|
||||
"description": "The mapping of user ID to receipt. The user ID is the entity who sent this receipt.",
|
||||
"x-pattern": "$USER_ID",
|
||||
"properties": {
|
||||
"ts": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "The timestamp the receipt was sent at."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["m.receipt"]
|
||||
}
|
||||
},
|
||||
"required": ["type", "content"]
|
||||
}
|
||||
type: object
|
||||
title: Receipt Event
|
||||
description: Informs the client of new receipts.
|
||||
x-changedInMatrixVersion:
|
||||
1.4: |
|
||||
Added `m.read.private` receipts to the event's `content`.
|
||||
allOf:
|
||||
- $ref: "core-event-schema/event.yaml"
|
||||
properties:
|
||||
content:
|
||||
type: object
|
||||
patternProperties:
|
||||
"^\\$":
|
||||
type: object
|
||||
x-pattern: "$EVENT_ID"
|
||||
title: Receipts
|
||||
description: |-
|
||||
The mapping of event ID to a collection of receipts for this
|
||||
event ID. The event ID is the ID of the event being acknowledged
|
||||
and *not* an ID for the receipt itself.
|
||||
properties:
|
||||
"m.read":
|
||||
type: object
|
||||
title: Users
|
||||
description: |-
|
||||
A collection of users who have sent `m.read` receipts for
|
||||
this event.
|
||||
patternProperties:
|
||||
"^@": &receiptUserMap
|
||||
type: object
|
||||
title: Receipt
|
||||
description: |-
|
||||
The mapping of user ID to receipt. The user ID is the
|
||||
entity who sent this receipt.
|
||||
x-pattern: "$USER_ID"
|
||||
properties:
|
||||
ts:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The timestamp the receipt was sent at.
|
||||
"m.read.private":
|
||||
type: object
|
||||
title: Own User
|
||||
description: |-
|
||||
Similar to `m.read`, the users who have sent `m.read.private`
|
||||
receipts for this event. Due to the nature of private read
|
||||
receipts, this should only ever have the current user's ID.
|
||||
patternProperties:
|
||||
"^@": *receiptUserMap
|
||||
additionalProperties: false
|
||||
type:
|
||||
type: string
|
||||
enum: ["m.receipt", "m.receipt.private"]
|
||||
required: ["type", "content"]
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
*/}}
|
||||
{{ if reflect.IsMap $property.additionalProperties }}
|
||||
{{ if $property.additionalProperties.title }}
|
||||
{{ $type = delimit (slice "{ string: " $property.additionalProperties.title "}" ) "" }}
|
||||
{{ $type = delimit (slice "{string: " $property.additionalProperties.title "}" ) "" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
|
@ -89,7 +89,7 @@
|
|||
<td>
|
||||
{{ if $required }}<strong>Required: </strong>{{end -}}
|
||||
{{ $property.description | markdownify -}}
|
||||
{{ if eq $type "enum"}}<p>One of: <code>{{ $property.enum }}</code>.</p>{{ end -}}
|
||||
{{ if eq $type "enum"}}<p>One of: <code>[{{ delimit $property.enum ", " }}]</code>.</p>{{ end -}}
|
||||
{{ if (index $property "x-addedInMatrixVersion") }}{{ partial "added-in" (dict "v" (index $property "x-addedInMatrixVersion")) }}{{ end -}}
|
||||
{{ if (index $property "x-changedInMatrixVersion") }}{{ partial "changed-in" (dict "changes_dict" (index $property "x-changedInMatrixVersion")) }}{{ end -}}
|
||||
</td>
|
||||
|
|
|
|||
Loading…
Reference in a new issue