Skip to content

Commit b67b811

Browse files
turt2liverichvdh
authored andcommitted
First iteration of specifying Spaces
MSCs: * #3288 * #2946 * #1772 Note that this makes modifications to the underlying MSCs as well. These are intended to be minor edits to aid clarity/accuracy of the MSCs, as per the proposal process. Functionally, clients and servers might need to change their behaviour slightly as is expected of implementing this stuff early. Synapse has these changes (alongside backwards compatibility) here: matrix-org/synapse#11667
1 parent 2a5e33a commit b67b811

16 files changed

+923
-119
lines changed

content/client-server-api/_index.md

+22
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,27 @@ the topic to be removed from the room.
18541854

18551855
## Rooms
18561856

1857+
### Types
1858+
1859+
{{% added-in v="1.2" %}}
1860+
1861+
Optionally, rooms can have types to denote their intended function. A room
1862+
without a type does not necessarily mean it has a specific default function,
1863+
though commonly these rooms will be for conversational purposes.
1864+
1865+
Room types are best applied when a client might need to differentiate between
1866+
two different rooms, such as conversation-holding and data-holding. If a room
1867+
has a type, it is specified in the `type` key of an [`m.room.create`](#mroomcreate)
1868+
event. To specify a room's type, provide it as part of `creation_content` on
1869+
the create room request.
1870+
1871+
In this specification the following room types are specified:
1872+
1873+
* [`m.space`](#spaces)
1874+
1875+
Unspecified room types are permitted through the use of
1876+
[Namespaced Identifiers](/appendices/#common-namespaced-identifier-grammar).
1877+
18571878
### Creation
18581879

18591880
The homeserver will create an `m.room.create` event when a room is
@@ -2190,6 +2211,7 @@ that profile.
21902211
| [Server ACLs](#server-access-control-lists-acls-for-rooms) | Optional | Optional | Optional | Optional | Optional |
21912212
| [Server Notices](#server-notices) | Optional | Optional | Optional | Optional | Optional |
21922213
| [Moderation policies](#moderation-policy-lists) | Optional | Optional | Optional | Optional | Optional |
2214+
| [Spaces](#spaces) | Optional | Optional | Optional | Optional | Optional |
21932215

21942216
*Please see each module for more details on what clients need to
21952217
implement.*
+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
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.

content/server-server-api.md

+7
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,13 @@ the server the room directory should be retrieved for.
902902

903903
{{% http-api spec="server-server" api="public_rooms" %}}
904904

905+
## Spaces
906+
907+
To complement the [Client-Server API's Spaces module](/client-server-api/#spaces),
908+
homeservers need a way to query information about spaces from other servers.
909+
910+
{{% http-api spec="server-server" api="space_hierarchy" %}}
911+
905912
## Typing Notifications
906913

907914
When a server's users send typing notifications, those notifications
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Copyright 2021 The Matrix.org Foundation C.I.C.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
type: object
16+
title: "PublicRoomsChunk"
17+
properties:
18+
aliases:
19+
type: array
20+
description: Aliases of the room. May be empty.
21+
items:
22+
type: string
23+
example: ["#general:example.org"]
24+
canonical_alias:
25+
type: string
26+
description: The canonical alias of the room, if any.
27+
example: "#general:example.org"
28+
name:
29+
type: string
30+
description: The name of the room, if any.
31+
example: "General Chat"
32+
num_joined_members:
33+
type: integer
34+
description: The number of members joined to the room.
35+
example: 42
36+
room_id:
37+
type: string
38+
description: The ID of the room.
39+
example: "!abcdefg:example.org"
40+
topic:
41+
type: string
42+
description: The topic of the room, if any.
43+
example: "All things general"
44+
world_readable:
45+
type: boolean
46+
description: Whether the room may be viewed by guest users without joining.
47+
example: false
48+
guest_can_join:
49+
type: boolean
50+
description: |-
51+
Whether guest users may join the room and participate in it.
52+
If they can, they will be subject to ordinary power level
53+
rules like any other user.
54+
example: true
55+
avatar_url:
56+
type: string
57+
format: uri
58+
description: The URL for the room's avatar, if one is set.
59+
example: "mxc://example.org/abcdef"
60+
join_rule:
61+
type: string
62+
description: |-
63+
The room's join rule. When not present, the room is assumed to
64+
be `public`.
65+
example: "public"
66+
required:
67+
- room_id
68+
- num_joined_members
69+
- world_readable
70+
- guest_can_join

0 commit comments

Comments
 (0)