--- 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. 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*1*)*, *S′(E*2*)*, …} after the `prev_event`s {*E*1, *E*2, …} 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*1, *S*2, …}: **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 *Si* 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 *Si* can be divided into two collections. If a given key *K* is present in every *Si* 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**i*, that is the union of the auth chains for each event in *S**i*, and then taking every event that doesn't appear in every auth chain. If *C**i* is the full auth chain of *S**i*, then the auth difference is ∪ *C**i* − ∩ *C**i*. {{% 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*0 be an `m.room.power_levels` event. Starting with *i* = 0, repeatedly fetch *P**i*+1, the `m.room.power_levels` event in the `auth_events` of *Pi*. Increment *i* and repeat until *Pi* has no `m.room.power_levels` event in its `auth_events`. The *mainline of P*0 is the list of events [*P*0 , *P*1, ... , *Pn*], fetched in this way. Let *e* = *e0* be another event (possibly another `m.room.power_levels` event). We can compute a similar list of events [*e*1, ..., *em*], where *e**j*+1 is the `m.room.power_levels` event in the `auth_events` of *ej* and where *em* has no `m.room.power_levels` event in its `auth_events`. (Note that the event we started with, *e0*, 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 *ej* belongs to the mainline of *P*. * If such a *j* exists, then *ej* = *Pi* 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" %}}