|
|
|
|
@ -0,0 +1,502 @@
|
|
|
|
|
---
|
|
|
|
|
title: Room Version 12
|
|
|
|
|
type: docs
|
|
|
|
|
weight: 100
|
|
|
|
|
version: 12
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
This room version builds on [version 11](/rooms/v11), iterating on the state resolution
|
|
|
|
|
algorithm, giving room creators infinite power level, and changing the format of room
|
|
|
|
|
IDs to be a hash of the create event.
|
|
|
|
|
|
|
|
|
|
## Client considerations
|
|
|
|
|
|
|
|
|
|
### Event format
|
|
|
|
|
|
|
|
|
|
Clients SHOULD observe the following changes to events in this room version:
|
|
|
|
|
|
|
|
|
|
* Room IDs no longer include a domain component and are instead a hash of the
|
|
|
|
|
`m.room.create` event, per below. See the [room ID grammar](/appendices#room-ids)
|
|
|
|
|
for more information.
|
|
|
|
|
|
|
|
|
|
* A concept of "room creators" is formally defined as the `sender` of the `m.room.create`
|
|
|
|
|
event *plus* any `additional_creators` from the `m.room.create` event's `content`,
|
|
|
|
|
if present. In prior room versions, the only creator was the `sender` of the
|
|
|
|
|
`m.room.create` event (or `creator` in much older room versions).
|
|
|
|
|
|
|
|
|
|
* Room creators have infinitely high power level and cannot be specified in the
|
|
|
|
|
`m.room.power_levels` event, nor can they be changed after the room is created.
|
|
|
|
|
|
|
|
|
|
## Server implementation components
|
|
|
|
|
|
|
|
|
|
{{% boxes/warning %}}
|
|
|
|
|
The information contained in this section is strictly for server
|
|
|
|
|
implementors. Applications which use the Client-Server API are generally
|
|
|
|
|
unaffected by the intricacies contained here. The section above
|
|
|
|
|
regarding client considerations is the resource that Client-Server API
|
|
|
|
|
use cases should reference.
|
|
|
|
|
{{% /boxes/warning %}}
|
|
|
|
|
|
|
|
|
|
Room version 12 is based upon room version 11 with the following considerations.
|
|
|
|
|
|
|
|
|
|
### Event format
|
|
|
|
|
|
|
|
|
|
{{% rver-fragment name="v12-event-format" %}}
|
|
|
|
|
|
|
|
|
|
### Authorization rules
|
|
|
|
|
|
|
|
|
|
Events must be signed by the server denoted by the `sender` property.
|
|
|
|
|
|
|
|
|
|
The types of state events that affect authorization are:
|
|
|
|
|
|
|
|
|
|
- [`m.room.create`](/client-server-api#mroomcreate)
|
|
|
|
|
- [`m.room.member`](/client-server-api#mroommember)
|
|
|
|
|
- [`m.room.join_rules`](/client-server-api#mroomjoin_rules)
|
|
|
|
|
- [`m.room.power_levels`](/client-server-api#mroompower_levels)
|
|
|
|
|
- [`m.room.third_party_invite`](/client-server-api#mroomthird_party_invite)
|
|
|
|
|
|
|
|
|
|
{{% boxes/note %}}
|
|
|
|
|
Power levels are inferred from defaults when not explicitly supplied.
|
|
|
|
|
For example, mentions of the `sender`'s power level can also refer to
|
|
|
|
|
the default power level for users in the room.
|
|
|
|
|
{{% /boxes/note %}}
|
|
|
|
|
|
|
|
|
|
{{% boxes/note %}}
|
|
|
|
|
{{% added-in v=12 %}} The power level of "room creators" is infinitely high.
|
|
|
|
|
|
|
|
|
|
Room creators include:
|
|
|
|
|
* The user ID denoted by the `sender` of the `m.room.create` event in the room.
|
|
|
|
|
* Any user IDs contained in the `additional_creators` array in `content` of the
|
|
|
|
|
`m.room.create` event in the room, if `additional_creators` is present.
|
|
|
|
|
|
|
|
|
|
Room creators cannot be demoted to a lower power level, even through `m.room.power_levels`.
|
|
|
|
|
This is reflected in rule 10.4 below.
|
|
|
|
|
{{% /boxes/note %}}
|
|
|
|
|
|
|
|
|
|
{{% boxes/note %}}
|
|
|
|
|
`m.room.redaction` events are subject to auth rules in the same way as any other event.
|
|
|
|
|
In practice, that means they will normally be allowed by the auth rules, unless the
|
|
|
|
|
`m.room.power_levels` event sets a power level requirement for `m.room.redaction`
|
|
|
|
|
events via the `events` or `events_default` properties. In particular, the _redact
|
|
|
|
|
level_ is **not** considered by the auth rules.
|
|
|
|
|
|
|
|
|
|
The ability to send a redaction event does not mean that the redaction itself should
|
|
|
|
|
be performed. Receiving servers must perform additional checks, as described in
|
|
|
|
|
the [Handling redactions](#handling-redactions) section.
|
|
|
|
|
{{% /boxes/note %}}
|
|
|
|
|
|
|
|
|
|
{{% boxes/note %}}
|
|
|
|
|
The `m.room.create` event MUST NOT be selected for `auth_events` on events. The
|
|
|
|
|
`room_id` (being the `m.room.create` event's ID) implies this instead. This is
|
|
|
|
|
reflected in a change to rule 3.2 below.
|
|
|
|
|
{{% /boxes/note %}}
|
|
|
|
|
|
|
|
|
|
The rules are as follows:
|
|
|
|
|
|
|
|
|
|
1. If type is `m.room.create`:
|
|
|
|
|
1. If it has any `prev_events`, reject.
|
|
|
|
|
2. {{% changed-in v=12 %}} If the event has a `room_id`, reject.
|
|
|
|
|
|
|
|
|
|
**Note**: The room ID is the event ID of the event with sigil `!` instead
|
|
|
|
|
of `$`.
|
|
|
|
|
3. If `content.room_version` is present and is not a recognised
|
|
|
|
|
version, reject.
|
|
|
|
|
4. {{% added-in v=12 %}} If `additional_creators` is present in `content` and
|
|
|
|
|
is not an array of strings where each string passes the same [user ID](/appendices#user-identifiers)
|
|
|
|
|
validation applied to `sender`, reject.
|
|
|
|
|
5. Otherwise, allow.
|
|
|
|
|
2. {{% added-in v=12 %}} If the event's `room_id` is not an event ID for an accepted
|
|
|
|
|
(not rejected) `m.room.create` event, with the sigil `!` instead of `$`, reject.
|
|
|
|
|
3. Considering the event's `auth_events`:
|
|
|
|
|
1. If there are duplicate entries for a given `type` and `state_key` pair,
|
|
|
|
|
reject.
|
|
|
|
|
2. {{% changed-in v=12 %}} If there are entries whose `type` and `state_key`
|
|
|
|
|
don't match those specified by the [auth events selection](/server-server-api#auth-events-selection)
|
|
|
|
|
algorithm described in the server specification, reject.
|
|
|
|
|
|
|
|
|
|
**Note**: In this room version, `m.room.create` MUST NOT be selected.
|
|
|
|
|
3. If there are entries which were themselves rejected under the [checks
|
|
|
|
|
performed on receipt of a
|
|
|
|
|
PDU](/server-server-api/#checks-performed-on-receipt-of-a-pdu), reject.
|
|
|
|
|
5. If any event in `auth_events` has a `room_id` which does not match that of
|
|
|
|
|
the event being authorised, reject.
|
|
|
|
|
4. If the `content` of the `m.room.create` event in the room state has the
|
|
|
|
|
property `m.federate` set to `false`, and the `sender` domain of the event
|
|
|
|
|
does not match the `sender` domain of the create event, reject.
|
|
|
|
|
5. If type is `m.room.member`:
|
|
|
|
|
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
|
|
|
|
|
by the key, reject.
|
|
|
|
|
3. If `membership` is `join`:
|
|
|
|
|
1. {{% changed-in v=11 %}}
|
|
|
|
|
If the only previous event is an `m.room.create` and the
|
|
|
|
|
`state_key` is the sender of the `m.room.create`, 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
|
|
|
|
|
membership state is `invite` or `join`.
|
|
|
|
|
5. If the `join_rule` is `restricted` or `knock_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
|
|
|
|
|
users, reject.
|
|
|
|
|
3. Otherwise, allow.
|
|
|
|
|
6. If the `join_rule` is `public`, allow.
|
|
|
|
|
7. Otherwise, reject.
|
|
|
|
|
4. If `membership` is `invite`:
|
|
|
|
|
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`
|
|
|
|
|
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
|
|
|
|
|
current room state with `state_key` matching `token`,
|
|
|
|
|
reject.
|
|
|
|
|
6. If `sender` does not match `sender` of the
|
|
|
|
|
`m.room.third_party_invite`, reject.
|
|
|
|
|
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` 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.
|
|
|
|
|
3. If *target user*'s current membership state is `join` or
|
|
|
|
|
`ban`, reject.
|
|
|
|
|
4. If the `sender`'s power level is greater than or equal to
|
|
|
|
|
the *invite level*, allow.
|
|
|
|
|
5. Otherwise, reject.
|
|
|
|
|
5. If `membership` is `leave`:
|
|
|
|
|
1. 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`,
|
|
|
|
|
reject.
|
|
|
|
|
3. If the *target user*'s current membership state is `ban`,
|
|
|
|
|
and the `sender`'s power level is less than the *ban level*,
|
|
|
|
|
reject.
|
|
|
|
|
4. If the `sender`'s power level is greater than or equal to
|
|
|
|
|
the *kick level*, and the *target user*'s power level is
|
|
|
|
|
less than the `sender`'s power level, allow.
|
|
|
|
|
5. Otherwise, reject.
|
|
|
|
|
6. If `membership` is `ban`:
|
|
|
|
|
1. If the `sender`'s current membership state is not `join`,
|
|
|
|
|
reject.
|
|
|
|
|
2. If the `sender`'s power level is greater than or equal to
|
|
|
|
|
the *ban level*, and the *target user*'s power level is less
|
|
|
|
|
than the `sender`'s power level, allow.
|
|
|
|
|
3. Otherwise, reject.
|
|
|
|
|
7. If `membership` is `knock`:
|
|
|
|
|
1. If the `join_rule` is anything other than `knock` or
|
|
|
|
|
`knock_restricted`, reject.
|
|
|
|
|
2. If `sender` does not match `state_key`, reject.
|
|
|
|
|
3. If the `sender`'s current membership is not `ban`, `invite`,
|
|
|
|
|
or `join`, allow.
|
|
|
|
|
4. Otherwise, reject.
|
|
|
|
|
8. Otherwise, the membership is unknown. Reject.
|
|
|
|
|
6. If the `sender`'s current membership state is not `join`, reject.
|
|
|
|
|
7. If type is `m.room.third_party_invite`:
|
|
|
|
|
1. Allow if and only if `sender`'s current power level is greater
|
|
|
|
|
than or equal to the *invite level*.
|
|
|
|
|
8. If the event type's *required power level* is greater than the
|
|
|
|
|
`sender`'s power level, reject.
|
|
|
|
|
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 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. If either of the properties `events` or `notifications` in `content`
|
|
|
|
|
are present and not an object with values that are integers,
|
|
|
|
|
reject.
|
|
|
|
|
3. If the `users` property in `content` is not an object with keys that
|
|
|
|
|
are valid user IDs with values that are integers, reject.
|
|
|
|
|
4. {{% added-in v=12 %}} If the `users` property in `content` contains the
|
|
|
|
|
`sender` of the `m.room.create` event or any of the `additional_creators`
|
|
|
|
|
array (if present) from the `content` of the `m.room.create` event, reject.
|
|
|
|
|
5. If there is no previous `m.room.power_levels` event in the room,
|
|
|
|
|
allow.
|
|
|
|
|
6. 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.
|
|
|
|
|
7. 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.
|
|
|
|
|
8. 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.
|
|
|
|
|
9. 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.
|
|
|
|
|
10. 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.
|
|
|
|
|
11. Otherwise, allow.
|
|
|
|
|
|
|
|
|
|
{{% boxes/note %}}
|
|
|
|
|
Some consequences of these rules:
|
|
|
|
|
|
|
|
|
|
- Unless you are a member of the room, the only permitted operations
|
|
|
|
|
(apart from the initial create/join) are: joining a public room;
|
|
|
|
|
accepting or rejecting an invitation to a room.
|
|
|
|
|
- To unban somebody, you must have power level greater than or equal
|
|
|
|
|
to both the kick *and* ban levels, *and* greater than the target
|
|
|
|
|
user's power level.
|
|
|
|
|
{{% /boxes/note %}}
|
|
|
|
|
|
|
|
|
|
### State resolution
|
|
|
|
|
|
|
|
|
|
{{% boxes/note %}}
|
|
|
|
|
{{% added-in v=12 %}} This state resolution algorithm is largely the same as the
|
|
|
|
|
algorithm found in [room version 2](/rooms/v2) with the following modifications:
|
|
|
|
|
|
|
|
|
|
1. The *iterative auth checks algorithm* in the [Algorithm](#algorithm) subsection
|
|
|
|
|
now starts with an *empty* state map instead of the unconflicted state map.
|
|
|
|
|
|
|
|
|
|
2. A new [definition](#definitions) for *conflicted state subgraph* has been added
|
|
|
|
|
which describes events that are required to authorize events during iterative
|
|
|
|
|
auth checks.
|
|
|
|
|
|
|
|
|
|
3. To ensure the new conflicted state subgraph is actually referenced, the definition
|
|
|
|
|
for *full conflicted set* additionally includes the subgraph.
|
|
|
|
|
{{% /boxes/note %}}
|
|
|
|
|
|
|
|
|
|
The room state *S′(E)* after an event *E* is defined in terms of the
|
|
|
|
|
room state *S(E)* before *E*, and depends on whether *E* is a state
|
|
|
|
|
event or a message event:
|
|
|
|
|
|
|
|
|
|
- If *E* is a message event, then *S′(E)* = *S(E)*.
|
|
|
|
|
- If *E* is a state event, then *S′(E)* is *S(E)*, except that its
|
|
|
|
|
entry corresponding to the `event_type` and `state_key` of *E* is
|
|
|
|
|
replaced by the `event_id` of *E*.
|
|
|
|
|
|
|
|
|
|
The room state *S(E)* before *E* is the *resolution* of the set of
|
|
|
|
|
states {*S′(E*<sub>1</sub>*)*, *S′(E*<sub>2</sub>*)*, …}
|
|
|
|
|
after the `prev_event`s {*E*<sub>1</sub>, *E*<sub>2</sub>, …} of *E*.
|
|
|
|
|
The resolution of a set of states is given in the algorithm below.
|
|
|
|
|
|
|
|
|
|
#### Definitions
|
|
|
|
|
|
|
|
|
|
The state resolution algorithm for version 2 rooms uses the following
|
|
|
|
|
definitions, given the set of room states
|
|
|
|
|
{*S*<sub>1</sub>, *S*<sub>2</sub>, …}:
|
|
|
|
|
|
|
|
|
|
**Power events.**
|
|
|
|
|
A *power event* is a state event with type `m.room.power_levels` or
|
|
|
|
|
`m.room.join_rules`, or a state event with type `m.room.member` where
|
|
|
|
|
the `membership` is `leave` or `ban` and the `sender` does not match the
|
|
|
|
|
`state_key`. The idea behind this is that power events are events that
|
|
|
|
|
might remove someone's ability to do something in the room.
|
|
|
|
|
|
|
|
|
|
**Unconflicted state map and conflicted state set.**
|
|
|
|
|
The keys of the state maps *S<sub>i</sub>* are 2-tuples of strings of the form
|
|
|
|
|
*K* = `(event_type, state_key)`. The values *V* are state events.
|
|
|
|
|
The key-value pairs (*K*, *V*) across all state maps *S<sub>i</sub>* can be
|
|
|
|
|
divided into two collections.
|
|
|
|
|
If a given key *K* is present in every *S<sub>i</sub>* with the same value *V*
|
|
|
|
|
in each state map, then the pair (*K*, *V*) belongs to the *unconflicted state map*.
|
|
|
|
|
Otherwise, *V* belongs to the *conflicted state set*.
|
|
|
|
|
|
|
|
|
|
Note that the unconflicted state map only has one event for each key *K*,
|
|
|
|
|
whereas the conflicted state set may contain multiple events with the same key.
|
|
|
|
|
|
|
|
|
|
**Auth chain.**
|
|
|
|
|
The *auth chain* of an event *E* is the set containing all of *E*'s auth events,
|
|
|
|
|
all of *their* auth events, and so on recursively, stretching back to the
|
|
|
|
|
start of the room. Put differently, these are the events reachable by walking
|
|
|
|
|
the graph induced by an event's `auth_events` links.
|
|
|
|
|
|
|
|
|
|
**Auth difference.**
|
|
|
|
|
The *auth difference* is calculated by first calculating the full auth
|
|
|
|
|
chain for each state *S*<sub>*i*</sub>, that is the union of the auth
|
|
|
|
|
chains for each event in *S*<sub>*i*</sub>, and then taking every event
|
|
|
|
|
that doesn't appear in every auth chain. If *C*<sub>*i*</sub> is the
|
|
|
|
|
full auth chain of *S*<sub>*i*</sub>, then the auth difference is
|
|
|
|
|
∪ *C*<sub>*i*</sub> − ∩ *C*<sub>*i*</sub>.
|
|
|
|
|
|
|
|
|
|
{{% added-in v=12 %}} **Conflicted state subgraph.**
|
|
|
|
|
Starting from an event in the *conflicted state set* and following `auth_events`
|
|
|
|
|
edges may lead to another event in the conflicted state set. The union of all
|
|
|
|
|
such paths between any pair of events in the conflicted state set (including
|
|
|
|
|
endpoints) forms a subgraph of the original `auth_event` graph, called the
|
|
|
|
|
*conflicted state subgraph*.
|
|
|
|
|
|
|
|
|
|
{{% changed-in v=12 %}} **Full conflicted set.**
|
|
|
|
|
The *full conflicted set* is the union of the conflicted state set, the conflicted
|
|
|
|
|
state subgraph, and the auth difference.
|
|
|
|
|
|
|
|
|
|
**Reverse topological power ordering.**
|
|
|
|
|
The *reverse topological power ordering* of a set of events is the
|
|
|
|
|
lexicographically smallest topological ordering based on the DAG formed
|
|
|
|
|
by auth events. The reverse topological power ordering is ordered from
|
|
|
|
|
earliest event to latest. For comparing two topological orderings to
|
|
|
|
|
determine which is the lexicographically smallest, the following
|
|
|
|
|
comparison relation on events is used: for events *x* and *y*,
|
|
|
|
|
*x* < *y* if
|
|
|
|
|
|
|
|
|
|
1. *x*'s sender has *greater* power level than *y*'s sender, when
|
|
|
|
|
looking at their respective `auth_event`s; or
|
|
|
|
|
2. the senders have the same power level, but *x*'s `origin_server_ts`
|
|
|
|
|
is *less* than *y*'s `origin_server_ts`; or
|
|
|
|
|
3. the senders have the same power level and the events have the same
|
|
|
|
|
`origin_server_ts`, but *x*'s `event_id` is *less* than *y*'s
|
|
|
|
|
`event_id`.
|
|
|
|
|
|
|
|
|
|
The reverse topological power ordering can be found by sorting the
|
|
|
|
|
events using Kahn's algorithm for topological sorting, and at each step
|
|
|
|
|
selecting, among all the candidate vertices, the smallest vertex using
|
|
|
|
|
the above comparison relation.
|
|
|
|
|
|
|
|
|
|
**Mainline ordering.**
|
|
|
|
|
Let *P* = *P*<sub>0</sub> be an `m.room.power_levels` event.
|
|
|
|
|
Starting with *i* = 0, repeatedly fetch *P*<sub>*i*+1</sub>, the
|
|
|
|
|
`m.room.power_levels` event in the `auth_events` of *P<sub>i</sub>*.
|
|
|
|
|
Increment *i* and repeat until *P<sub>i</sub>* has no `m.room.power_levels`
|
|
|
|
|
event in its `auth_events`.
|
|
|
|
|
The *mainline of P*<sub>0</sub> is the list of events
|
|
|
|
|
[*P*<sub>0</sub> , *P*<sub>1</sub>, ... , *P<sub>n</sub>*],
|
|
|
|
|
fetched in this way.
|
|
|
|
|
|
|
|
|
|
Let *e* = *e<sub>0</sub>* be another event (possibly another
|
|
|
|
|
`m.room.power_levels` event). We can compute a similar list of events
|
|
|
|
|
[*e*<sub>1</sub>, ..., *e<sub>m</sub>*],
|
|
|
|
|
where *e*<sub>*j*+1</sub> is the `m.room.power_levels` event in the
|
|
|
|
|
`auth_events` of *e<sub>j</sub>* and where *e<sub>m</sub>* has no
|
|
|
|
|
`m.room.power_levels` event in its `auth_events`. (Note that the event we
|
|
|
|
|
started with, *e<sub>0</sub>*, is not included in this list. Also note that it
|
|
|
|
|
may be empty, because *e* may not cite an `m.room.power_levels` event in its
|
|
|
|
|
`auth_events` at all.)
|
|
|
|
|
|
|
|
|
|
Now compare these two lists as follows.
|
|
|
|
|
* Find the smallest index *j* ≥ 1 for which *e<sub>j</sub>* belongs to the
|
|
|
|
|
mainline of *P*.
|
|
|
|
|
* If such a *j* exists, then *e<sub>j</sub>* = *P<sub>i</sub>* for some unique
|
|
|
|
|
index *i* ≥ 0. Otherwise set *i* = ∞, where ∞ is a sentinel value greater
|
|
|
|
|
than any integer.
|
|
|
|
|
* In both cases, the *mainline position* of *e* is *i*.
|
|
|
|
|
|
|
|
|
|
Given mainline positions calculated from *P*, the *mainline ordering based on* *P* of a set of events is the ordering,
|
|
|
|
|
from smallest to largest, using the following comparison relation on
|
|
|
|
|
events: for events *x* and *y*, *x* < *y* if
|
|
|
|
|
|
|
|
|
|
1. the mainline position of *x* is **greater** than
|
|
|
|
|
the mainline position of *y* (i.e. the auth chain of
|
|
|
|
|
*x* is based on an earlier event in the mainline than *y*); or
|
|
|
|
|
2. the mainline positions of the events are the same, but *x*'s
|
|
|
|
|
`origin_server_ts` is *less* than *y*'s `origin_server_ts`; or
|
|
|
|
|
3. the mainline positions of the events are the same and the events have the
|
|
|
|
|
same `origin_server_ts`, but *x*'s `event_id` is *less* than *y*'s
|
|
|
|
|
`event_id`.
|
|
|
|
|
|
|
|
|
|
**Iterative auth checks.**
|
|
|
|
|
The *iterative auth checks algorithm* takes as input an initial room
|
|
|
|
|
state and a sorted list of state events, and constructs a new room state
|
|
|
|
|
by iterating through the event list and applying the state event to the
|
|
|
|
|
room state if the state event is allowed by the [authorization
|
|
|
|
|
rules](/server-server-api#authorization-rules).
|
|
|
|
|
If the state event is not allowed by the authorization rules, then the
|
|
|
|
|
event is ignored. If a `(event_type, state_key)` key that is required
|
|
|
|
|
for checking the authorization rules is not present in the state, then
|
|
|
|
|
the appropriate state event from the event's `auth_events` is used if
|
|
|
|
|
the auth event is not rejected.
|
|
|
|
|
|
|
|
|
|
#### Algorithm
|
|
|
|
|
|
|
|
|
|
The *resolution* of a set of states is obtained as follows:
|
|
|
|
|
|
|
|
|
|
1. Select the set *X* of all *power events* that appear in the *full
|
|
|
|
|
conflicted set*. For each such power event *P*, enlarge *X* by adding
|
|
|
|
|
the events in the auth chain of *P* which also belong to the full
|
|
|
|
|
conflicted set. Sort *X* into a list using the *reverse topological
|
|
|
|
|
power ordering*.
|
|
|
|
|
2. {{% changed-in v=12 %}} Apply the *iterative auth checks algorithm*,
|
|
|
|
|
starting from an *empty* state map, to the list of events from the previous
|
|
|
|
|
step to get a partially resolved state.
|
|
|
|
|
3. Take all remaining events that weren't picked in step 1 and order
|
|
|
|
|
them by the mainline ordering based on the power level in the
|
|
|
|
|
partially resolved state obtained in step 2.
|
|
|
|
|
4. Apply the *iterative auth checks algorithm* on the partial resolved
|
|
|
|
|
state and the list of events from the previous step.
|
|
|
|
|
5. Update the result by replacing any event with the event with the
|
|
|
|
|
same key from the *unconflicted state map*, if such an event exists,
|
|
|
|
|
to get the final resolved state.
|
|
|
|
|
|
|
|
|
|
#### Rejected events
|
|
|
|
|
|
|
|
|
|
Events that have been rejected due to failing auth based on the state at
|
|
|
|
|
the event (rather than based on their auth chain) are handled as usual
|
|
|
|
|
by the algorithm, unless otherwise specified.
|
|
|
|
|
|
|
|
|
|
Note that no events rejected due to failure to auth against their auth
|
|
|
|
|
chain should appear in the process, as they should not appear in state
|
|
|
|
|
(the algorithm only uses events that appear in either the state sets or
|
|
|
|
|
in the auth chain of the events in the state sets).
|
|
|
|
|
|
|
|
|
|
{{% boxes/rationale %}}
|
|
|
|
|
This helps ensure that different servers' view of state is more likely
|
|
|
|
|
to converge, since rejection state of an event may be different. This
|
|
|
|
|
can happen if a third server gives an incorrect version of the state
|
|
|
|
|
when a server joins a room via it (either due to being faulty or
|
|
|
|
|
malicious). Convergence of state is a desirable property as it ensures
|
|
|
|
|
that all users in the room have a (mostly) consistent view of the state
|
|
|
|
|
of the room. If the view of the state on different servers diverges it
|
|
|
|
|
can lead to bifurcation of the room due to e.g. servers disagreeing on
|
|
|
|
|
who is in the room.
|
|
|
|
|
|
|
|
|
|
Intuitively, using rejected events feels dangerous, however:
|
|
|
|
|
|
|
|
|
|
1. Servers cannot arbitrarily make up state, since they still need to
|
|
|
|
|
pass the auth checks based on the event's auth chain (e.g. they
|
|
|
|
|
can't grant themselves power levels if they didn't have them
|
|
|
|
|
before).
|
|
|
|
|
2. For a previously rejected event to pass auth there must be a set of
|
|
|
|
|
state that allows said event. A malicious server could therefore
|
|
|
|
|
produce a fork where it claims the state is that particular set of
|
|
|
|
|
state, duplicate the rejected event to point to that fork, and send
|
|
|
|
|
the event. The duplicated event would then pass the auth checks.
|
|
|
|
|
Ignoring rejected events would therefore not eliminate any potential
|
|
|
|
|
attack vectors.
|
|
|
|
|
{{% /boxes/rationale %}}
|
|
|
|
|
|
|
|
|
|
Rejected auth events are deliberately excluded from use in the iterative
|
|
|
|
|
auth checks, as auth events aren't re-authed (although non-auth events
|
|
|
|
|
are) during the iterative auth checks.
|
|
|
|
|
|
|
|
|
|
## Unchanged from v11
|
|
|
|
|
|
|
|
|
|
The following sections have not been modified since v11, but are included for
|
|
|
|
|
completeness.
|
|
|
|
|
|
|
|
|
|
### Redactions
|
|
|
|
|
|
|
|
|
|
{{% rver-fragment name="v11-redactions" %}}
|
|
|
|
|
|
|
|
|
|
### Handling redactions
|
|
|
|
|
|
|
|
|
|
{{% rver-fragment name="v3-handling-redactions" %}}
|
|
|
|
|
|
|
|
|
|
### Event IDs
|
|
|
|
|
|
|
|
|
|
{{% rver-fragment name="v4-event-ids" %}}
|
|
|
|
|
|
|
|
|
|
### Canonical JSON
|
|
|
|
|
|
|
|
|
|
{{% rver-fragment name="v6-canonical-json" %}}
|
|
|
|
|
|
|
|
|
|
### Signing key validity period
|
|
|
|
|
|
|
|
|
|
{{% rver-fragment name="v5-signing-requirements" %}}
|