Merge branch 'main' into travis/ref-rel

This commit is contained in:
Travis Ralston 2022-10-12 14:02:36 -06:00
commit 0add77dea9
83 changed files with 959 additions and 218 deletions

View file

@ -1 +0,0 @@
Add HTML anchors for object definitions in the formatted specification.

View file

@ -1 +0,0 @@
Replace homeserver authorization approach with an `Authorization` header instead of `access_token` when talking to the application service, as per [MSC2832](https://github.com/matrix-org/matrix-spec-proposals/pull/2832).

View file

@ -1 +0,0 @@
Mention that the `/rooms/{roomId}/invite` endpoint will return a 200 response if the user is already invited to the room.

View file

@ -1 +0,0 @@
Describe return codes for account data endpoints, and clarify that per-room data does not inherit from the global data.

View file

@ -1 +0,0 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Clarify that policy rule globs work like ACL globs. Contributed by Nico.

View file

@ -1 +0,0 @@
Clarify the format of some structures in the End-to-end encryption module.

View file

@ -1 +0,0 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Add HTML anchors for object definitions in the formatted specification.

View file

@ -1 +0,0 @@
Tweak the styling of `<code>` snippets in tables rendered from OpenAPI definitions.

View file

@ -1 +0,0 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Update "API Standards" section to clarify how JSON is used.

View file

@ -1 +0,0 @@
Add a `.m.rule.room.server_acl` push rule to match `m.room.server_acl` events, as per [MSC3786](https://github.com/matrix-org/matrix-spec-proposals/pull/3786).

View file

@ -1 +0,0 @@
Remove unused policy room sharing mechanism, as per [MSC3844](https://github.com/matrix-org/matrix-spec-proposals/pull/3844).

View file

@ -1 +0,0 @@
Add `Cross-Origin-Resource-Policy` (CORP) headers to media repository, as per [MSC3828](https://github.com/matrix-org/matrix-spec-proposals/pull/3828).

View file

@ -1 +0,0 @@
Copy a room's `type` when upgrading it, as per [MSC3818](https://github.com/matrix-org/matrix-spec-proposals/pull/3818).

View file

@ -1 +0,0 @@
Add `room_types` filter and `room_type` response to `/publicRooms`, as per [MSC3827](https://github.com/matrix-org/matrix-spec-proposals/pull/3827).

View file

@ -1 +0,0 @@
Add a `.m.rule.room.server_acl` push rule to match `m.room.server_acl` events, as per [MSC3786](https://github.com/matrix-org/matrix-spec-proposals/pull/3786).

View file

@ -1 +0,0 @@
Clarify that the "device_id", "user_id" and "access_token" fields are required in the response body of `POST /_matrix/client/v3/login`.

View file

@ -1 +0,0 @@
Add `m.replace` relations (event edits), as per [MSC2676](https://github.com/matrix-org/matrix-spec-proposals/pull/2676).

View file

@ -1 +0,0 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Add `m.read.private` receipts, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285).

View file

@ -1 +0,0 @@
Make `m.fully_read` optional on `/read_markers`, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285).

View file

@ -1 +0,0 @@
Allow `m.fully_read` markers to be set from `/receipts`, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285).

View file

@ -1 +0,0 @@
Reinforce the relationship of refreshed access tokens to transaction IDs.

View file

@ -1 +0,0 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Clarify enum values by separating possible values with commas.

View file

@ -0,0 +1 @@
Fix naming of `device_one_time_keys_count` in `/sync`.

View file

@ -0,0 +1 @@
Fix various typos throughout the specification.

View file

@ -1 +0,0 @@
Add HTML anchors for object definitions in the formatted specification.

View file

@ -1 +0,0 @@
Update "API Standards" section to clarify how JSON is used.

View file

@ -1 +0,0 @@
Render HTML anchors for object definition tables.

View file

@ -1 +0,0 @@
Add internal changes changelog section.

View file

@ -1 +0,0 @@
Give rendered-data sections a background and some padding.

View file

@ -1 +0,0 @@
Fix rendering of shortcodes within the client-server API.

View file

@ -1 +0,0 @@
Fix the spacing of mapping types generated from the OpenAPI spec.

View file

@ -1 +0,0 @@
Add HTML anchors for object definitions in the formatted specification.

View file

@ -1 +0,0 @@
For room versions 1 through 10, clarify that events with rejected `auth_events` must be rejected.

View file

@ -1 +0,0 @@
For room versions 210: correct a mistaken clarification to the state resolution algorithm.

View file

@ -1 +0,0 @@
For room versions 7 through 10: Clarify that `invite->knock` is actually a legal transition.

View file

@ -0,0 +1 @@
Reword the event auth rules to clarify that users cannot demote other users with the same power level.

View file

@ -0,0 +1 @@
Various clarifications to the text on event authorisation rules.

View file

@ -1 +0,0 @@
Add HTML anchors for object definitions in the formatted specification.

View file

@ -1 +0,0 @@
Tweak the styling of `<code>` snippets in tables rendered from OpenAPI definitions.

View file

@ -1 +0,0 @@
Update "API Standards" section to clarify how JSON is used.

View file

@ -49,9 +49,9 @@ status = "unstable"
current_version_url = "https://spec.matrix.org/latest"
# The following is used when status = "stable", and is displayed in various UI elements on a released version
# of the spec. CI will set these values here automatically when a release git tag (i.e `v1.5`) is created.
#major = "1"
#minor = "3"
#release_date = "June 16, 2022"
# major = "1"
# minor = "4"
# release_date = "September 29, 2022"
# User interface configuration
[params.ui]

View file

@ -14,7 +14,7 @@ underlying homeserver implementation.
## Application Services
Application services are passive and can only observe events from
Application services are passive and can only observe events from the
homeserver. They can inject events into rooms they are participating in.
They cannot prevent events from being sent, nor can they modify the
content of the event being sent. In order to observe events from a

View file

@ -9,6 +9,7 @@ weight: 1000
{{% changelog/changelog-changes %}}
<!-- DO NOT REMOVE OR CHANGE - Release script puts next release here -->
{{% changelog/changelog-rendered p="changelogs/v1.4.md" %}}
{{% changelog/changelog-rendered p="changelogs/v1.3.md" %}}
{{% changelog/changelog-rendered p="changelogs/v1.2.md" %}}
{{% changelog/changelog-rendered p="changelogs/v1.1.md" %}}

View file

@ -1972,6 +1972,7 @@ This specification describes the following relationship types:
* [Rich replies](#rich-replies) (**Note**: does not use `rel_type`).
* [Event replacements](#event-replacements).
* [Threads](#threading).
* [References](#reference-relations)
#### Aggregations
@ -1988,11 +1989,6 @@ of times that `key` was used by child events.
The actual aggregation format depends on the `rel_type`.
{{% boxes/note %}}
This specification does not currently describe any `rel_type`s which require
aggregation. This functionality forms a framework for future extensions.
{{% /boxes/note %}}
Aggregations are sometimes automatically included by a server alongside the parent
event. This is known as a "bundled aggregation" or "bundle" for simplicity. The
act of doing this is "bundling".
@ -2057,6 +2053,7 @@ The endpoints where the server *should* include bundled aggregations are:
* [`GET /sync`](#get_matrixclientv3sync) when the relevant section has a `limited` value
of `true`.
* [`POST /search`](#post_matrixclientv3search) for any matching events under `room_events`.
* {{< added-in v="1.4" >}} [`GET /rooms/{roomId}/threads`](#get_matrixclientv1roomsroomidthreads)
{{% boxes/note %}}
The server is **not** required to return bundled aggregations on deprecated endpoints
@ -2643,4 +2640,5 @@ systems.
{{< cs-module name="moderation_policies" >}}
{{< cs-module name="spaces" >}}
{{< cs-module name="event_replacements" >}}
{{< cs-module name="threading" >}}
{{< cs-module name="reference_relations" >}}

View file

@ -1622,7 +1622,7 @@ specified). The client is expected to use [`/keys/query`](/client-server-api/#po
sync, as documented in [Tracking the device list for a
user](#tracking-the-device-list-for-a-user).
It also adds a `one_time_keys_count` property. Note the spelling
It also adds a `device_one_time_keys_count` property. Note the spelling
difference with the `one_time_key_counts` property in the
[`/keys/upload`](/client-server-api/#post_matrixclientv3keysupload) response.

View file

@ -47,8 +47,9 @@ Not all attributes on those tags should be permitted as they may be
avenues for other disruption attempts, such as adding `onclick` handlers
or excessively large text. Clients should only permit the attributes
listed for the tags below. Where `data-mx-bg-color` and `data-mx-color`
are listed, clients should translate the value (a 6-character hex color
code) to the appropriate CSS/attributes for the tag.
are listed, clients should translate the value (a `#` character followed
by a 6-character hex color code) to the appropriate CSS/attributes for
the tag.
`font`
`data-mx-bg-color`, `data-mx-color`, `color`

View file

@ -107,8 +107,10 @@ 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. Note that users can send both an `m.read` and
`m.read.private` receipt, both of which are capable of clearing notifications.
be marked as read. Which specific events are affected can vary depending
on whether a [threaded read receipt](#threaded-read-receipts) was used.
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
@ -121,6 +123,17 @@ 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).
{{< added-in v="1.4" >}} When handling threaded read receipts, the server
is to partition the notification count to each thread (with the main timeline
being its own thread). To determine if an event is part of a thread the
server follows the [event relationship](#forming-relationships-between-events)
until it finds a thread root (as specified by the [threading module](#threading)),
however it is not recommended that the server traverse infinitely. Instead,
implementations are encouraged to do a maximum of 3 hops to find a thread
before deciding that the event does not belong to a thread. This is primarily
to ensure that future events, like `m.reaction`, are correctly considered
"part of" a given thread.
##### Push Rules
A push rule is a single rule that states under what *conditions* an

View file

@ -22,33 +22,68 @@ that the user had read all events *up to* the referenced event. See the
[Receiving notifications](#receiving-notifications) section for more
information on how read receipts affect notification counts.
{{< added-in v="1.4" >}} Read receipts exist in three major forms:
* Unthreaded: Denotes a read-up-to receipt regardless of threads. This is how
pre-threading read receipts worked.
* Threaded, main timeline: Denotes a read-up-to receipt for events not in a
particular thread. Identified by the thread ID `main`.
* Threaded, in a thread: Denotes a read-up-to receipt within a particular
thread. Identified by the event ID of the thread root.
Threaded read receipts are discussed in further detail [below](#threaded-read-receipts).
#### Events
Each `user_id`, `receipt_type` pair must be associated with only a
single `event_id`.
{{< changed-in v="1.4" >}} Each `user_id`, `receipt_type`, and categorisation
(unthreaded, or `thread_id`) tuple must be associated with only a single
`event_id`.
{{% event event="m.receipt" %}}
#### Client behaviour
{{< changed-in v="1.4" >}} Altered to support threaded read receipts.
In `/sync`, receipts are listed under the `ephemeral` array of events
for a given room. New receipts that come down the event streams are
deltas which update existing mappings. Clients should replace older
receipt acknowledgements based on `user_id` and `receipt_type` pairs.
receipt acknowledgements based on `user_id`, `receipt_type`, and the
`thread_id` (if present).
For example:
Client receives m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $aaa:example.com
thread_id = undefined
Client receives another m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $bbb:example.com
thread_id = main
The client should replace the older acknowledgement for $aaa:example.com with
this one for $bbb:example.com
The client does not replace any acknowledgements, yet.
Client receives yet another m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $ccc:example.com
thread_id = undefined
The client replaces the older acknowledgement for $aaa:example.com
with this new one for $ccc:example.com, but does not replace the
acknowledgement for $bbb:example.com because it belongs to a thread.
Client receives yet another m.receipt:
user = @alice:example.com
receipt_type = m.read
event_id = $ddd:example.com
thread_id = main
Now the client replaces the older $bbb:example.com acknowledgement with
this new $ddd:example.com acknowledgement. The client does NOT replace the
older acknowledgement for $ccc:example.com as it is unthreaded.
Clients should send read receipts when there is some certainty that the
event in question has been **displayed** to the user. Simply receiving
@ -58,6 +93,12 @@ room that the event was sent to or dismissing a notification in order
for the event to count as "read". Clients SHOULD NOT send read receipts
for events sent by their own user.
Similar to the rules for sending receipts, threaded receipts should appear
in the context of the thread. If a thread is rendered behind a disclosure,
the client hasn't yet shown the event (or any applicable read receipts)
to the user. Once they expand the thread though, a threaded read receipt
would be sent and per-thread receipts from other users shown.
A client can update the markers for its user by interacting with the
following HTTP APIs.
@ -87,6 +128,89 @@ 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.
##### Threaded read receipts
{{% added-in v="1.4" %}}
If a client does not use [threading](#threading), then they will simply only
send "unthreaded" read receipts which affect the whole room regardless of threads.
A threaded read receipt is simply one which has a `thread_id` on it, targeting
either a thread root's event ID or `main` for the main timeline.
Threading introduces a concept of multiple conversations being held in the same
room and thus deserve their own read receipts and notification counts. An event is
considered to be "in a thread" if it meets any of the following criteria:
* It has a `rel_type` of `m.thread`.
* It has child events with a `rel_type` of `m.thread` (in which case it'd be the
thread root).
* Following the event relationships, it has a parent event which qualifies for
one of the above. Implementations should not recurse infinitely, though: a
maximum of 3 hops is recommended to cover indirect relationships.
Events not in a thread but still in the room are considered to be part of the
"main timeline", or a special thread with an ID of `main`.
The following is an example DAG for a room, with dotted lines showing event
relationships and solid lines showing topological ordering.
![threaded-dag](/diagrams/threaded-dag.png)
{{% boxes/note %}}
`m.reaction` relationships are not currently specified, but are shown here for
their conceptual place in a threaded DAG. They are currently proposed as
[MSC2677](https://github.com/matrix-org/matrix-spec-proposals/pull/2677).
{{% /boxes/note %}}
This DAG can be represented as 3 threaded timelines, with `A` and `B` being thread
roots:
![threaded-dag-threads](/diagrams/threaded-dag-threads.png)
With this, we can demonstrate that:
* A threaded read receipt on `I` would mark `A`, `B`, and `I` as read.
* A threaded read receipt on `E` would mark `C` and `E` as read.
* An unthreaded read receipt on `D` would mark `A`, `B`, `C`, and `D` as read.
Note that marking `A` as read with a threaded read receipt would not mean
that `C`, `E`, `G`, or `H` get marked as read: Thread A's timeline would need
its own threaded read receipt at `H` to accomplish that.
The read receipts for the above 3 examples would be:
```json
{
"$I": {
"m.read": {
"@user:example.org": {
"ts": 1661384801651,
"thread_id": "main" // because `I` is not in a thread, but is a threaded receipt
}
}
},
"$E": {
"m.read": {
"@user:example.org": {
"ts": 1661384801651,
"thread_id": "$A" // because `E` is in Thread `A`
}
}
},
"$D": {
"m.read": {
"@user:example.org": {
"ts": 1661384801651
// no `thread_id` because the receipt is *unthreaded*
}
}
}
}
```
Conditions on sending read receipts apply similarly to threaded and unthreaded read
receipts. For example, a client might send a private read receipt for a threaded
event when the user expands that thread.
#### Server behaviour
For efficiency, receipts SHOULD be batched into one event per room
@ -99,7 +223,7 @@ format of the EDUs are:
{
<room_id>: {
<receipt_type>: {
<user_id>: { <content> }
<user_id>: { <content (ts & thread_id, currently)> }
},
...
},

View file

@ -0,0 +1,201 @@
---
type: module
---
### Threading
{{% added-in v="1.4" %}}
Threads allow users to visually branch their conversations in a room. Typically mostly used
when a room is discussing multiple topics, threads provide more organisation of communication
that traditional [rich replies](#rich-replies) can't always offer.
Clients SHOULD render threads differently to regular messages or replies in the timeline, such
as by providing some context to what is going on in the thread but keeping the full conversation
history behind a disclosure.
Threads are established using a `rel_type` of `m.thread` and reference the *thread root* (the
first event in a thread). It is not possible to create a thread from an event with a `rel_type`,
which includes not being able to nest threads. All conversation in a thread reference the thread
root instead of the most recent message, unlike rich reply chains.
As a worked example, the following represents a thread and how it'd be formed:
```json
{
// irrelevant fields excluded
"type": "m.room.message",
"event_id": "$alice_hello",
"sender": "@alice:example.org",
"content": {
"msgtype": "m.text",
"body": "Hello world! How are you?"
}
}
```
```json
{
// irrelevant fields excluded
"type": "m.room.message",
"event_id": "$bob_hello",
"sender": "@bob:example.org",
"content": {
"m.relates_to": {
"rel_type": "m.thread",
"event_id": "$alice_hello"
},
"msgtype": "m.text",
"body": "I'm doing okay, thank you! How about yourself?"
}
}
```
```json
{
// irrelevant fields excluded
"type": "m.room.message",
"event_id": "$alice_reply",
"sender": "@alice:example.org",
"content": {
"m.relates_to": {
"rel_type": "m.thread",
"event_id": "$alice_hello" // note: always references the *thread root*
},
"msgtype": "m.text",
"body": "I'm doing great! Thanks for asking."
}
}
```
As shown, any event without a `rel_type` can become a thread root by simply referencing it
using an `m.thread` relationship.
#### Fallback for unthreaded clients
Clients which understand how to work with threads should simply do so, however clients which
might not be aware of threads (due to age or scope) might not be able to helpfully represent
the conversation history to its users.
To work around this, events sent by clients which understand threads include [rich reply](#rich-replies)
metadata to attempt to form a reply chain representation of the conversation. This representation
is not ideal for heavily threaded rooms, but allows for users to have context as to what is
being discussed with respect to other messages in the room.
This representation is achieved by merging the two relationships and setting a new `is_falling_back`
flag to `true`.
```json
// within an event's content...
"m.relates_to": {
// The m.thread relationship structure
"rel_type": "m.thread",
"event_id": "$root",
// The rich reply structure
"m.in_reply_to": {
// The most recent message known to the client in the thread.
// This should be something with a high chance of being rendered by the other client,
// such as an `m.room.message` event.
"event_id": "$target"
},
// A flag to denote that this is a thread with reply fallback
"is_falling_back": true
}
```
For `m.room.message` events represented this way, no [reply fallback](#fallbacks-for-rich-replies)
is specified. This allows thread-aware clients to discard the `m.in_reply_to` object entirely
when `is_falling_back` is `true`.
{{% boxes/note %}}
Clients which are acutely aware of threads (they do not render threads, but are otherwise
aware of the feature existing in the spec) can treat rich replies to an event with a `rel_type`
of `m.thread` as a threaded reply, for conversation continuity on the threaded client's side.
To do this, copy the `event_id` (thread root) from the event being replied to, add the
`m.in_reply_to` metadata, and add `is_falling_back: true` to `m.relates_to`.
{{% /boxes/note %}}
#### Replies within threads
In the [fallback for unthreaded clients](#fallback-for-unthreaded-clients) section, a new
`is_falling_back` flag is added to `m.relates_to`. This flag defaults to `false` when not
provided, which also allows a threaded message to contain a reply itself.
Aside from `is_falling_back` being `false` (or not specified), the fallback for unthreaded
clients is used to create a reply within a thread: clients should render the event accordingly.
#### Server behaviour
##### Validation of `m.thread` relationships
Servers SHOULD reject client requests which attempt to start a thread off an event with a
`rel_type`. If the client attempts to target an event which already has an `m.thread`,
`m.reference`, or any other `rel_type` then it should receive a HTTP 400 error response
with appropriate error message, as per the [standard error response](#standard-error-response)
structure.
{{% boxes/note %}}
A specific error code is not currently available for this case: servers should use `M_UNKNOWN`
alongside the HTTP 400 status code.
{{% /boxes/note %}}
##### Server-side aggregation of `m.thread` relationships
Given threads always reference the thread root, an event can have multiple "child" events which
then form the thread itself. These events should be [aggregated](#aggregations) by the server.
The aggregation for threads includes some information about the user's participation in the thread,
the approximate number of events in the thread (as known to the server), and the most recent event
in the thread (topologically). This is then bundled into the event as `m.thread`:
```json
{
"event_id": "$root_event",
// irrelevant fields not shown
"unsigned": {
"m.relations": {
"m.thread": {
"latest_event": {
// A serialized copy of the latest event in the thread.
// Some fields are not shown here for brevity.
"event_id": "$message",
"sender": "@alice:example.org",
"room_id": "!room:example.org",
"type": "m.room.message",
"content": {
"msgtype": "m.text",
"body": "Woo! Threads!"
}
},
"count": 7,
"current_user_participated": true
}
}
}
}
```
`latest_event` is the most recent event (topologically to the server) in the thread sent by an
un-[ignored user](#ignoring-users).
Note that any bundled aggregations on `latest_event` should also be present. The server should be
careful to avoid loops, though loops are not currently possible due to `m.thread` not being possible
to target an event with a `rel_type` already.
`count` is simply the number of events using `m.thread` as a `rel_type` pointing to the target event.
It does not include events sent by [ignored users](#ignoring-users).
`current_user_participated` is `true` when the authenticated user is either:
1. The `sender` of the event receiving the bundle (they sent the thread root).
2. The `sender` of an event which references the thread root with a `rel_type` of `m.thread`.
#### Querying threads in a room
Clients looking to get all the events in a thread can use
[`GET /relations/{threadRootId}/m.thread`](#get_matrixclientv1roomsroomidrelationseventidreltype),
however getting all threads in a room is done through a dedicated API:
{{% http-api spec="client-server" api="threads_list" %}}

View file

@ -6,8 +6,9 @@ type: module
Users may wish to be informed when another user is typing in a room.
This can be achieved using typing notifications. These are ephemeral
events scoped to a `room_id`. This means they do not form part of the
[Event Graph](index.html#event-graphs) but still have a `room_id` key.
events, so they do not form part of the
[Event Graph](index.html#event-graphs). Typing notifications are scoped
to a room.
#### Events

View file

@ -19,12 +19,12 @@ the default power level for users in the room.
The rules are as follows:
1. If type is `m.room.create`:
1. If it has any previous events, reject.
1. If it has any `prev_events`, reject.
2. If the domain of the `room_id` does not match the domain of the
`sender`, reject.
3. If `content.room_version` is present and is not a recognised
version, reject.
4. If `content` has no `creator` field, reject.
4. If `content` has no `creator` property, reject.
5. Otherwise, allow.
2. Considering the event's `auth_events`:
1. If there are duplicate entries for a given `type` and `state_key` pair,
@ -45,7 +45,8 @@ The rules are as follows:
2. If sender's domain doesn't matches `state_key`, reject.
3. Otherwise, allow.
5. If type is `m.room.member`:
1. If no `state_key` key or `membership` key in `content`, reject.
1. If there is no `state_key` property, or no `membership` property in
`content`, reject.
2. If `membership` is `join`:
1. If the only previous event is an `m.room.create` and the
`state_key` is the creator, allow.
@ -56,11 +57,11 @@ The rules are as follows:
5. If the `join_rule` is `public`, allow.
6. Otherwise, reject.
3. If `membership` is `invite`:
1. If `content` has `third_party_invite` key:
1. If `content` has a `third_party_invite` property:
1. If *target user* is banned, reject.
2. If `content.third_party_invite` does not have a `signed`
key, reject.
3. If `signed` does not have `mxid` and `token` keys,
property, reject.
3. If `signed` does not have `mxid` and `token` properties,
reject.
4. If `mxid` does not match `state_key`, reject.
5. If there is no `m.room.third_party_invite` event in the
@ -71,8 +72,8 @@ The rules are as follows:
7. If any signature in `signed` matches any public key in
the `m.room.third_party_invite` event, allow. The public
keys are in `content` of `m.room.third_party_invite` as:
1. A single public key in the `public_key` field.
2. A list of public keys in the `public_keys` field.
1. A single public key in the `public_key` property.
2. A list of public keys in the `public_keys` property.
8. Otherwise, reject.
2. If the `sender`'s current membership state is not `join`,
reject.
@ -110,29 +111,32 @@ The rules are as follows:
9. If the event has a `state_key` that starts with an `@` and does not
match the `sender`, reject.
10. If type is `m.room.power_levels`:
1. If `users` key in `content` is not a dictionary with keys that
1. If the `users` property in `content` is not an object with keys that
are valid user IDs with values that are integers (or a string
that is an integer), reject.
2. If there is no previous `m.room.power_levels` event in the room,
allow.
3. For the keys `users_default`, `events_default`, `state_default`,
3. For the properties `users_default`, `events_default`, `state_default`,
`ban`, `redact`, `kick`, `invite` check if they were added,
changed or removed. For each found alteration:
1. If the current value is higher than the `sender`'s current
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
2. If the new value is greater than the `sender`'s current power
level, reject.
4. For each entry being added, changed or removed in both the
`events` and `users` keys:
1. If the current value is higher than the `sender`'s current
4. For each entry being changed in, or removed from, the `events` property:
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
5. For each entry being added to, or changed in, the `events` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
5. For each entry being changed under the `users` key, other than
the `sender`'s own entry:
1. If the current value is equal to the `sender`'s current
power level, reject.
6. Otherwise, allow.
6. For each entry being changed in, or removed from, the `users` property,
other than the `sender`'s own entry:
1. If the current value is greater than or equal to the `sender`'s
current power level, reject.
7. For each entry being added to, or changed in, the `users` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
8. Otherwise, allow.
11. If type is `m.room.redaction`:
1. If the `sender`'s power level is greater than or equal to the
*redact level*, allow.

View file

@ -2,11 +2,11 @@
toc_hide: true
---
{{% added-in this=true %}} In room versions 1 and 2, events need a
{{< added-in this=true >}} In room versions 1 and 2, events need a
signature from the domain of the `event_id` in order to be considered
valid. This room version does not include an `event_id` over federation
in the same respect, so does not need a signature from that server.
The event must still be signed by the server denoted by the `sender`,
The event must still be signed by the server denoted by the `sender` property,
however.
The types of state events that affect authorization are:
@ -26,12 +26,12 @@ the default power level for users in the room.
The complete list of rules, as of room version 3, is as follows:
1. If type is `m.room.create`:
1. If it has any previous events, reject.
1. If it has any `prev_events`, reject.
2. If the domain of the `room_id` does not match the domain of the
`sender`, reject.
3. If `content.room_version` is present and is not a recognised
version, reject.
4. If `content` has no `creator` field, reject.
4. If `content` has no `creator` property, reject.
5. Otherwise, allow.
2. Considering the event's `auth_events`:
1. If there are duplicate entries for a given `type` and `state_key` pair,
@ -52,7 +52,8 @@ The complete list of rules, as of room version 3, is as follows:
2. If sender's domain doesn't matches `state_key`, reject.
3. Otherwise, allow.
5. If type is `m.room.member`:
1. If no `state_key` key or `membership` key in `content`, reject.
1. If there is no `state_key` property, or no `membership` property in
`content`, reject.
2. If `membership` is `join`:
1. If the only previous event is an `m.room.create` and the
`state_key` is the creator, allow.
@ -63,11 +64,11 @@ The complete list of rules, as of room version 3, is as follows:
5. If the `join_rule` is `public`, allow.
6. Otherwise, reject.
3. If `membership` is `invite`:
1. If `content` has `third_party_invite` key:
1. If `content` has a `third_party_invite` property:
1. If *target user* is banned, reject.
2. If `content.third_party_invite` does not have a `signed`
key, reject.
3. If `signed` does not have `mxid` and `token` keys,
property, reject.
3. If `signed` does not have `mxid` and `token` properties,
reject.
4. If `mxid` does not match `state_key`, reject.
5. If there is no `m.room.third_party_invite` event in the
@ -78,8 +79,8 @@ The complete list of rules, as of room version 3, is as follows:
7. If any signature in `signed` matches any public key in
the `m.room.third_party_invite` event, allow. The public
keys are in `content` of `m.room.third_party_invite` as:
1. A single public key in the `public_key` field.
2. A list of public keys in the `public_keys` field.
1. A single public key in the `public_key` property.
2. A list of public keys in the `public_keys` property.
8. Otherwise, reject.
2. If the `sender`'s current membership state is not `join`,
reject.
@ -117,29 +118,32 @@ The complete list of rules, as of room version 3, is as follows:
9. If the event has a `state_key` that starts with an `@` and does not
match the `sender`, reject.
10. If type is `m.room.power_levels`:
1. If `users` key in `content` is not a dictionary with keys that
1. If `users` property in `content` is not an object with keys that
are valid user IDs with values that are integers (or a string
that is an integer), reject.
2. If there is no previous `m.room.power_levels` event in the room,
allow.
3. For the keys `users_default`, `events_default`, `state_default`,
3. For the properties `users_default`, `events_default`, `state_default`,
`ban`, `redact`, `kick`, `invite` check if they were added,
changed or removed. For each found alteration:
1. If the current value is higher than the `sender`'s current
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
2. If the new value is greater than the `sender`'s current power
level, reject.
4. For each entry being added, changed or removed in both the
`events` and `users` keys:
1. If the current value is higher than the `sender`'s current
4. For each entry being changed in, or removed from, the `events` property:
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
5. For each entry being added to, or changed in, the `events` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
5. For each entry being changed under the `users` key, other than
the `sender`'s own entry:
1. If the current value is equal to the `sender`'s current
power level, reject.
6. Otherwise, allow.
6. For each entry being changed in, or removed from, the `users` property,
other than the `sender`'s own entry:
1. If the current value is greater than or equal to the `sender`'s
current power level, reject.
7. For each entry being added to, or changed in, the `users` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
8. Otherwise, allow.
11. Otherwise, allow.
{{% boxes/note %}}

View file

@ -2,7 +2,7 @@
toc_hide: true
---
Events must be signed by the server denoted by the `sender` key.
Events must be signed by the server denoted by the `sender` property.
`m.room.redaction` events are not explicitly part of the auth rules.
They are still subject to the minimum power level rules, but should always
@ -27,12 +27,12 @@ the default power level for users in the room.
The rules are as follows:
1. If type is `m.room.create`:
1. If it has any previous events, reject.
1. If it has any `prev_events`, reject.
2. If the domain of the `room_id` does not match the domain of the
`sender`, reject.
3. If `content.room_version` is present and is not a recognised
version, reject.
4. If `content` has no `creator` field, reject.
4. If `content` has no `creator` property, reject.
5. Otherwise, allow.
2. Considering the event's `auth_events`:
1. If there are duplicate entries for a given `type` and `state_key` pair,
@ -49,9 +49,10 @@ The rules are as follows:
property `m.federate` set to `false`, and the `sender` domain of the event
does not match the `sender` domain of the create event, reject.
4. If type is `m.room.member`:
1. If no `state_key` key or `membership` key in `content`, reject.
2. If `content` has a `join_authorised_via_users_server`
key:
1. If there is no `state_key` property, or no `membership` property in
`content`, reject.
2. {{< added-in this=true >}}
If `content` has a `join_authorised_via_users_server` property:
1. If the event is not validly signed by the homeserver of the user ID denoted
by the key, reject.
3. If `membership` is `join`:
@ -61,7 +62,8 @@ The rules are as follows:
3. If the `sender` is banned, reject.
4. If the `join_rule` is `invite` or `knock` then allow if
membership state is `invite` or `join`.
5. If the `join_rule` is `restricted`:
5. {{< added-in this=true >}}
If the `join_rule` is `restricted`:
1. If membership state is `join` or `invite`, allow.
2. If the `join_authorised_via_users_server` key in `content`
is not a user with sufficient permission to invite other
@ -70,11 +72,11 @@ The rules are as follows:
6. If the `join_rule` is `public`, allow.
7. Otherwise, reject.
4. If `membership` is `invite`:
1. If `content` has `third_party_invite` key:
1. If `content` has a `third_party_invite` property:
1. If *target user* is banned, reject.
2. If `content.third_party_invite` does not have a `signed`
key, reject.
3. If `signed` does not have `mxid` and `token` keys,
property, reject.
3. If `signed` does not have `mxid` and `token` properties,
reject.
4. If `mxid` does not match `state_key`, reject.
5. If there is no `m.room.third_party_invite` event in the
@ -85,8 +87,8 @@ The rules are as follows:
7. If any signature in `signed` matches any public key in
the `m.room.third_party_invite` event, allow. The public
keys are in `content` of `m.room.third_party_invite` as:
1. A single public key in the `public_key` field.
2. A list of public keys in the `public_keys` field.
1. A single public key in the `public_key` property.
2. A list of public keys in the `public_keys` property.
8. Otherwise, reject.
2. If the `sender`'s current membership state is not `join`,
reject.
@ -130,29 +132,34 @@ The rules are as follows:
8. If the event has a `state_key` that starts with an `@` and does not
match the `sender`, reject.
9. If type is `m.room.power_levels`:
1. If `users` key in `content` is not a dictionary with keys that
1. If the `users` property in `content` is not an object with keys that
are valid user IDs with values that are integers (or a string
that is an integer), reject.
2. If there is no previous `m.room.power_levels` event in the room,
allow.
3. For the keys `users_default`, `events_default`, `state_default`,
3. For the properties `users_default`, `events_default`, `state_default`,
`ban`, `redact`, `kick`, `invite` check if they were added,
changed or removed. For each found alteration:
1. If the current value is higher than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
level, reject.
4. For each entry being added, changed or removed in both the
`events`, `users`, and `notifications` keys:
1. If the current value is higher than the `sender`'s current
4. For each entry being changed in, or removed from, the `events` or
`notifications` properties:
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
5. For each entry being added to, or changed in the `events` or
`notifications` properties:
1. If the new value is greater than the `sender`'s current power
level, reject.
5. For each entry being changed under the `users` key, other than
the `sender`'s own entry:
1. If the current value is equal to the `sender`'s current
power level, reject.
6. Otherwise, allow.
6. For each entry being changed in, or removed from, the `users` property,
other than the `sender`'s own entry:
1. If the current value is greater than or equal to the `sender`'s
current power level, reject.
7. For each entry being added to, or changed in, the `users` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
8. Otherwise, allow.
10. Otherwise, allow.
{{% boxes/note %}}

View file

@ -74,7 +74,7 @@ correctly structured are rejected under the authorization rules below.
### Authorization rules
Events must be signed by the server denoted by the `sender` key.
Events must be signed by the server denoted by the `sender` property.
`m.room.redaction` events are not explicitly part of the auth rules.
They are still subject to the minimum power level rules, but should always
@ -99,12 +99,12 @@ the default power level for users in the room.
The rules are as follows:
1. If type is `m.room.create`:
1. If it has any previous events, reject.
1. If it has any `prev_events`, reject.
2. If the domain of the `room_id` does not match the domain of the
`sender`, reject.
3. If `content.room_version` is present and is not a recognised
version, reject.
4. If `content` has no `creator` field, reject.
4. If `content` has no `creator` property, reject.
5. Otherwise, allow.
2. Considering the event's `auth_events`:
1. If there are duplicate entries for a given `type` and `state_key` pair,
@ -121,7 +121,8 @@ The rules are as follows:
property `m.federate` set to `false`, and the `sender` domain of the event
does not match the `sender` domain of the create event, reject.
4. If type is `m.room.member`:
1. If no `state_key` key or `membership` key in `content`, reject.
1. If there is no `state_key` property, or no `membership` property in
`content`, reject.
2. If `content` has a `join_authorised_via_users_server`
key:
1. If the event is not validly signed by the homeserver of the user ID denoted
@ -143,11 +144,11 @@ The rules are as follows:
6. If the `join_rule` is `public`, allow.
7. Otherwise, reject.
4. If `membership` is `invite`:
1. If `content` has `third_party_invite` key:
1. If `content` has a `third_party_invite` property:
1. If *target user* is banned, reject.
2. If `content.third_party_invite` does not have a `signed`
key, reject.
3. If `signed` does not have `mxid` and `token` keys,
property, reject.
3. If `signed` does not have `mxid` and `token` properties,
reject.
4. If `mxid` does not match `state_key`, reject.
5. If there is no `m.room.third_party_invite` event in the
@ -158,8 +159,8 @@ The rules are as follows:
7. If any signature in `signed` matches any public key in
the `m.room.third_party_invite` event, allow. The public
keys are in `content` of `m.room.third_party_invite` as:
1. A single public key in the `public_key` field.
2. A list of public keys in the `public_keys` field.
1. A single public key in the `public_key` property.
2. A list of public keys in the `public_keys` property.
8. Otherwise, reject.
2. If the `sender`'s current membership state is not `join`,
reject.
@ -206,35 +207,40 @@ The rules are as follows:
match the `sender`, reject.
9. If type is `m.room.power_levels`:
1. {{< added-in this="true" >}}
If any of the keys `users_default`, `events_default`, `state_default`,
If any of the properties `users_default`, `events_default`, `state_default`,
`ban`, `redact`, `kick`, or `invite` in `content` are present and
not an integer, reject.
2. {{< added-in this="true" >}}
If either of the keys `events` or `notifications` in `content`
are present and not a dictionary with values that are integers,
If either of the properties `events` or `notifications` in `content`
are present and not an object with values that are integers,
reject.
3. If `users` key in `content` is not a dictionary with keys that
3. If the `users` property in `content` is not an obiect with keys that
are valid user IDs with values that are integers, reject.
2. If there is no previous `m.room.power_levels` event in the room,
4. If there is no previous `m.room.power_levels` event in the room,
allow.
3. For the keys `users_default`, `events_default`, `state_default`,
5. For the properties `users_default`, `events_default`, `state_default`,
`ban`, `redact`, `kick`, `invite` check if they were added,
changed or removed. For each found alteration:
1. If the current value is higher than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
level, reject.
4. For each entry being added, changed or removed in both the
`events`, `users`, and `notifications` keys:
1. If the current value is higher than the `sender`'s current
6. For each entry being changed in, or removed from, the `events` or
`notifications` properties:
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
7. For each entry being added to, or changed in, the `events` or
`notifications` properties:
1. If the new value is greater than the `sender`'s current power
level, reject.
5. For each entry being changed under the `users` key, other than
the `sender`'s own entry:
1. If the current value is equal to the `sender`'s current
power level, reject.
6. Otherwise, allow.
8. For each entry being changed in, or removed from, the `users` property,
other than the `sender`'s own entry:
1. If the current value is greater than or equal to the `sender`'s
current power level, reject.
9. For each entry being added to, or changed in, the `users` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
10. Otherwise, allow.
10. Otherwise, allow.
{{% boxes/note %}}

View file

@ -89,7 +89,7 @@ The complete structure of a event in a v3 room is shown below.
### Authorization rules
{{% added-in this=true %}} `m.room.redaction` events are no longer
{{< added-in this=true >}} `m.room.redaction` events are no longer
explicitly part of the auth rules. They are still subject to the
minimum power level rules, but should always fall into "11. Otherwise,
allow". Instead of being authorized at the time of receipt, they are
@ -97,7 +97,7 @@ authorized at a later stage: see the [Handling Redactions](#handling-redactions)
section below for more information.
<!-- set withVersioning=true so we get all the "new in this version" stuff -->
{{% rver-fragment name="v3-auth-rules" withVersioning=true %}}
{{< rver-fragment name="v3-auth-rules" withVersioning=true >}}
## Unchanged from v2

View file

@ -46,16 +46,16 @@ fall into "10. Otherwise, allow". Instead of being authorized at the time
of receipt, they are authorized at a later stage: see the
[Handling Redactions](#handling-redactions) section below for more information.
{{% added-in this=true %}} Rule 4, which related specifically to events
{{< added-in this=true >}} Rule 4, which related specifically to events
of type `m.room.aliases`, is removed. `m.room.aliases` events must still pass
authorization checks relating to state events.
{{% added-in this=true %}} Additionally, the authorization rules for events
of type `m.room.power_levels` now include the content key `notifications`.
This new rule takes the place of rule 10.4, which checked the `events` and
`users` keys.
{{< added-in this=true >}} Additionally, the authorization rules for events of
type `m.room.power_levels` now include a `notifications` property under
`content`. This updates rules 10.4 and 10.5 (now 9.4 and 9.5), which checked
the `events` property.
Events must be signed by the server denoted by the `sender` key.
Events must be signed by the server denoted by the `sender` property.
The types of state events that affect authorization are:
@ -74,12 +74,12 @@ the default power level for users in the room.
The rules are as follows:
1. If type is `m.room.create`:
1. If it has any previous events, reject.
1. If it has any `prev_events`, reject.
2. If the domain of the `room_id` does not match the domain of the
`sender`, reject.
3. If `content.room_version` is present and is not a recognised
version, reject.
4. If `content` has no `creator` field, reject.
4. If `content` has no `creator` property, reject.
5. Otherwise, allow.
2. Reject if event has `auth_events` that:
1. have duplicate entries for a given `type` and `state_key` pair
@ -90,7 +90,8 @@ The rules are as follows:
3. If event does not have a `m.room.create` in its `auth_events`,
reject.
4. If type is `m.room.member`:
1. If no `state_key` key or `membership` key in `content`, reject.
1. If there is no `state_key` property, or no `membership` property in
`content`, reject.
2. If `membership` is `join`:
1. If the only previous event is an `m.room.create` and the
`state_key` is the creator, allow.
@ -101,11 +102,11 @@ The rules are as follows:
5. If the `join_rule` is `public`, allow.
6. Otherwise, reject.
3. If `membership` is `invite`:
1. If `content` has `third_party_invite` key:
1. If `content` has a `third_party_invite` property:
1. If *target user* is banned, reject.
2. If `content.third_party_invite` does not have a `signed`
key, reject.
3. If `signed` does not have `mxid` and `token` keys,
property, reject.
3. If `signed` does not have `mxid` and `token` properties,
reject.
4. If `mxid` does not match `state_key`, reject.
5. If there is no `m.room.third_party_invite` event in the
@ -116,8 +117,8 @@ The rules are as follows:
7. If any signature in `signed` matches any public key in
the `m.room.third_party_invite` event, allow. The public
keys are in `content` of `m.room.third_party_invite` as:
1. A single public key in the `public_key` field.
2. A list of public keys in the `public_keys` field.
1. A single public key in the `public_key` property.
2. A list of public keys in the `public_keys` property.
8. Otherwise, reject.
2. If the `sender`'s current membership state is not `join`,
reject.
@ -155,29 +156,36 @@ The rules are as follows:
8. If the event has a `state_key` that starts with an `@` and does not
match the `sender`, reject.
9. If type is `m.room.power_levels`:
1. If `users` key in `content` is not a dictionary with keys that
1. If the `users` property in `content` is not an object with keys that
are valid user IDs with values that are integers (or a string
that is an integer), reject.
2. If there is no previous `m.room.power_levels` event in the room,
allow.
3. For the keys `users_default`, `events_default`, `state_default`,
3. For the properties `users_default`, `events_default`, `state_default`,
`ban`, `redact`, `kick`, `invite` check if they were added,
changed or removed. For each found alteration:
1. If the current value is higher than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
level, reject.
4. For each entry being added, changed or removed in both the
`events`, `users`, and `notifications` keys:
1. If the current value is higher than the `sender`'s current
4. {{< changed-in this="true" >}}
For each entry being changed in, or removed from, the `events` or
`notifications` properties:
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
5. {{< changed-in this="true" >}}
For each entry being added to, or changed in, the `events` or
`notifications` properties:
1. If the new value is greater than the `sender`'s current power
level, reject.
5. For each entry being changed under the `users` key, other than
the `sender`'s own entry:
1. If the current value is equal to the `sender`'s current
power level, reject.
6. Otherwise, allow.
6. For each entry being changed in, or removed from, the `users` property,
other than the `sender`'s own entry:
1. If the current value is greater than or equal to the `sender`'s
current power level, reject.
7. For each entry being added to, or changed in, the `users` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
8. Otherwise, allow.
10. Otherwise, allow.
{{% boxes/note %}}

View file

@ -32,10 +32,10 @@ as do the versions v6 is based upon.
### Authorization rules
{{% added-in this=true %}} For checks performed upon `m.room.member` events, a
{{< added-in this=true >}} For checks performed upon `m.room.member` events, a
new point for `membership=knock` is added.
Events must be signed by the server denoted by the `sender` key.
Events must be signed by the server denoted by the `sender` property.
`m.room.redaction` events are not explicitly part of the auth rules.
They are still subject to the minimum power level rules, but should always
@ -60,12 +60,12 @@ the default power level for users in the room.
The rules are as follows:
1. If type is `m.room.create`:
1. If it has any previous events, reject.
1. If it has any `prev_events`, reject.
2. If the domain of the `room_id` does not match the domain of the
`sender`, reject.
3. If `content.room_version` is present and is not a recognised
version, reject.
4. If `content` has no `creator` field, reject.
4. If `content` has no `creator` property, reject.
5. Otherwise, allow.
2. Reject if event has `auth_events` that:
1. have duplicate entries for a given `type` and `state_key` pair
@ -76,22 +76,24 @@ The rules are as follows:
3. If event does not have a `m.room.create` in its `auth_events`,
reject.
4. If type is `m.room.member`:
1. If no `state_key` key or `membership` key in `content`, reject.
1. If there is no `state_key` property, or no `membership` property in
`content`, reject.
2. If `membership` is `join`:
1. If the only previous event is an `m.room.create` and the
`state_key` is the creator, allow.
2. If the `sender` does not match `state_key`, reject.
3. If the `sender` is banned, reject.
4. If the `join_rule` is `invite` or `knock` then allow if
4. {{< changed-in this=true >}}
If the `join_rule` is `invite` or `knock` then allow if
membership state is `invite` or `join`.
5. If the `join_rule` is `public`, allow.
6. Otherwise, reject.
3. If `membership` is `invite`:
1. If `content` has `third_party_invite` key:
1. If `content` has `third_party_invite` property:
1. If *target user* is banned, reject.
2. If `content.third_party_invite` does not have a `signed`
key, reject.
3. If `signed` does not have `mxid` and `token` keys,
property, reject.
3. If `signed` does not have `mxid` and `token` properties,
reject.
4. If `mxid` does not match `state_key`, reject.
5. If there is no `m.room.third_party_invite` event in the
@ -102,8 +104,8 @@ The rules are as follows:
7. If any signature in `signed` matches any public key in
the `m.room.third_party_invite` event, allow. The public
keys are in `content` of `m.room.third_party_invite` as:
1. A single public key in the `public_key` field.
2. A list of public keys in the `public_keys` field.
1. A single public key in the `public_key` property.
2. A list of public keys in the `public_keys` property.
8. Otherwise, reject.
2. If the `sender`'s current membership state is not `join`,
reject.
@ -113,7 +115,8 @@ The rules are as follows:
the *invite level*, allow.
5. Otherwise, reject.
4. If `membership` is `leave`:
1. If the `sender` matches `state_key`, allow if and only if
1. {{< changed-in this=true >}}
If the `sender` matches `state_key`, allow if and only if
that user's current membership state is `invite`, `join`,
or `knock`.
2. If the `sender`'s current membership state is not `join`,
@ -132,7 +135,8 @@ The rules are as follows:
the *ban level*, and the *target user*'s power level is less
than the `sender`'s power level, allow.
3. Otherwise, reject.
6. If `membership` is `knock`:
6. {{< added-in this=true >}}
If `membership` is `knock`:
1. If the `join_rule` is anything other than `knock`, reject.
2. If `sender` does not match `state_key`, reject.
3. If the `sender`'s current membership is not `ban` or `join`, allow.
@ -147,29 +151,34 @@ The rules are as follows:
8. If the event has a `state_key` that starts with an `@` and does not
match the `sender`, reject.
9. If type is `m.room.power_levels`:
1. If `users` key in `content` is not a dictionary with keys that
1. If the `users` property in `content` is not an object with keys that
are valid user IDs with values that are integers (or a string
that is an integer), reject.
2. If there is no previous `m.room.power_levels` event in the room,
allow.
3. For the keys `users_default`, `events_default`, `state_default`,
3. For the properties `users_default`, `events_default`, `state_default`,
`ban`, `redact`, `kick`, `invite` check if they were added,
changed or removed. For each found alteration:
1. If the current value is higher than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
level, reject.
4. For each entry being added, changed or removed in both the
`events`, `users`, and `notifications` keys:
1. If the current value is higher than the `sender`'s current
4. For each entry being changed in, or removed from, the `events` or
`notifications` properties:
1. If the current value is greater than the `sender`'s current
power level, reject.
2. If the new value is higher than the `sender`'s current power
5. For each entry being added to, or changed in, the `events` or
`notifications` properties:
1. If the new value is greater than the `sender`'s current power
level, reject.
5. For each entry being changed under the `users` key, other than
the `sender`'s own entry:
1. If the current value is equal to the `sender`'s current
power level, reject.
6. Otherwise, allow.
6. For each entry being changed in, or removed from, the `users` property,
other than the `sender`'s own entry:
1. If the current value is greater than or equal to the `sender`'s
current power level, reject.
7. For each entry being added to, or changed in, the `users` property:
1. If the new value is greater than the `sender`'s current power
level, reject.
8. Otherwise, allow..
10. Otherwise, allow.
{{% boxes/note %}}

View file

@ -83,11 +83,11 @@ room without invite. Otherwise, the room version inherits all properties of
### Authorization rules
{{% added-in this=true %}} For checks performed upon `m.room.member` events, new
{{< added-in this=true >}} For checks performed upon `m.room.member` events, new
points for handling `content.join_authorised_via_users_server` are added (Rule 4.2
and 4.3.5).
{{% rver-fragment name="v8-auth-rules" %}}
{{< rver-fragment name="v8-auth-rules" withVersioning=true >}}
### Redactions

View file

@ -62,7 +62,7 @@ completeness.
### Authorization rules
{{% rver-fragment name="v8-auth-rules" %}}
{{< rver-fragment name="v8-auth-rules" >}}
### State resolution

View file

@ -34,8 +34,8 @@ properties:
The relationship type determines how clients should perceive the event, and in what
context. Some relationship types are processed server-side for "bundling", though not
all relationships require such behaviour. For example, an `m.thread` relationship type
might denote that the event is part of a "thread" of messages and should be rendered as
all relationships require such behaviour. For example, an [`m.thread` relationship type](/client-server-api/#threading)
denotes that the event is part of a "thread" of messages and should be rendered as
such.
event_id:
type: string

View file

@ -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.
@ -16,6 +17,12 @@ allOf:
- type: object
title: RoomEventFilter
properties:
unread_thread_notifications:
type: boolean
description: |-
If `true`, enables per-[thread](/client-server-api/#threading) notification
counts. Only applies to the `/sync` endpoint. Defaults to `false`.
x-addedInMatrixVersion: "1.4"
lazy_load_members:
type: boolean
description: |-

View file

@ -74,8 +74,18 @@ paths:
required: true
schema:
type: object
properties:
thread_id:
type: string
x-addedInMatrixVersion: "1.4"
description: |-
The root thread event's ID (or `main`) for which
thread this receipt is intended to be under. If
not specified, the read receipt is *unthreaded*
(default).
example: {
}
"thread_id": "main"
}
responses:
200:
description: The receipt was sent.
@ -88,5 +98,19 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
400:
description: |-
The `thread_id` is invalid in some way. For example:
* It is not a string.
* It is empty.
* It is provided for an incompatible receipt type.
* The `event_id` is not related to the `thread_id`.
schema:
$ref: "definitions/errors/error.yaml"
examples:
application/json: {
"errcode": "M_INVALID_PARAM",
"error": "thread_id field must be a non-empty string"
}
tags:
- Room participation

View file

@ -63,7 +63,7 @@ paths:
The pagination token to start returning results from. If not supplied, results
start at the most recent topological event known to the server.
Can be a `next_batch` token from a previous call, or a returned
Can be a `next_batch` or `prev_batch` token from a previous call, or a returned
`start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages),
or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync).
required: false
@ -89,6 +89,16 @@ paths:
Similarly, the server should apply a default value when not supplied.
required: false
x-example: 20
- in: query
type: string
enum: ["b", "f"]
name: dir
x-addedInMatrixVersion: "1.4"
description: |-
Optional (default `b`) direction to return events from. If this is set to `f`, events
will be returned in chronological order starting at `from`. If it
is set to `b`, events will be returned in *reverse* chronological
order, again starting at `from`.
responses:
# note: this endpoint deliberately does not support rate limiting, therefore a
# 429 error response is not included.
@ -193,7 +203,7 @@ paths:
The pagination token to start returning results from. If not supplied, results
start at the most recent topological event known to the server.
Can be a `next_batch` token from a previous call, or a returned
Can be a `next_batch` or `prev_batch` token from a previous call, or a returned
`start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages),
or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync).
required: false
@ -219,6 +229,16 @@ paths:
Similarly, the server should apply a default value when not supplied.
required: false
x-example: 20
- in: query
type: string
enum: ["b", "f"]
name: dir
x-addedInMatrixVersion: "1.4"
description: |-
Optional (default `b`) direction to return events from. If this is set to `f`, events
will be returned in chronological order starting at `from`. If it
is set to `b`, events will be returned in *reverse* chronological
order, again starting at `from`.
responses:
# note: this endpoint deliberately does not support rate limiting, therefore a
# 429 error response is not included.
@ -335,7 +355,7 @@ paths:
The pagination token to start returning results from. If not supplied, results
start at the most recent topological event known to the server.
Can be a `next_batch` token from a previous call, or a returned
Can be a `next_batch` or `prev_batch` token from a previous call, or a returned
`start` token from [`/messages`](/client-server-api/#get_matrixclientv3roomsroomidmessages),
or a `next_batch` token from [`/sync`](/client-server-api/#get_matrixclientv3sync).
required: false
@ -361,6 +381,16 @@ paths:
Similarly, the server should apply a default value when not supplied.
required: false
x-example: 20
- in: query
type: string
enum: ["b", "f"]
name: dir
x-addedInMatrixVersion: "1.4"
description: |-
Optional (default `b`) direction to return events from. If this is set to `f`, events
will be returned in chronological order starting at `from`. If it
is set to `b`, events will be returned in *reverse* chronological
order, again starting at `from`.
responses:
# note: this endpoint deliberately does not support rate limiting, therefore a
# 429 error response is not included.

View file

@ -239,17 +239,50 @@ paths:
Counts of unread notifications for this room. See the
[Receiving notifications](/client-server-api/#receiving-notifications) section
for more information on how these are calculated.
If `unread_thread_notifications` was specified as `true` on the `RoomEventFilter`,
these counts will only be for the main timeline rather than all events in the room.
See the [threading module](#threading) for more information.
x-changedInMatrixVersion:
1.4: |
Updated to reflect behaviour of having `unread_thread_notifications` as `true` in
the `RoomEventFilter` for `/sync`.
properties:
highlight_count:
title: Highlighted notification count
type: integer
description: The number of unread notifications
for this room with the highlight flag set
for this room with the highlight flag set.
notification_count:
title: Total notification count
type: integer
description: The total number of unread notifications
for this room
for this room.
unread_thread_notifications:
title: Unread Thread Notification Counts
type: object
description: |-
If `unread_thread_notifications` was specified as `true` on the `RoomEventFilter`,
the notification counts for each [thread](#threading) in this room. The object is
keyed by thread root ID, with values matching `unread_notifications`.
If a thread does not have any notifications it can be omitted from this object. If
no threads have notification counts, this whole object can be omitted.
x-addedInMatrixVersion: "1.4"
additionalProperties:
title: ThreadNotificationCounts
type: object
properties:
highlight_count:
title: ThreadedHighlightNotificationCount
type: integer
description: |-
The number of unread notifications for this *thread* with the highlight flag set.
notification_count:
title: ThreadedTotalNotificationCount
type: integer
description: |-
The total number of unread notifications for this *thread*.
invite:
title: Invited Rooms
type: object
@ -424,6 +457,16 @@ paths:
}
}
]
},
"unread_notifications": {
"highlight_count": 1,
"notification_count": 5
},
"unread_thread_notifications": {
"$threadroot": {
"highlight_count": 3,
"notification_count": 6
}
}
}
},

View file

@ -0,0 +1,135 @@
# 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.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Client-Server Threads List API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/rooms/{roomId}/threads":
get:
x-addedInMatrixVersion: "1.4"
summary: Retrieve a list of threads in a room, with optional filters.
description: |-
Paginates over the thread roots in a room, ordered by the `latest_event` of each thread root
in its bundle.
operationId: getThreadRoots
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room ID where the thread roots are located.
required: true
x-example: "!room:example.org"
- in: query
type: string
name: include
enum: [all, participated]
description: |-
Optional (default `all`) flag to denote which thread roots are of interest to the caller.
When `all`, all thread roots found in the room are returned. When `participated`, only
thread roots for threads the user has [participated in](/client-server-api/#server-side-aggreagtion-of-mthread-relationships)
will be returned.
x-example: "all"
- in: query
type: integer
name: limit
description: |-
Optional limit for the maximum number of thread roots to include per response. Must be an integer
greater than zero.
Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.
x-example: 20
- in: query
type: string
name: from
description: |-
A pagination token from a previous result. When not provided, the server starts paginating from
the most recent event visible to the user (as per history visibility rules; topologically).
x-example: "next_batch_token"
responses:
200:
description: |-
A portion of the available thread roots in the room, based on the filter criteria.
examples:
application/json: {
"chunk": [{ "$ref": "../../event-schemas/examples/m.room.message$m.text.yaml" }],
"next_batch": "next_batch_token"
}
schema:
type: object
properties:
chunk:
type: array
description: |-
The thread roots, ordered by the `latest_event` in each event's aggregation bundle. All events
returned include bundled [aggregations](/client-server-api/#aggregations).
If the thread root event was sent by an [ignored user](/client-server-api/#ignoring-users), the
event is returned redacted to the caller. This is to simulate the same behaviour of a client doing
aggregation locally on the thread.
items:
$ref: "definitions/client_event.yaml"
next_batch:
type: string
description: |-
A token to supply to `from` to keep paginating the responses. Not present when there are
no further results.
required: [chunk]
403:
description: |-
The user cannot view or peek on the room. A meaningful `errcode`
and description error text will be returned. Example reasons for rejection are:
- The room is not set up for peeking.
- The user has been banned from the room.
- The room does not exist.
examples:
application/json: {
"errcode": "M_FORBIDDEN",
"error": "You are not allowed to view this room."
}
schema:
"$ref": "definitions/errors/error.yaml"
400:
description: |-
The request was invalid in some way. A meaningful `errcode`
and description error text will be returned. Example reasons for rejection are:
- The `from` token is unknown to the server.
examples:
application/json: {
"errcode": "M_INVALID_PARAM",
"error": "Unknown pagination token"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- Threads

View file

@ -65,6 +65,15 @@ allOf:
A POSIX timestamp in milliseconds for when the user read
the event specified in the read receipt.
example: 1533358089009
thread_id:
type: string
x-addedInMatrixVersion: "1.4"
description: |-
The root thread event's ID (or `main`) for which
thread this receipt is intended to be under. If
not specified, the read receipt is *unthreaded*
(default).
example: "$threadroot"
required: ['ts']
required: ['event_ids', 'data']
required: ['m.read']

View file

@ -287,7 +287,7 @@ paths:
items:
$ref: "../../event-schemas/schema/core-event-schema/stripped_state.yaml"
description: |-
An optional list of [stripped state events](/client-server-api/#stripped-state)
A list of [stripped state events](/client-server-api/#stripped-state)
to help the initiator of the knock identify the room.
example:
$ref: "../../event-schemas/examples/knock_room_state.json"

View file

@ -38,6 +38,14 @@ properties:
type: integer
format: int64
description: The timestamp the receipt was sent at.
thread_id:
type: string
x-addedInMatrixVersion: "1.4"
description: |-
The root thread event's ID (or `main`) for which
thread this receipt is intended to be under. If
not specified, the read receipt is *unthreaded*
(default).
"m.read.private":
type: object
title: Own User

View file

@ -0,0 +1,141 @@
<!--
This is a header file for the generated changelog.
Variables:
v1.4 = Replaced by the version number (eg: v1.2)
September 29, 2022 = Replaced by the date (eg: April 01, 2021)
-->
## v1.4
<table class="release-info">
<tr><th>Git commit</th><td><a href="https://github.com/matrix-org/matrix-spec/tree/v1.4">https://github.com/matrix-org/matrix-spec/tree/v1.4</a></td>
<tr><th>Release date</th><td>September 29, 2022</td>
</table>
<!-- Intentionally blank line to ensure headers work in the concatenated changelog -->
### Client-Server API
<strong>Removed Endpoints</strong>
- Remove unused policy room sharing mechanism, as per [MSC3844](https://github.com/matrix-org/matrix-spec-proposals/pull/3844). ([#1196](https://github.com/matrix-org/matrix-spec/issues/1196))
<strong>Backwards Compatible Changes</strong>
- Add a `.m.rule.room.server_acl` push rule to match `m.room.server_acl` events, as per [MSC3786](https://github.com/matrix-org/matrix-spec-proposals/pull/3786). ([#1190](https://github.com/matrix-org/matrix-spec/issues/1190), [#1201](https://github.com/matrix-org/matrix-spec/issues/1201))
- Add `Cross-Origin-Resource-Policy` (CORP) headers to media repository, as per [MSC3828](https://github.com/matrix-org/matrix-spec-proposals/pull/3828). ([#1197](https://github.com/matrix-org/matrix-spec/issues/1197))
- Copy a room's `type` when upgrading it, as per [MSC3818](https://github.com/matrix-org/matrix-spec-proposals/pull/3818). ([#1198](https://github.com/matrix-org/matrix-spec/issues/1198))
- Add `room_types` filter and `room_type` response to `/publicRooms`, as per [MSC3827](https://github.com/matrix-org/matrix-spec-proposals/pull/3827). ([#1199](https://github.com/matrix-org/matrix-spec/issues/1199))
- Add `m.replace` relations (event edits), as per [MSC2676](https://github.com/matrix-org/matrix-spec-proposals/pull/2676). ([#1211](https://github.com/matrix-org/matrix-spec/issues/1211))
- Add `m.read.private` receipts, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285). ([#1216](https://github.com/matrix-org/matrix-spec/issues/1216))
- Make `m.fully_read` optional on `/read_markers`, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285). ([#1216](https://github.com/matrix-org/matrix-spec/issues/1216))
- Allow `m.fully_read` markers to be set from `/receipts`, as per [MSC2285](https://github.com/matrix-org/matrix-spec-proposals/pull/2285). ([#1216](https://github.com/matrix-org/matrix-spec/issues/1216))
- Add threading via `m.thread` relations, as per [MSC3440](https://github.com/matrix-org/matrix-spec-proposals/pull/3440), [MSC3816](https://github.com/matrix-org/matrix-spec-proposals/pull/3816), [MSC3856](https://github.com/matrix-org/matrix-spec-proposals/pull/3856), and [MSC3715](https://github.com/matrix-org/matrix-spec-proposals/pull/3715). ([#1254](https://github.com/matrix-org/matrix-spec/issues/1254))
- Add per-thread notifications and read receipts, as per [MSC3771](https://github.com/matrix-org/matrix-spec-proposals/pull/3771) and [MSC3773](https://github.com/matrix-org/matrix-spec-proposals/pull/3773). ([#1255](https://github.com/matrix-org/matrix-spec/issues/1255))
- Add `thread_id` to the `/receipt` endpoint, as per [MSC3771](https://github.com/matrix-org/matrix-spec-proposals/pull/3771). ([#1261](https://github.com/matrix-org/matrix-spec/issues/1261))
<strong>Spec Clarifications</strong>
- Mention that the `/rooms/{roomId}/invite` endpoint will return a 200 response if the user is already invited to the room. ([#1084](https://github.com/matrix-org/matrix-spec/issues/1084))
- Fix various typos throughout the specification. ([#1135](https://github.com/matrix-org/matrix-spec/issues/1135), [#1161](https://github.com/matrix-org/matrix-spec/issues/1161), [#1164](https://github.com/matrix-org/matrix-spec/issues/1164), [#1170](https://github.com/matrix-org/matrix-spec/issues/1170), [#1180](https://github.com/matrix-org/matrix-spec/issues/1180), [#1215](https://github.com/matrix-org/matrix-spec/issues/1215), [#1238](https://github.com/matrix-org/matrix-spec/issues/1238), [#1243](https://github.com/matrix-org/matrix-spec/issues/1243), [#1263](https://github.com/matrix-org/matrix-spec/issues/1263))
- Describe return codes for account data endpoints, and clarify that per-room data does not inherit from the global data. ([#1155](https://github.com/matrix-org/matrix-spec/issues/1155))
- Clarify that policy rule globs work like ACL globs. Contributed by Nico. ([#1165](https://github.com/matrix-org/matrix-spec/issues/1165))
- Clarify the format of some structures in the End-to-end encryption module. ([#1166](https://github.com/matrix-org/matrix-spec/issues/1166))
- Add HTML anchors for object definitions in the formatted specification. ([#1174](https://github.com/matrix-org/matrix-spec/issues/1174))
- Tweak the styling of `<code>` snippets in tables rendered from OpenAPI definitions. ([#1179](https://github.com/matrix-org/matrix-spec/issues/1179))
- Update "API Standards" section to clarify how JSON is used. ([#1185](https://github.com/matrix-org/matrix-spec/issues/1185))
- Clarify that the "device_id", "user_id" and "access_token" fields are required in the response body of `POST /_matrix/client/v3/login`. ([#1210](https://github.com/matrix-org/matrix-spec/issues/1210))
- Reinforce the relationship of refreshed access tokens to transaction IDs. ([#1236](https://github.com/matrix-org/matrix-spec/issues/1236))
- Clarify enum values by separating possible values with commas. ([#1240](https://github.com/matrix-org/matrix-spec/issues/1240))
### Server-Server API
<strong>Backwards Compatible Changes</strong>
- Add per-thread notifications and read receipts, as per [MSC3771](https://github.com/matrix-org/matrix-spec-proposals/pull/3771) and [MSC3773](https://github.com/matrix-org/matrix-spec-proposals/pull/3773). ([#1255](https://github.com/matrix-org/matrix-spec/issues/1255))
<strong>Spec Clarifications</strong>
- Add HTML anchors for object definitions in the formatted specification. ([#1174](https://github.com/matrix-org/matrix-spec/issues/1174))
- Tweak the styling of `<code>` snippets in tables rendered from OpenAPI definitions. ([#1179](https://github.com/matrix-org/matrix-spec/issues/1179))
- Update "API Standards" section to clarify how JSON is used. ([#1185](https://github.com/matrix-org/matrix-spec/issues/1185))
### Application Service API
<strong>Breaking Changes</strong>
- Replace homeserver authorization approach with an `Authorization` header instead of `access_token` when talking to the application service, as per [MSC2832](https://github.com/matrix-org/matrix-spec-proposals/pull/2832). ([#1200](https://github.com/matrix-org/matrix-spec/issues/1200))
<strong>Spec Clarifications</strong>
- Add HTML anchors for object definitions in the formatted specification. ([#1174](https://github.com/matrix-org/matrix-spec/issues/1174))
### Identity Service API
<strong>Spec Clarifications</strong>
- Add HTML anchors for object definitions in the formatted specification. ([#1174](https://github.com/matrix-org/matrix-spec/issues/1174))
- Update "API Standards" section to clarify how JSON is used. ([#1185](https://github.com/matrix-org/matrix-spec/issues/1185))
### Push Gateway API
<strong>Spec Clarifications</strong>
- Add HTML anchors for object definitions in the formatted specification. ([#1174](https://github.com/matrix-org/matrix-spec/issues/1174))
### Room Versions
<strong>Spec Clarifications</strong>
- For room versions 1 through 10, clarify that events with rejected `auth_events` must be rejected. ([#1137](https://github.com/matrix-org/matrix-spec/issues/1137))
- For room versions 210: correct a mistaken clarification to the state resolution algorithm. ([#1158](https://github.com/matrix-org/matrix-spec/issues/1158))
- For room versions 7 through 10: Clarify that `invite->knock` is actually a legal transition. ([#1175](https://github.com/matrix-org/matrix-spec/issues/1175))
### Appendices
No significant changes.
### Internal Changes/Tooling
<strong>Backwards Compatible Changes</strong>
- Add internal changes changelog section. ([#1194](https://github.com/matrix-org/matrix-spec/issues/1194))
<strong>Spec Clarifications</strong>
- Render HTML anchors for object definition tables. ([#1191](https://github.com/matrix-org/matrix-spec/issues/1191))
- Give rendered-data sections a background and some padding. ([#1195](https://github.com/matrix-org/matrix-spec/issues/1195))
- Fix rendering of shortcodes within the client-server API. ([#1205](https://github.com/matrix-org/matrix-spec/issues/1205))
- Fix the spacing of mapping types generated from the OpenAPI spec. ([#1230](https://github.com/matrix-org/matrix-spec/issues/1230))

View file

@ -21,5 +21,6 @@
{{ $content := $page.Content }}
{{ if not $withVersioning }}
{{ $content = (replace $content "[New in this version]" "") }}
{{ $content = (replace $content "[Changed in this version]" "") }}
{{ end }}
{{ $content | safeHTML }}

View file

@ -83,3 +83,5 @@ is typically best reserved for the next release cycle.
4. Wait for the GitHub Actions build to complete on the tag.
5. Update the assets on the GitHub release to those generated by the latest Actions build.
6. Deploy the release on the webserver. See internal wiki.
7. Remove the changelog entries from `main`, if the changes landed on `main`.
8. Update the github release changelog and changelog on `main`, likely by hand.

View file

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2022-09-27T03:26:23.216Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="YZcXq9Sm_7Lqw5o2RvSU" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7ZpdU+IwFIZ/DZc6bdNWuJQCujPquOvOrl452TbQaNowIXztr9+EJv0goLCirYwyo81JGpL3PQ8nU2mBIFlcMDiOr2mESMuxokUL9FqO41i+I/7IyDKL2JbvZZERw5GKFYE7/BfpgSo6xRGaVAZySgnH42owpGmKQl6JQcbovDpsSEn1XcdwhIzAXQiJGf2NIx5n0bZzVsQvER7F+p1tv5P1JFAPVjuZxDCi81II9FsgYJTy7CpZBIhI9bQu2X2DLb35whhK+S43/PIf6YMfPSfXNzfx1WP3ez/2ToDaxwySqdrxz5ghGInYudQaJ4jgFKkt8KXWhdFpGiE5td0C3XmMObobw1D2zkUqiFjME6K6hzTlA5hgIrPgEpEZ4jiEsgMTElBC2WpS0O/Jl4jPEJMjyDnBo1T0cTpW09ypJajty4FosVURO9dZZCiiCeJsKYbo9NTWqOT0dHteOO2c+acqY+Oyz201FKr8GuWzFxaIC+XCPo742x3pHrsjAFQdcS3TEdu3Nvhhv5sfruHHNcTpsTuRq7zUHJhO5G59jBO2YcS5If0rYsPJOCsSQ7yQBq2LHAR9byDW2D2Egp2qgsDeoOAGAcG76dc25EKRKHaqSRmP6YimkPSLaLeay8WYKyrTbhV8QpwvVeWGU06rkqMF5vfi2lLXD/JafKRmrd6i1NVb6kYq9nuvJ5CN0l2yWdy2aun7Kiz9FHhOxHZv0Fz8/kETmObGyn2/bKuQiU5ZiF7QUx1tOGQjxF/LWzNNGCKQ41l1HQc33TGg6TYZGsdvGDQueDM0T9NkrMenNEU1cFRC56GM1RaOCPyDSBeGz6PVTkpeDwL5eqlsHRAw8BkAAwZgwZsBOwRIbn5u1afbjlczSs4XSnWh5O6IklMnSuZJu9cElMBaTXLdumuSewwg7Xm2awhI3o4ggTpB8gyQ+o0AyW5eTbINYb5Q+iCU/B1RcutEyXwaN2gCSu76g7K6axLoGEIlpwzBkGOaHgNje577IjiJ8+XLxi3kHLF0FXEstzkYnr2toqmEPLFObfFTSUpbfbjvTKqa/ZZisYliCB0OJ2Jp6ymaL+L/s9b878dFE+g2C6Xv1lwoQXsD3yjC/BjY3rN+fh62Ozuy7dVZYs3KcdkECF3QvNOqZQhz/LQ1BCX9ILrZj1D0KkswfWsETO6HPdcXzeJLFdlBofhuCuj/Aw==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2022-09-27T03:11:43.523Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="L_ujIRop4Jndk67DcTE9" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7VpbU+IwFP41PMqkTS/wKDfdGXXcdWdXn5wsDTTaNkwIAvvrN6UJbYlCF1gbXcYZTU5P0ub0u6S1DdiNFxcMTcJrGuCoYYNg0YC9hm3bwLPFnzSyzCIW8NwsMmYkkLE8cEd+Y5UoozMS4GkpkVMacTIpB4c0SfCQl2KIMTovp41oVD7rBI2xFrgbokiP/iQBD7Noy/bz+CUm41Cd2fLa2ZEYqWS5kmmIAjovhGC/AbuMUp614kUXR2n1VF2ycYM3jq4vjOGEVxnww3ukD17wHF/f3IRXj52v/dA9k7O8oGgmF3wur5YvVQnmIeH4boKGaX8u7nMDdkIeR6JniSaaTrLCj8gCi3N15JSYcbx481qtdQUEdjCNMWdLkaIGQFk0BRtH9uf5PVApYaH8KobkXR+vZ84LIxqyNn9Tp5ZWFhwIoMguZTykY5qgqJ9HO4zOkiAtyapOec4VpRMZfMKcLyXq0YzTcmnxgvB70Qay/ZC2m67s9RaFQ72l6iRivfdqgrRTGJV282Grnho3ogkfoJhEaeA7iQXpbHCD5+L3NxqjZH1j03Vvv62iTHTGhnhLPaUscMTGmO/Cpw4ThiPEyUv5Oo5+022NHB0TyGED08jhn8hxTHLAiuSwayWHr7EjbvKQYRQcjIanWTxR+QlN8IEAadpuASNWNYSApu8WQWLtgEiApuF6AWnnFnGOWbKK2MAR0Qj9wlEHDZ/Hq+V2aUTZqkBw0E1/NoF2iaMXzMkQ1QOvWrUXaujqGqG9vmna2z5p7zHJ4VQkB6xVe52Po71F6QXVALIpvWAHQj6O9FZFV63OroOrZ4L0Qsc06XVP0ntMcrgVyeHUKr3ux5He/ba99ifd9laF13YVOgNNB/pSIitDbjXdOWNoWUiYUJLwaeFst2kgP5WjZpSCp577B2/kW7C1LV80sivYGK0uh45GU1GYTYKsS7A/Z3TK9E0wFMeCTdcwS3FOlnJMznsVOe/Wain6e0ZjLWWv3bz9SXfzVdF16G5+L/dwwYa27XAPG7S25Zfd41jO4GnIHxjhDG3THjXgyReOyVy/InO9Wn1BZ0fcFK4w5IQmRjvDf/+KvSq+Dn2LuJ8ztMrOoH04sOkMbbgt/984g/7fpQsTnME17v2/fZASgJMzlOvZrshcv1ZnAK84Aw4If7ddwrvq/puKXcEQpkIeSDL+vloMyAOi54LXbbCA8wp2UgP2an1abWvQuzRBmD3XNGG2tLKchPkAcqgPQneyo10nOyxdmb+YQA///T6oFN38o9ZsO5h/Gwz7fwA=</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB