You're looking at an old version of this specification.

Switch to the current stable release.

Room Version 4

This room version builds on version 3 using a different encoding for event IDs.

Client considerations

This room version changes the format form event IDs sent to clients. Clients should already be treating event IDs as opaque identifiers, and should not be concerned with the format of them. Clients should still encode the event ID when including it in a request path.

Clients should expect to see event IDs changed from the format of $randomstring:example.org to something like $Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg (note the lack of domain).

Though unchanged in this room version, clients which implement the redaction algorithm locally should refer to the redactions section below for a full overview.

Server implementation components

Room version 4 uses the same algorithms defined in room version 3, however using URL-safe base64 to generate the event ID.

Event IDs

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

Unchanged from v3

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

Redactions

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 key join_rule.
  • m.room.power_levels allows keys ban, events, events_default, kick, redact, state_default, users, users_default.
  • m.room.aliases allows key aliases.
  • m.room.history_visibility allows key history_visibility.

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 format

The event format is the same as room version 3, however the event IDs in the following example are updated to reflect the changes in this room version.

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 string Required: The server_name of the homeserver that created this event.
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: Server Signatures} 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": "example.com",
  "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"
    }
  },
  "state_key": "my_key",
  "type": "m.room.message",
  "unsigned": {
    "age": 4612,
    "key": "value"
  }
}

Authorization rules

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, however.

The types of state events that affect authorization are:

  • m.room.create
  • m.room.member
  • m.room.join_rules
  • m.room.power_levels
  • m.room.third_party_invite

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.
    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.
    5. Otherwise, allow.
  2. Reject if event has auth_events that:
    1. have duplicate entries for a given type and state_key pair
    2. have entries whose type and state_key don’t match those specified by the auth events selection algorithm described in the server specification.
  3. If event does not have a m.room.create in its auth_events, reject.
  4. If type is m.room.aliases:
    1. If event has no state_key, reject.
    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.
    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 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 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, 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 field.
          2. A list of public keys in the public_keys field.
        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.
    4. If membership is leave:
      1. If the sender matches state_key, allow if and only if that user’s current membership state is invite or join.
      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.
    5. 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.
    6. 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 users key in content is not a dictionary 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, 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 and users keys:
      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.
    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.
  11. Otherwise, allow.

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 E’s event_type and state_key is replaced by E’s event_id.

The room state S(E) before E is the resolution of the set of states {S′(E1), S′(E2), …} consisting of the states after each of E’s prev_events {E1, E2, …}, where 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 unconflicted state map is the state where the value of each key exists and is the same in each state Si. The conflicted state set is the set of all other state events. Note that the unconflicted state map only has one event per (event_type, state_key), whereas the conflicted state set may have multiple events.

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 Given an m.room.power_levels event P, the mainline of P is the list of events generated by starting with P and recursively taking the m.room.power_levels events from the auth_events, ordered such that P is last. Given another event e, the closest mainline event to e is the first event encountered in the mainline when iteratively descending through the m.room.power_levels events in the auth_events starting at e. If no mainline event is encountered when iteratively descending through the m.room.power_levels events, then the closest mainline event to e can be considered to be a dummy event that is before any other event in the mainline of P for the purposes of condition 1 below.

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 closest mainline event to x appears before the closest mainline event to y; or
  2. the closest mainline events are the same, but x’s origin_server_ts is less than y’s origin_server_ts; or
  3. the closest mainline 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. Take all power events and any events in their auth chains, recursively, that appear in the full conflicted set and order them by 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).

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 NOT strictly enforce the JSON format specified in the appendices for the reasons described there.