Room Version 8

This room version builds on version 7 to introduce a new join rule that allows members to join the room based on membership in another room.

This room version is known to have issues relating to redactions of member join events. Room version 9 should be preferred over v8 when creating rooms.

Client considerations

Clients are encouraged to expose the option for the join rule in their user interface for supported room versions.

The new join rule, restricted, is described in the Client-Server API.

Clients which implement the redaction algorithm locally should refer to the redactions section below for a full overview.

Redactions

[New in this version] m.room.join_rules events now keep allow in addition to other keys in content when being redacted.

Room version 9 adds additional cases of protected properties for behaviour related to restricted rooms (the functionality introduced in v8). v9 is preferred over v8 when creating new rooms.

The full redaction algorithm follows.

Upon receipt of a redaction event, the server must strip off any keys not in the following list:

  • event_id
  • type
  • room_id
  • sender
  • state_key
  • content
  • hashes
  • signatures
  • depth
  • prev_events
  • prev_state
  • auth_events
  • origin
  • origin_server_ts
  • membership

The content object must also be stripped of all keys, unless it is one of one of the following event types:

  • m.room.member allows key membership.
  • m.room.create allows key creator.
  • m.room.join_rules allows keys join_rule, allow.
  • m.room.power_levels allows keys ban, events, events_default, kick, redact, state_default, users, users_default.
  • m.room.history_visibility allows key history_visibility.

Server implementation components

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.

Room version 8 adds a new join rule to allow members of a room to join another room without invite. Otherwise, the room version inherits all properties of Room version 7.

Authorization rules

[New in this version] 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).

Events must be signed by the server denoted by the sender property.

The types of state events that affect authorization are:

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.

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 section.

The rules are as follows:

  1. If type is m.room.create:
    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 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, reject.
    2. If there are entries whose type and state_key don’t match those specified by the auth events selection algorithm described in the server specification, reject.
    3. If there are entries which were themselves rejected under the checks performed on receipt of a PDU, reject.
    4. If there is no m.room.create event among the entries, reject.
  3. 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.
  4. If type is m.room.member:
    1. If there is no state_key property, or no membership property in content, reject.
    2. [New in this version] 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:
      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 membership state is invite or join.
      5. [New in this version] 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 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, 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.
  5. If the sender’s current membership state is not join, reject.
  6. 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.
  7. If the event type’s required power level is greater than the sender’s power level, reject.
  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 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 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 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.
    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.
    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.

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.

Redactions

See above.

Unchanged from v7

The following sections have not been modified since v7, but are included for completeness.

Handling redactions

In room versions 1 and 2, redactions were explicitly part of the authorization rules under Rule 11. As of room version 3, these conditions no longer exist as represented by this version’s authorization rules.

While redactions are always accepted by the authorization rules for events, they should not be sent to clients until both the redaction event and the event the redaction affects have been received, and can be validated. If both events are valid and have been seen by the server, then the server applies the redaction if one of the following conditions is met:

  1. The power level of the redaction event’s sender is greater than or equal to the redact level.
  2. The domain of the redaction event’s sender matches that of the original event’s sender.

If the server would apply a redaction, the redaction event is also sent to clients. Otherwise, the server simply waits for a valid partner event to arrive where it can then re-check the above.

Event IDs

The event ID is the reference hash of the event encoded using a variation of Unpadded Base64 which replaces the 62nd and 63rd characters with - and _ instead of using + and /. This matches RFC4648’s definition of URL-safe base64.

Event IDs are still prefixed with $ and might result in looking like $Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg.

Event format

Events in rooms of this version have the following structure:

Persistent Data Unit


A persistent data unit (event) for room version 4 and beyond.

Persistent Data Unit
Name Type Description
auth_events [string]

Required: Event IDs for the authorization events that would allow this event to be in the room.

Must contain less than or equal to 10 events. Note that if the relevant auth event selection rules are used, this restriction should never be encountered.

content object

Required: The content of the event.

depth integer

Required: The maximum depth of the prev_events, plus one. Must be less than the maximum value for an integer (2^63 - 1). If the room’s depth is already at the limit, the depth must be set to the limit.

hashes Event Hash

Required: Content hashes of the PDU, following the algorithm specified in Signing Events.

origin_server_ts integer

Required: Timestamp in milliseconds on origin homeserver when this event was created.

prev_events [string]

Required: Event IDs for the most recent events in the room that the homeserver was aware of when it made this event.

Must contain less than or equal to 20 events.

redacts string

For redaction events, the ID of the event being redacted.

room_id string

Required: Room identifier.

sender string

Required: The ID of the user sending the event.

signatures {string: {string: string}}

Required: Signatures for the PDU, following the algorithm specified in Signing Events.

state_key string

If this key is present, the event is a state event, and it will replace previous events with the same type and state_key in the room state.

type string

Required: Event type

unsigned UnsignedData

Additional data added by the origin server but not covered by the signatures.

Event Hash
Name Type Description
sha256 string

Required: The hash.

UnsignedData
Name Type Description
age integer

The number of milliseconds that have passed since this message was sent.

Examples

{
  "auth_events": [
    "$urlsafe_base64_encoded_eventid",
    "$a-different-event-id"
  ],
  "content": {
    "key": "value"
  },
  "depth": 12,
  "hashes": {
    "sha256": "thishashcoversallfieldsincasethisisredacted"
  },
  "origin_server_ts": 1404838188000,
  "prev_events": [
    "$urlsafe_base64_encoded_eventid",
    "$a-different-event-id"
  ],
  "redacts": "$some-old_event",
  "room_id": "!UcYsUzyxTGDxLBEvLy:example.org",
  "sender": "@alice:example.com",
  "signatures": {
    "example.com": {
      "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus"
    }
  },
  "type": "m.room.message",
  "unsigned": {
    "age": 4612
  }
}

Deprecated event content schemas

Events sent into rooms of this version can have formats which are different from their normal schema. Those cases are documented here.

The behaviour described here is preserved strictly for backwards compatibility only. A homeserver should take reasonable precautions to prevent users from sending these so-called “malformed” events, and must never rely on the behaviours described here as a default.

m.room.power_levels events accept values as strings

In order to maintain backwards compatibility with early implementations, each of the integer-valued properties within m.room.power_levels events can be encoded as strings instead of integers. This includes the nested values within the events, notifications and users properties. For example, the following is a valid m.room.power_levels event in this room version:

{
  "content": {
    "ban": "50",
    "events": {
      "m.room.power_levels": "100"
    },
    "events_default": "0",
    "state_default": "50",
    "users": {
      "@example:localhost": "100"
    },
    "users_default": "0"
  },
  "origin_server_ts": 1432735824653,
  "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
  "sender": "@example:example.org",
  "state_key": "",
  "type": "m.room.power_levels"
}

When the value is representative of an integer, they must be the following format:

  • a single base 10 integer, no float values or decimal points, optionally with any number of leading zeroes ("100", "000100");
  • optionally prefixed with a single - or + character before the integer ("+100", "-100").
  • optionally with any number of leading or trailing whitespace characters (" 100 ", " 00100 ", " +100 ", " -100 ");

State resolution

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′(E1), S′(E2), …} after the prev_events {E1, E2, …} 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 {S1, S2, …}:

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 Si, that is the union of the auth chains for each event in Si, and then taking every event that doesn’t appear in every auth chain. If Ci is the full auth chain of Si, then the auth difference is ∪ Ci − ∩ Ci.

Full conflicted set. The full conflicted set is the union of the conflicted state set 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_events; 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 = P0 be an m.room.power_levels event. Starting with i = 0, repeatedly fetch Pi+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 P0 is the list of events [P0 , P1, … , 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 [e1, …, em], where ej+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. 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. Apply the iterative auth checks algorithm, starting from the unconflicted 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).

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.

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.

Canonical JSON

Servers MUST strictly enforce the JSON format specified in the appendices. This translates to a 400 M_BAD_JSON error on most endpoints, or discarding of events over federation. For example, the Federation API’s /send endpoint would discard the event whereas the Client Server API’s /send/{eventType} endpoint would return a M_BAD_JSON error.

Signing key validity period

When validating event signatures, servers MUST enforce the valid_until_ts property from a key request is at least as large as the origin_server_ts for the event being validated. Servers missing a copy of the signing key MUST try to obtain one via the GET /_matrix/key/v2/server or POST /_matrix/key/v2/query APIs. When using the /query endpoint, servers MUST set the minimum_valid_until_ts property to prompt the notary server to attempt to refresh the key if appropriate.

Servers MUST use the lesser of valid_until_ts and 7 days into the future when determining if a key is valid. This is to avoid a situation where an attacker publishes a key which is valid for a significant amount of time without a way for the homeserver owner to revoke it.