|
| 1 | +--- |
| 2 | +type: module |
| 3 | +weight: 340 |
| 4 | +--- |
| 5 | + |
| 6 | +### Spaces |
| 7 | + |
| 8 | +{{% added-in v="1.2" %}} |
| 9 | + |
| 10 | +Often used to group rooms of similar subject matter (such as a public "Official |
| 11 | +matrix.org rooms" space or personal "Work stuff" space), spaces are a way to |
| 12 | +organise rooms while being represented as rooms themselves. |
| 13 | + |
| 14 | +A space is defined by the [`m.space` room type](#types), making it known as a |
| 15 | +"space-room". The space's name, topic, avatar, aliases, etc are all defined through |
| 16 | +the existing relevant state events within the space-room. |
| 17 | + |
| 18 | +Sending normal [`m.room.message`](#mroommessage) events within the space-room is |
| 19 | +discouraged - clients are not generally expected to have a way to render the timeline |
| 20 | +of the room. As such, space-rooms should be created with [`m.room.power_levels`](#mroompower_levels) |
| 21 | +which prohibit normal events by setting `events_default` to a suitably high number. |
| 22 | +In the default power level structure, this would be `100`. Clients might wish to |
| 23 | +go a step further and explicitly ignore notification counts on space-rooms. |
| 24 | + |
| 25 | +Membership of a space is defined and controlled by the existing mechanisms which |
| 26 | +govern a room: [`m.room.member`](#mroommember), [`m.room.history_visibility`](#mroomhistory_visibility), |
| 27 | +and [`m.room.join_rules`](#mroomjoin_rules). Public spaces are encouraged to have |
| 28 | +a similar setup to public rooms: `world_readable` history visibility, published |
| 29 | +canonical alias, and suitibly public `join_rule`. Invites, including third-party |
| 30 | +invites, still work just as they do in normal rooms as well. |
| 31 | + |
| 32 | +All other aspects of regular rooms are additionally carried over, such as the |
| 33 | +ability to set arbitrary state events, hold room account data, etc. Spaces are |
| 34 | +just rooms with extra functionality on top. |
| 35 | + |
| 36 | +#### Managing rooms/spaces included in a space |
| 37 | + |
| 38 | +Spaces form a hierarchy of rooms which clients can use to structure their room |
| 39 | +list into a tree-like view. The parent/child relationship can be defined in two |
| 40 | +ways: with [`m.space.child`](#mspacechild) state events in the space-room, or with |
| 41 | +[`m.space.parent`](#mspaceparent) state events in the child room. |
| 42 | + |
| 43 | +In most cases, both the child and parent relationship should be defined to aid |
| 44 | +discovery of the space and its rooms. When only a `m.space.child` is used, the space |
| 45 | +is effectively a curated list of rooms which the rooms themselves might not be aware |
| 46 | +of. When only a `m.space.parent` is used, the rooms are "secretly" added to spaces |
| 47 | +with the effect of not being advertised directly by the space. |
| 48 | + |
| 49 | +{{% boxes/warning %}} |
| 50 | +Considering spaces are rooms themselves, it is possible to nest spaces within spaces, |
| 51 | +infinitely. Though loops are explicitly disallowed, they are still possible. Loops |
| 52 | +must be broken rather than infinitely looping. |
| 53 | + |
| 54 | +Clients and servers should additionally be aware of excessively long trees which may |
| 55 | +cause performance issues. |
| 56 | +{{% /boxes/warning %}} |
| 57 | + |
| 58 | +##### `m.space.child` relationship |
| 59 | + |
| 60 | +When using this approach, the state events get sent into the space-room which is the |
| 61 | +parent to the room. The `state_key` for the event is the child room's ID. |
| 62 | + |
| 63 | +For example, to achieve the following: |
| 64 | + |
| 65 | +``` |
| 66 | +#space:example.org |
| 67 | + #general:example.org (!abcdefg:example.org) |
| 68 | + !private:example.org |
| 69 | +``` |
| 70 | + |
| 71 | +the state of `#space:example.org` would consist of: |
| 72 | + |
| 73 | +*Unimportant fields trimmed for brevity.* |
| 74 | + |
| 75 | +```json |
| 76 | +{ |
| 77 | + "type": "m.space.child", |
| 78 | + "state_key": "!abcdefg:example.org", |
| 79 | + "content": { |
| 80 | + "via": ["example.org"] |
| 81 | + } |
| 82 | +} |
| 83 | +``` |
| 84 | +```json |
| 85 | +{ |
| 86 | + "type": "m.space.child", |
| 87 | + "state_key": "!private:example.org", |
| 88 | + "content": { |
| 89 | + "via": ["example.org"] |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +No state events in the child rooms themselves would be required. This allows for users |
| 95 | +to define personal/private spaces to organise their own rooms without needing explicit |
| 96 | +permission from the room moderators/admins. |
| 97 | + |
| 98 | +Child rooms can be removed from a space by omitting the `via` key of `content` on the |
| 99 | +relevant state event, such as through redaction or otherwise clearing the `content`. |
| 100 | +This is a temporary measure until it is possible to delete state events in Matrix. |
| 101 | + |
| 102 | +{{% event event="m.space.child" %}} |
| 103 | + |
| 104 | +###### Ordering |
| 105 | + |
| 106 | +When the client is displaying the children of a space, the children should be ordered |
| 107 | +using the algorithm below. In some cases, like a traditional left side room list, the |
| 108 | +client may override the ordering to provide better user experience. A theoretical |
| 109 | +space summary view would however show the children ordered. |
| 110 | + |
| 111 | +Taking the set of space children, first order the children with a valid `order` key |
| 112 | +lexicographically by Unicode code-points such that `\x20` (space) is sorted before |
| 113 | +`\x7E` (`~`). Then, take the remaining children and order them by the `origin_server_ts` |
| 114 | +of their `m.space.child` event in ascending numeric order, placing them after the |
| 115 | +children with a valid `order` key in the resulting set. |
| 116 | + |
| 117 | +In cases where the `order` or timestamps are the same, the children are ordered |
| 118 | +lexicographically by their room IDs (state keys) in ascending order. |
| 119 | + |
| 120 | +Noting the careful use of ASCII spaces here, the following demonstrates a set of space |
| 121 | +children being ordered appropriately: |
| 122 | + |
| 123 | +*Unimportant fields trimmed for brevity.* |
| 124 | + |
| 125 | +```json |
| 126 | +[ |
| 127 | + { |
| 128 | + "type": "m.space.child", |
| 129 | + "state_key": "!a:example.org", |
| 130 | + "origin_server_ts": 1640141000000, |
| 131 | + "content": { |
| 132 | + "order": "aaaa", |
| 133 | + "via": ["example.org"] |
| 134 | + } |
| 135 | + }, |
| 136 | + { |
| 137 | + "type": "m.space.child", |
| 138 | + "state_key": "!b:example.org", |
| 139 | + "origin_server_ts": 1640341000000, |
| 140 | + "content": { |
| 141 | + "order": " ", |
| 142 | + "via": ["example.org"] |
| 143 | + } |
| 144 | + }, |
| 145 | + { |
| 146 | + "type": "m.space.child", |
| 147 | + "state_key": "!c:example.org", |
| 148 | + "origin_server_ts": 1640841000000, |
| 149 | + "content": { |
| 150 | + "order": "first", |
| 151 | + "via": ["example.org"] |
| 152 | + } |
| 153 | + }, |
| 154 | + { |
| 155 | + "type": "m.space.child", |
| 156 | + "state_key": "!d:example.org", |
| 157 | + "origin_server_ts": 1640741000000, |
| 158 | + "content": { |
| 159 | + "via": ["example.org"] |
| 160 | + } |
| 161 | + }, |
| 162 | + { |
| 163 | + "type": "m.space.child", |
| 164 | + "state_key": "!e:example.org", |
| 165 | + "origin_server_ts": 1640641000000, |
| 166 | + "content": { |
| 167 | + "via": ["example.org"] |
| 168 | + } |
| 169 | + } |
| 170 | +] |
| 171 | +``` |
| 172 | + |
| 173 | +The above would result in the following order: |
| 174 | + |
| 175 | +1. `!b:example.org` first because `\x20` is before `aaaa` lexically. |
| 176 | +2. `!a:example.org` next because `aaaa` is before `first` lexically. |
| 177 | +3. `!c:example.org` next because `first` is the last `order` value. |
| 178 | +4. `!e:example.org` next because the event timestamp is smallest. |
| 179 | +5. `!d:example.org` last because the event timestamp is largest. |
| 180 | + |
| 181 | +##### `m.space.parent` relationships |
| 182 | + |
| 183 | +Rooms can additionally claim to be part of a space by populating their own state |
| 184 | +with a parent event. Similar to child events within spaces, the parent event's |
| 185 | +`state_key` is the room ID of the parent space and have a similar `via` list |
| 186 | +within their `content` to denote both whether or not the link is valid and which |
| 187 | +servers might be possible to join through. |
| 188 | + |
| 189 | +To avoid situations where a room falsely claims it is part of a given space, |
| 190 | +`m.space.parent` events should be ignored unless one of the following is true: |
| 191 | + |
| 192 | +* A corresponding `m.space.child` event can be found in the supposed parent space. |
| 193 | +* The sender of the `m.space.parent` event has sufficient power level in the |
| 194 | + supposed parent space to send `m.space.child` state events (there doesn't need |
| 195 | + to be a matching child event). |
| 196 | + |
| 197 | +{{% boxes/note %}} |
| 198 | +Clients might need to peek into a parent space to inspect the room state if they |
| 199 | +aren't already joined. If the client is unable to peek the state, the link should |
| 200 | +be assumed to be invalid. |
| 201 | +{{% /boxes/note %}} |
| 202 | + |
| 203 | +{{% boxes/note %}} |
| 204 | +A consequence of the second condition is that a room admin being demoted in the |
| 205 | +parent space, leaving the parent space, or otherwise being removed from the parent |
| 206 | +space can mean that a previously valid `m.space.parent` event becomes invalid. |
| 207 | +{{% /boxes/note %}} |
| 208 | + |
| 209 | +`m.space.parent` events can additionally include a `canonical` boolean key in their |
| 210 | +`content` to denote that the parent space is the main/primary space for the room. |
| 211 | +This can be used to, for example, have the client find other rooms by peeking into |
| 212 | +that space and suggesting them to the user. Only one canonical parent should exist, |
| 213 | +though this is not enforced. To tiebreak, use the lowest room ID sorted lexicographically |
| 214 | +by Unicode code-points. |
| 215 | + |
| 216 | +{{% event event="m.space.parent" %}} |
| 217 | + |
| 218 | +#### Discovering rooms within spaces |
| 219 | + |
| 220 | +Often the client will want to assist the user in exploring what rooms/spaces are part |
| 221 | +of a space. This can be done with crawling [`m.space.child`](#mspacechild) state events |
| 222 | +in the client and peeking into the rooms to get information like the room name, though |
| 223 | +this is impractical for most cases. |
| 224 | + |
| 225 | +Instead, a hierarchy API is provided to walk the space tree and discover the rooms with |
| 226 | +their aesthetic details. |
| 227 | + |
| 228 | +The [`GET /hierarchy`](#get_matrixclientv1roomsroomidhierarchy) API works in a depth-first |
| 229 | +manner: when it encounters another space as a child it recurses into that space before |
| 230 | +returning non-space children. |
| 231 | + |
| 232 | +{{% boxes/warning %}} |
| 233 | +Though prohibited, it is still possible for loops to occur. Servers should gracefully |
| 234 | +break loops. |
| 235 | + |
| 236 | +Additionally, a given child room might appear multiple times in the response as a |
| 237 | +grandchild (for example). |
| 238 | +{{% /boxes/warning %}} |
| 239 | + |
| 240 | +{{% http-api spec="client-server" api="space_hierarchy" %}} |
| 241 | + |
| 242 | +##### Server behaviour |
| 243 | + |
| 244 | +In the case where the server does not have access to the state of a child room, it can |
| 245 | +request the information over federation with the |
| 246 | +[`GET /hierarchy`](/server-server-api/#get_matrixfederationv1hierarchyroomid) API. The |
| 247 | +response to this endpoint should be cached for a period of time. The response might |
| 248 | +additionally contain information about rooms the requesting user is already a member |
| 249 | +of, or that the server is aware of - the local data should be used instead of the remote |
| 250 | +server's data. |
| 251 | + |
| 252 | +Note that the response to this endpoint is contextual based on the user. Servers are |
| 253 | +encouraged to cache the data for a period of time, though permission checks may need to |
| 254 | +be performed to ensure the response is accurate for that user. |
0 commit comments