Skip to content

Commit f9d654f

Browse files
kegsayclokeprichvdhara4nturt2live
authored
MSC2946: Spaces Summary (#2946)
* Spaces Summary * MSC2946 * Clarity * More clarity * Clarify what no room data means for clients * Federation API * Update 2946-spaces-summary.md * auto_join filter * Blurb on auth for fed api * Update to reflect MSC1772 changes * Mention auth chain on federation api * Add 'version' field * Stripped state; remove room versions * Update 2946-spaces-summary.md * Update proposals/2946-spaces-summary.md Co-authored-by: Patrick Cloke <[email protected]> * Replace with link to draft doc. * Add a preamble and copy the current draft API. * Switch to using stable identifiers (and add an unstable identifiers section). * Updates / clarifications. * Fix typo. * Clean-ups. * Update proposals/2946-spaces-summary.md Co-authored-by: Travis Ralston <[email protected]> * Drop unstable identifiers from MSC1772. * Various updates and clarifications. * Include the origin_server_ts in the response, as needed by MSC1772. * Rename a parameter for clarity. * Fix typo. Co-authored-by: David Baker <[email protected]> * Various clarifications based on feedback. * Add auth / rate-limiting info. * Combine some double spaces. * Use only GET endpoints. * Add notes about DoS potential. * Tweaks from review. * Add context about why stripped events are returned. * Remove some implementation details. * Add notes on ordering. * Remove unnecessary data. * Clarify the server-server API. * More clarifications. * Remove obsolete note. * Some clarifications to what accessible means. * Update notes about sorting to include the origin_server_ts of the m.space.child event. This reverts commit af8c7b0. * Only consider `m.space` rooms and do not return links to nowhere. * Updates based on MSC3173 merging and updates to MSC3083. * Updates per MSC2403. * Remove field which is not part of the C-S API. * Rewrite the proposal. * Handle todo comments. * Update URLs. * Rename field. * Updates based on implementation. * Clarify the state which is persisted. * Expand notes about errors. * Update MSC with pagination parameter. * Fix wrong endpoint. Co-authored-by: Matthew Hodgson <[email protected]> * Clarifications based on implementation. * Remove empty section. * Fix typo. Co-authored-by: Richard van der Hoff <[email protected]> * Rename field in example. * Clarify error code. * Clarify ordering changes. * Clarify wording. Co-authored-by: Travis Ralston <[email protected]> * Fix typos. Co-authored-by: Hubert Chathi <[email protected]> * Clarify that rooms do not belong to servers. Co-authored-by: Hubert Chathi <[email protected]> * Fix example to use correct URL. Co-authored-by: Hubert Chathi <[email protected]> * Clarify using local vs. remote data. Co-authored-by: Hubert Chathi <[email protected]> * Clarify bits aboud stripped state. * Clarify access control of federation responses. * Clarify error code. Co-authored-by: Hubert Chathi <[email protected]> * Be less prescriptive about expiring data. * Limit must be non-zero. Co-authored-by: Travis Ralston <[email protected]> * Rate limiting. Co-authored-by: Travis Ralston <[email protected]> * Add a note about room upgrades. * Update stable URLs per MSC2844. * Clarify federation return values. * Clarify `origin_server_ts`. Co-authored-by: Andrew Morgan <[email protected]> * Tweak wording around `inaccessible_children`. Co-authored-by: Patrick Cloke <[email protected]> Co-authored-by: Richard van der Hoff <[email protected]> Co-authored-by: Patrick Cloke <[email protected]> Co-authored-by: Matthew Hodgson <[email protected]> Co-authored-by: Travis Ralston <[email protected]> Co-authored-by: David Baker <[email protected]> Co-authored-by: Richard van der Hoff <[email protected]> Co-authored-by: Travis Ralston <[email protected]> Co-authored-by: Hubert Chathi <[email protected]> Co-authored-by: Andrew Morgan <[email protected]>
1 parent 7885c6a commit f9d654f

File tree

1 file changed

+389
-0
lines changed

1 file changed

+389
-0
lines changed

proposals/2946-spaces-summary.md

Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
# MSC2946: Spaces Summary
2+
3+
This MSC depends on [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772), which
4+
describes why a Space is useful:
5+
6+
> Collecting rooms together into groups is useful for a number of purposes. Examples include:
7+
>
8+
> * Allowing users to discover different rooms related to a particular topic: for example "official matrix.org rooms".
9+
> * Allowing administrators to manage permissions across a number of rooms: for example "a new employee has joined my company and needs access to all of our rooms".
10+
> * Letting users classify their rooms: for example, separating "work" from "personal" rooms.
11+
>
12+
> We refer to such collections of rooms as "spaces".
13+
14+
This MSC attempts to solve how a member of a space discovers rooms in that space. This
15+
is useful for quickly exposing a user to many aspects of an entire community, using the
16+
examples above, joining the "official matrix.org rooms" space might suggest joining a few
17+
rooms:
18+
19+
* A room to discuss development of the Matrix Spec.
20+
* An announcements room for news related to matrix.org.
21+
* An off-topic room for members of the space.
22+
23+
## Proposal
24+
25+
A new client-server API (and corresponding server-server API) is added which allows
26+
for querying for the rooms and spaces contained within a space. This allows a client
27+
to efficiently display a hierarchy of rooms to a user (i.e. without having
28+
to walk the full state of each room).
29+
30+
### Client-server API
31+
32+
An endpoint is provided to walk the space tree, starting at the provided room ID
33+
("the root room"), and visiting other rooms/spaces found via `m.space.child`
34+
events. It recurses into the children and into their children, etc.
35+
36+
Any child room that the user is joined or is potentially joinable (per
37+
[MSC3173](https://github.com/matrix-org/matrix-doc/pull/3173)) is included in
38+
the response. When a room with a `type` of `m.space` is found, it is searched
39+
for valid `m.space.child` events to recurse into.
40+
41+
In order to provide a consistent experience, the space tree should be walked in
42+
a depth-first manner, e.g. whenever a space is found it should be recursed into
43+
by sorting the children rooms and iterating through them.
44+
45+
There could be loops in the returned child events; clients and servers should
46+
handle this gracefully. Similarly, note that a child room might appear multiple
47+
times (e.g. also be a grandchild). Clients and servers should handle this
48+
appropriately.
49+
50+
This endpoint requires authentication and is subject to rate-limiting.
51+
52+
#### Request format
53+
54+
```text
55+
GET /_matrix/client/v1/rooms/{roomID}/hierarchy
56+
```
57+
58+
Query Parameters:
59+
60+
* **`suggested_only`**: Optional. If `true`, return only child events and rooms
61+
where the `m.space.child` event has `suggested: true`. Must be a boolean,
62+
defaults to `false`.
63+
64+
This applies transitively, i.e. if a `suggested_only` is `true` and a space is
65+
not suggested then it should not be searched for children. The inverse is also
66+
true, if a space is suggested, but a child of that space is not then the child
67+
should not be included.
68+
* **`limit`**: Optional: a client-defined limit to the maximum
69+
number of rooms to return per page. Must an integer greater than zero.
70+
71+
Server implementations should impose a maximum value to avoid resource
72+
exhaustion.
73+
* **`max_depth`**: Optional: The maximum depth in the tree (from the root room)
74+
to return. The deepest depth returned will not include children events. Defaults
75+
to no-limit. Must be a non-negative integer.
76+
77+
Server implementations may wish to impose a maximum value to avoid resource
78+
exhaustion.
79+
* **`from`**: Optional. Pagination token given to retrieve the next set of rooms.
80+
81+
Note that if a pagination token is provided, then the parameters given for
82+
`suggested_only` and `max_depth` must be the same.
83+
84+
#### Response Format
85+
86+
* **`rooms`**: `[object]` For each room/space, starting with the root room, a
87+
summary of that room. The fields are the same as those returned by
88+
`/publicRooms` (see
89+
[spec](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-publicrooms)),
90+
with the addition of:
91+
* **`room_type`**: the value of the `m.type` field from the room's
92+
`m.room.create` event, if any.
93+
* **`children_state`**: The stripped state of the `m.space.child` events of
94+
the room per [MSC3173](https://github.com/matrix-org/matrix-doc/pull/3173).
95+
In addition to the standard stripped state fields, the following is included:
96+
* **`origin_server_ts`**: `integer`. The `origin_server_ts` field from the
97+
room's `m.space.child` event. This is required for sorting of rooms as
98+
specified below.
99+
* **`next_batch`**: Optional `string`. The token to supply in the `from` param
100+
of the next `/hierarchy` request in order to request more rooms. If this is absent,
101+
there are no more results.
102+
103+
#### Example request:
104+
105+
```text
106+
GET /_matrix/client/v1/rooms/%21ol19s%3Ableecker.street/hierarchy?
107+
limit=30&
108+
suggested_only=true&
109+
max_depth=4
110+
```
111+
112+
#### Example response:
113+
114+
```jsonc
115+
{
116+
"rooms": [
117+
{
118+
"room_id": "!ol19s:bleecker.street",
119+
"avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
120+
"guest_can_join": false,
121+
"name": "CHEESE",
122+
"num_joined_members": 37,
123+
"topic": "Tasty tasty cheese",
124+
"world_readable": true,
125+
"join_rules": "public",
126+
"room_type": "m.space",
127+
"children_state": [
128+
{
129+
"type": "m.space.child",
130+
"state_key": "!efgh:example.com",
131+
"content": {
132+
"via": ["example.com"],
133+
"suggested": true
134+
},
135+
"room_id": "!ol19s:bleecker.street",
136+
"sender": "@alice:bleecker.street",
137+
"origin_server_ts": 1432735824653
138+
},
139+
{ ... }
140+
]
141+
},
142+
{ ... }
143+
],
144+
"next_batch": "abcdef"
145+
}
146+
```
147+
148+
#### Errors:
149+
150+
An HTTP response with a status code of 403 and an error code of `M_FORBIDDEN`
151+
should be returned if the user doesn't have permission to view/peek the root room.
152+
This should also be returned if that room does not exist, which matches the
153+
behavior of other room endpoints (e.g.
154+
[`/_matrix/client/r0/rooms/{roomID}/aliases`](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-rooms-roomid-aliases))
155+
to not divulge that a room exists which the user doesn't have permission to view.
156+
157+
An HTTP response with a status code of 400 and an error code of `M_INVALID_PARAM`
158+
should be returned if the `from` token provided is unknown to the server or if
159+
the `suggested_only` or `max_depth` parameters are modified during pagination.
160+
161+
#### Server behaviour
162+
163+
The server should generate the response as discussed above, by doing a depth-first
164+
search (starting at the "root" room) for any `m.space.child` events. Any
165+
`m.space.child` with an invalid `via` are discarded (invalid is defined as in
166+
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772): missing, not an
167+
array or an empty array).
168+
169+
In the case of the homeserver not having access to the state of a room, the
170+
server-server API (see below) can be used to query for this information over
171+
federation from one of the servers provided in the `via` key of the
172+
`m.space.child` event. It is recommended to cache the federation response for a
173+
period of time. The federation results may contain information on a room
174+
that the requesting server is already participating in; the requesting server
175+
should use its local data for such rooms rather than the data returned over
176+
federation.
177+
178+
When the current response page is full, the current state should be persisted
179+
and a pagination token should be generated (if there is more data to return).
180+
To prevent resource exhaustion, the server may expire persisted data that it
181+
deems to be stale.
182+
183+
The persisted state will include:
184+
185+
* The processed rooms.
186+
* Rooms to process (in depth-first order with rooms at the same depth
187+
ordered [according to MSC1772, as updated to below](#msc1772-ordering)).
188+
* Room information from federation responses for rooms which have yet to be
189+
processed.
190+
191+
### Server-server API
192+
193+
The Server-Server API has a similar interface to the Client-Server API, but a
194+
simplified response. It is used when a homeserver is not participating in a room
195+
(and cannot summarize room due to not having the state).
196+
197+
The main difference is that it does *not* recurse into spaces and does not support
198+
pagination. This is somewhat equivalent to a Client-Server request with a `max_depth=1`.
199+
200+
Additional federation requests are made to recurse into sub-spaces. This allows
201+
for trivially caching responses for a short period of time (since it is not
202+
easily known the room summary might have changed).
203+
204+
Since the server-server API does not know the requesting user, the response should
205+
divulge information based on if any member of the requesting server could join
206+
the room. The requesting server is trusted to properly filter this information
207+
using the `world_readable`, `join_rules`, and `allowed_room_ids` fields from the
208+
response.
209+
210+
If the target server is not a member of some children rooms (so would have to send
211+
another request over federation to inspect them), no attempt is made to recurse
212+
into them. They are simply omitted from the `children` key of the response.
213+
(Although they will still appear in the `children_state`key of the `room`.)
214+
215+
Similarly, if a server-set limit on the size of the response is reached, additional
216+
rooms are not added to the response and can be queried individually.
217+
218+
#### Request format
219+
220+
```text
221+
GET /_matrix/federation/v1/hierarchy/{roomID}
222+
```
223+
224+
Query Parameters:
225+
226+
* **`suggested_only`**: The same as the Client-Server API.
227+
228+
#### Response format
229+
230+
The response format is similar to the Client-Server API:
231+
232+
* **`room`**: `object` The summary of the requested room, see below for details.
233+
* **`children`**: `[object]` For each room/space, a summary of that room, see
234+
below for details.
235+
* **`inaccessible_children`**: Optional `[string]`. A list of room IDs which are
236+
children of the requested room, but are inaccessible to the requesting server.
237+
Assuming the target server is non-malicious and well-behaved, then other
238+
non-malicious servers should respond with the same set of inaccessible rooms.
239+
Thus the requesting server can consider the rooms inaccessible from everywhere.
240+
241+
This is used to differentiate between rooms which the requesting server does
242+
not have access to from those that the target server cannot include in the
243+
response (which will simply be missing in the response).
244+
245+
For both the `room` and `children` fields the summary of the room/space includes
246+
the fields returned by `/publicRooms` (see [spec](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-publicrooms)),
247+
with the addition of:
248+
249+
* **`room_type`**: the value of the `m.type` field from the room's `m.room.create`
250+
event, if any.
251+
* **`allowed_room_ids`**: A list of room IDs which give access to this room per
252+
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083).<sup id="a1">[1](#f1)</sup>
253+
254+
#### Example request:
255+
256+
```jsonc
257+
GET /_matrix/federation/v1/hierarchy/{roomID}?
258+
suggested_only=true
259+
```
260+
261+
#### Errors:
262+
263+
An HTTP response with a status code of 404 and an error code of `M_NOT_FOUND` is
264+
returned if the target server is not a member of the requested room or the
265+
requesting server is not allowed to access the room.
266+
267+
### MSC1772 Ordering
268+
269+
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772) defines the ordering
270+
of "default ordering of siblings in the room list" using the `order` key:
271+
272+
> Rooms are sorted based on a lexicographic ordering of the Unicode codepoints
273+
> of the characters in `order` values. Rooms with no `order` come last, in
274+
> ascending numeric order of the `origin_server_ts` of their `m.room.create`
275+
> events, or ascending lexicographic order of their `room_id`s in case of equal
276+
> `origin_server_ts`. `order`s which are not strings, or do not consist solely
277+
> of ascii characters in the range `\x20` (space) to `\x7F` (~), or consist of
278+
> more than 50 characters, are forbidden and the field should be ignored if
279+
> received.
280+
281+
Unfortunately there are situations when a homeserver comes across a reference to
282+
a child room that is unknown to it and must decide the ordering. Without being
283+
able to see the `m.room.create` event (which it might not have permission to see)
284+
no proper ordering can be given.
285+
286+
Consider the following case of a space with 3 child rooms:
287+
288+
```
289+
Space A
290+
|
291+
+--------+--------+
292+
| | |
293+
Room B Room C Room D
294+
```
295+
296+
HS1 has users in Space A, Room B, and Room C, while HS2 has users in Room D. HS1 has no users
297+
in Room D (and thus has no state from it). Room B, C, and D do not have an
298+
`order` field set (and default to using the ordering rules above).
299+
300+
When a user asks HS1 for the space summary with a `limit` equal to `2` it cannot
301+
fulfill this request since it is unsure how to order Room B, Room C, and Room D,
302+
but it can only return 2 of them. It *can* reach out over federation to HS2 and
303+
request a space summary for Room D, but this is undesirable:
304+
305+
* HS1 might not have the permissions to know any of the state of Room D, so might
306+
receive a 404 error.
307+
* If we expand the example above to many rooms than this becomes expensive to
308+
query a remote server simply for ordering.
309+
310+
This proposes changing the ordering rules from MSC1772 to the following:
311+
312+
> Rooms are sorted based on a lexicographic ordering of the Unicode codepoints
313+
> of the characters in `order` values. Rooms with no `order` come last, in
314+
> ascending numeric order of the `origin_server_ts` of their `m.space.child`
315+
> events, or ascending lexicographic order of their `room_id`s in case of equal
316+
> `origin_server_ts`. `order`s which are not strings, or do not consist solely
317+
> of ascii characters in the range `\x20` (space) to `\x7E` (~), or consist of
318+
> more than 50 characters, are forbidden and the field should be ignored if
319+
> received.
320+
321+
This modifies the clause for calculating the order to use the `origin_server_ts`
322+
of the `m.space.child` event instead of the `m.room.create` event. This allows
323+
for a defined sorting of siblings based purely on the information available in
324+
the state of the space while still allowing for a natural ordering due to the
325+
age of the relationship.
326+
327+
## Potential issues
328+
329+
A large flat space (a single room with many `m.space.child` events) could cause
330+
a large federation response.
331+
332+
Room version upgrades of rooms in a space are unsolved and left to a future MSC.
333+
When upgrading a room it is unclear if the old room should be removed (in which
334+
case users who have not yet joined the new room will no longer see it in the space)
335+
or leave the old room (in which case users who have joined the new room will see
336+
both). The current recommendation is for clients de-duplicate rooms which are
337+
known old versions of rooms in the space.
338+
339+
## Alternatives
340+
341+
Peeking to explore the room state could be used to build the tree of rooms/spaces,
342+
but this would be significantly more expensive for both clients and servers. It
343+
would also require peeking over federation (which is explored in
344+
[MSC2444](https://github.com/matrix-org/matrix-doc/pull/2444)).
345+
346+
## Security considerations
347+
348+
A space with many sub-spaces and rooms on different homeservers could cause
349+
a large number of federation requests. A carefully crafted space with inadequate
350+
server enforced limits could be used in a denial of service attack. Generally
351+
this is mitigated by enforcing server limits and caching of responses.
352+
353+
The requesting server over federation is trusted to filter the response for the
354+
requesting user. The alternative, where the requesting server sends the requesting
355+
`user_id`, and the target server does the filtering, is unattractive because it
356+
rules out a caching of the result. This does not decrease security since a server
357+
could lie and make a request on behalf of a user in the proper space to see the
358+
given information. I.e. the calling server must be trusted anyway.
359+
360+
## Unstable prefix
361+
362+
During development of this feature it will be available at unstable endpoints.
363+
364+
The client-server API will be:
365+
`/_matrix/client/unstable/org.matrix.msc2946/rooms/{roomID}/hierarchy`
366+
367+
The server-server API will be:
368+
`/_matrix/federation/unstable/org.matrix.msc2946/hierarchy/{roomID}`
369+
370+
## Footnotes
371+
372+
<a id="f1"/>[1]: As a worked example, in the context of
373+
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083), consider that Alice
374+
and Bob share a server; Alice is a member of a space, but Bob is not. A remote
375+
server will not know whether the request is on behalf of Alice or Bob (and hence
376+
whether it should share details of restricted rooms within that space).
377+
378+
Consider if the space is modified to include a restricted room on a different server
379+
which allows access from the space. When summarizing the space, the homeserver must make
380+
a request over federation for information on the room. The response should include
381+
the room (since Alice is able to join it). Without additional information the
382+
calling server does not know *why* they received the room and cannot properly
383+
filter the returned results.
384+
385+
Note that there are still potential situations where each server individually
386+
doesn't have enough information to properly return the full summary, but these
387+
do not seem reasonable in what is considered a normal structure of spaces. (E.g.
388+
in the above example, if the remote server is not in the space and does not know
389+
whether the server is in the space or not it cannot return the room.)[↩](#a1)

0 commit comments

Comments
 (0)