# Spaces A space is a room whose create event says `"type": "m.space"`, and whose `m.space.child` state events name the rooms in it. As with relations, the authorisation rules know nothing of it: a child event is a pointer, and the room it points at need not know. What the server provides is the walk. The code is in `internal/roomserver/hierarchy.go`. ## `GET /_matrix/client/v1/rooms/{roomID}/hierarchy` The space's tree, breadth first, a page at a time. Each room is described the way the public room directory describes it — name, topic, avatar, alias, member count, join rule, room type, room version and encryption — plus its `children_state`: the `m.space.child` events, each with its `origin_server_ts`. | Parameter | | |---|---| | `max_depth` | How far below the root to go; `0` is the root alone. Capped at 50. | | `suggested_only` | Follow only children marked `"suggested": true`. | | `limit` | Rooms per page: 50 by default, 100 at most. | | `from` | The previous page's `next_batch`. | Children are visited in the spec's order: those with a valid `order` (printable ASCII, at most 50 characters) by that string, then by when the child event was sent, then by room ID. A child event whose `via` is missing or empty is how a space removes a child — a state event cannot be deleted, only emptied — and is skipped. Only a space's children are walked into: an ordinary room that happens to hold an `m.space.child` event is not a space. Paging walks from the root again and skips what earlier pages returned. That keeps the server free of per-walk state, and a walk is capped at 2000 rooms whatever the page size, which bounds the cost of the last page of a very large space. ### Who sees what A room is shown to a user who: - is joined to it or invited to it; or - may know about it anyway: its join rule is `public`, `knock` or `knock_restricted`, or its history is world-readable; or - is joined to one of the rooms a `restricted` room lets in. Everything else is left out, and so are its children. The root itself is the exception: a space the user may not see is `403`. ### Rooms this server is not in A child this server is not in is asked about over federation, of the servers its `via` names, with `GET /_matrix/federation/v1/hierarchy`. The answer is remembered for the rest of the walk. The other server has already judged the room for this server as a whole; Warp then checks a restricted room's allow list against its own user. A child that no server will describe is left out. With federation off, rooms this server is not in are simply not shown. ## `GET /_matrix/federation/v1/hierarchy/{roomID}` Another server walking one of Warp's spaces: the room and the children Warp is in, each judged for the asking server. A room is shown to a server when anybody could be shown it (see above), when it is restricted — with `allowed_room_ids`, so the asking server can check its own users — or when a user of that server is joined or invited. Children it may not see are listed in `inaccessible_children`. A root it may not see is `404`, which is also the answer for a room Warp is not in. ## `GET /_matrix/client/v1/room_summary/{roomIDOrAlias}` One room's face, for a link preview or an invite: the same fields as a hierarchy room without its children, plus the user's `membership` (`leave` for a user who was never in it). An alias is resolved first, and the servers it names are added to `?via=`. The same visibility rules apply; a room the user may not see is `403`. The endpoint is in the spec from v1.15. Warp still advertises v1.14, because v1.15 also brought requirements Warp does not meet yet. ## What is deliberately missing | Thing | Why | |---|---| | Unauthenticated `room_summary` | The spec allows it for guests; Warp asks for a token. | | Caching other servers' hierarchy answers between walks | Each walk asks again; a space with many remote children costs a federation request per child per page. | | `m.space.parent` checks | Parent events are state like any other, and are not used to validate children. |