|
| 1 | +# MSC3266: Room Summary API |
| 2 | + |
| 3 | +Quite a few clients and tools have a need to preview a room: |
| 4 | + |
| 5 | +- matrix.to may want to show avatar and name of a room. |
| 6 | +- Nextcloud may want to list the names and avatars of your `/joined_rooms` when |
| 7 | + asking where to share the media. |
| 8 | +- A client may want to preview a room, when hovering a room alias, id or after |
| 9 | + clicking on it. |
| 10 | +- A client may want to preview a room, when the user is trying to knock on it or |
| 11 | + to show pending knocks. |
| 12 | +- A traveller bot may use that to show a room summary on demand without actually |
| 13 | + keeping the whole room state around and having to subscribe to /sync (or |
| 14 | + using the appservice API). |
| 15 | +- A client can use this to knock on a room instead of joining it when the user |
| 16 | + tries to join my room alias or link. |
| 17 | +- External services can use this API to preview rooms like shields.io. |
| 18 | + |
| 19 | +There are a few ways to request a room summary, but they only support some of |
| 20 | +the use cases. The [spaces hierarchy API](https://spec.matrix.org/v1.13/client-server-api/#get_matrixclientv1roomsroomidhierarchy) only provides |
| 21 | +limited control over what rooms to summarize and returns a lot more data than |
| 22 | +necessary. `{roomid}/initialSync` and `{roomid}/state/{event_type}` don't work |
| 23 | +over federation and are much heavier than necessary or need a lot of http calls |
| 24 | +for each room. |
| 25 | + |
| 26 | +## Proposal |
| 27 | + |
| 28 | +A new client-server API, which allows you to fetch a summary of a room by id or |
| 29 | +alias. |
| 30 | + |
| 31 | +### Client-Server API |
| 32 | + |
| 33 | +The API returns a summary of the given room, provided the user is either already |
| 34 | +a member, or has the necessary permissions to join. (For example, the user may |
| 35 | +be a member of a room mentioned in an `allow` condition in the join rules of a |
| 36 | +restricted room.) |
| 37 | + |
| 38 | +For unauthenticated requests a response should only be returned if the room is |
| 39 | +publicly accessible; specifically, that means either: |
| 40 | + * the room has `join_rule: public` or `join_rule: knock`, or: |
| 41 | + * the room has `history_visibility: world_readable`. |
| 42 | + |
| 43 | +Note that rooms the user has been invited to or knocked at might result in |
| 44 | +outdated or partial information, or even a 404 `M_NOT_FOUND` response, since |
| 45 | +the the homeserver may not have access to the current state of the room. (In |
| 46 | +particular: the federation API does not provide a mechanism to get the current |
| 47 | +state of a non-publically-joinable room where the requesting server has no |
| 48 | +joined members. Improving this is left for a future MSC.) |
| 49 | + |
| 50 | +A request could look like this: |
| 51 | + |
| 52 | +``` |
| 53 | +GET /_matrix/client/v1/room_summary/{roomIdOrAlias}? |
| 54 | + via=matrix.org& |
| 55 | + via=neko.dev |
| 56 | +``` |
| 57 | + |
| 58 | +(This is not under `/rooms`, because it can be used with an alias.) |
| 59 | + |
| 60 | +- `roomIdOrAlias` can be the roomid or an alias to a room. |
| 61 | +- `via` are servers that should be tried to request a summary from, if it can't |
| 62 | + be generated locally. These can be from a matrix URI, matrix.to link or a |
| 63 | + `m.space.child` event for example. |
| 64 | + |
| 65 | +A successful `200` response should contain a JSON object giving information about the room. For example: |
| 66 | + |
| 67 | +```json5 |
| 68 | +{ |
| 69 | + room_id: "!ol19s:bleecker.street", |
| 70 | + avatar_url: "mxc://bleecker.street/CHEDDARandBRIE", |
| 71 | + guest_can_join: false, |
| 72 | + name: "CHEESE", |
| 73 | + num_joined_members: 37, |
| 74 | + topic: "Tasty tasty cheese", |
| 75 | + world_readable: true, |
| 76 | + join_rule: "public", |
| 77 | + room_type: "m.space", |
| 78 | + membership: "invite", |
| 79 | + encryption: "m.megolm.v100", |
| 80 | + room_version: "9001", |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +See below for a more detailed description of the response. |
| 85 | + |
| 86 | +#### Unauthenticated and guest access |
| 87 | + |
| 88 | +This API may optionally be exposed to unauthenticated users, or guest users, at the choice of |
| 89 | +server implementations and administrators. Clients MUST NOT rely on being able |
| 90 | +to use the endpoint without authentication, and should degrade gracefully if |
| 91 | +access is denied. |
| 92 | + |
| 93 | + * Rationale: unauthenticated access is beneficial for third-party services such as |
| 94 | + https://matrix.to. On the other hand, allowing unauthenticated access may leak |
| 95 | + information about rooms that would otherwise be restricted to registered users (particularly |
| 96 | + on servers which do not allow public federation), and may lead to unexpected |
| 97 | + resource usage. |
| 98 | + |
| 99 | +Servers may rate limit how often they fetch information over federation more heavily, if the |
| 100 | +user is unauthenticated. |
| 101 | + |
| 102 | +When the endpoint is called unauthenticated, the `membership` field will be |
| 103 | +absent in the response. |
| 104 | + |
| 105 | +As mentioned above, a successful response should only be returned for |
| 106 | +unauthenticated requests where the room is publicly accessible. |
| 107 | + |
| 108 | +#### Response format |
| 109 | + |
| 110 | +If the room cannot be found, the server should return a `404` |
| 111 | +HTTP status code along with an `M_NOT_FOUND` error code. The server should |
| 112 | +NOT return `M_UNAUTHORIZED` or otherwise divulge existence of a room, that |
| 113 | +requires authentication to preview, if the request is unauthenticated or |
| 114 | +authenticated by a user without access to the room. |
| 115 | + |
| 116 | +If the request is successful, the server returns a JSON object containing the |
| 117 | +following properties: |
| 118 | + |
| 119 | +| property name | description | |
| 120 | +| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | |
| 121 | +| avatar_url | Optional. Avatar of the room | |
| 122 | +| canonical_alias | Optional. The canonical alias of the room, if any. | |
| 123 | +| guest_can_join | Required. Whether guests can join the room. | |
| 124 | +| join_rule | Optional. Join rules of the room | |
| 125 | +| name | Optional. Name of the room | |
| 126 | +| num_joined_members | Required. Member count of the room | |
| 127 | +| room_id | Required. Id of the room | |
| 128 | +| room_type | Optional. Type of the room, if any, i.e. `m.space` | |
| 129 | +| topic | Optional. Topic of the room | |
| 130 | +| world_readable | Required. If the room history can be read without joining. | |
| 131 | +| allowed_room_ids | Optional. If the room is a restricted room, these are the room IDs which are specified by the join rules. Empty or omitted otherwise. | |
| 132 | +| encryption | Optional. If the room is encrypted, this specifies the algorithm used for this room. Otherwise, omitted. | |
| 133 | +| membership | Optional (1). The current membership of this user in the room. Usually `leave` if the server has no local users (so fetches the room over federation). | |
| 134 | +| room_version | Optional (for historical reasons (2)). Version of the room. | |
| 135 | + |
| 136 | +(1) The `membership` field will not be present when called unauthenticated, but |
| 137 | +is required when called authenticated. |
| 138 | + |
| 139 | +(2) Prior to this MSC, `/_matrix/federation/v1/hierarchy/{roomId}` doesn't |
| 140 | +return the room version, so `room_version` may be unavailable for remote |
| 141 | +rooms. |
| 142 | + |
| 143 | +Most of the fields above are the same as those returned by |
| 144 | +[`/_matrix/client/v3/publicRooms`](https://spec.matrix.org/v1.13/client-server-api/#get_matrixclientv3publicrooms). (Those |
| 145 | +same fields are also a subset of those returned by |
| 146 | +[`/_matrix/client/v1/rooms/{roomId}/hierarchy`](https://spec.matrix.org/v1.13/client-server-api/#get_matrixclientv1roomsroomidhierarchy).) The |
| 147 | +exceptions are: |
| 148 | + |
| 149 | + * `allowed_room_ids`. This is currently accessible via the federation |
| 150 | + hierarchy endpoint [`GET |
| 151 | + /_matrix/federation/v1/hierarchy/{roomId}`](https://spec.matrix.org/v1.13/server-server-api/#get_matrixfederationv1hierarchyroomid), |
| 152 | + and is necessary |
| 153 | + to distinguish if the room can be joined or only knocked at. |
| 154 | + |
| 155 | + * `encryption`. Currently part of the [stripped |
| 156 | + state](https://spec.matrix.org/v1.13/client-server-api/#stripped-state). Some |
| 157 | + users may only want to join encrypted rooms; alternatively, clients may want to filter |
| 158 | + out encrypted rooms, for example if they don't support encryption, or do not |
| 159 | + support particular encryption algorithms. |
| 160 | + |
| 161 | + * `membership`. Exposed solely for convenience: a client has many other ways |
| 162 | + to access this information. |
| 163 | + |
| 164 | + * `room_version`. Also part of the [stripped |
| 165 | + state](https://spec.matrix.org/v1.13/client-server-api/#stripped-state). |
| 166 | + Can be used by clients to show incompatibilities with a room early. |
| 167 | + |
| 168 | +#### Modifications to `/_matrix/client/v1/rooms/{roomId}/hierarchy` |
| 169 | + |
| 170 | +For symmetry the `room_version`, `allowed_room_ids` and `encryption` fields are |
| 171 | +also added to the `/hierarchy` API. |
| 172 | + |
| 173 | +### Server-Server API |
| 174 | + |
| 175 | +For fetching room summaries of a room a server is not joined to, the federation API of the |
| 176 | +[`/hierarchy`](https://spec.matrix.org/v1.13/server-server-api/#get_matrixfederationv1hierarchyroomid) |
| 177 | +endpoint is reused. This provides (with a few changes) all the information |
| 178 | +needed in this MSC, but it also provides a few additional fields and one level |
| 179 | +of children of this room. |
| 180 | + |
| 181 | +Additionally the `encryption` and `room_version` fields are added to the |
| 182 | +responses for each room. |
| 183 | + |
| 184 | +In theory one could also add the `max_depth` parameter with allowed values of 0 |
| 185 | +and 1, so that child rooms are excluded, but this performance optimization does |
| 186 | +not seem necessary at this time and could be added at any later point while |
| 187 | +degrading gracefully. |
| 188 | + |
| 189 | +(Originally there was a separate federation API for this, but it was decided by |
| 190 | +the author that lowering the duplication on the federation side is the way to |
| 191 | +go.) |
| 192 | + |
| 193 | +## Potential issues |
| 194 | + |
| 195 | +### Performance |
| 196 | + |
| 197 | +Clients may start calling this API very often instead of using the |
| 198 | +[`/hierarchy`](https://spec.matrix.org/v1.13/client-server-api/#get_matrixclientv1roomsroomidhierarchy) |
| 199 | +for spaces or caching the state received via `/sync`. |
| 200 | +Looking up all the state events required for this API may cause performance |
| 201 | +issues in that case. |
| 202 | + |
| 203 | +To mitigate that, servers are recommended to cache the response for this API and |
| 204 | +apply rate limiting if necessary. |
| 205 | + |
| 206 | +## Alternatives |
| 207 | + |
| 208 | +### The Space Summary / `/hierarchy` API |
| 209 | + |
| 210 | +The |
| 211 | +[`/hierarchy`](https://spec.matrix.org/v1.13/client-server-api/#get_matrixclientv1roomsroomidhierarchy) |
| 212 | +API could be used, but it returns more data than necessary by default (but it |
| 213 | +can be limited to just 1 room) such as all the `m.space.child` events in a |
| 214 | +space, but also is missing the room version, membership and the encryption |
| 215 | +field. |
| 216 | + |
| 217 | +Additionally the `/hierarchy` API doesn't work using aliases. This currently |
| 218 | +doesn't allow users to preview rooms not known to the local server over |
| 219 | +federation. While the user can resolve the alias and then call the `/hierarchy` |
| 220 | +API using the resolved roomid, a roomid is not a routable entity, so the server |
| 221 | +never receives the information which servers to ask about the requested rooms. |
| 222 | +This MSC resolves that by providing a way to pass server names to ask for the |
| 223 | +room as well as the alias directly. |
| 224 | + |
| 225 | +For server to server communication the efficiency is not as important, which is |
| 226 | +why we use the same API as the `/hierarchy` API to fetch the data over |
| 227 | +federation. |
| 228 | + |
| 229 | +### The `sync` API |
| 230 | + |
| 231 | +For joined rooms, the `/sync` API can be used to get a summary for all joined |
| 232 | +rooms. Apart from not working for unjoined rooms, like knocks, invites and space |
| 233 | +children, `/sync` is very heavy for the server and the client needs to cobble |
| 234 | +together information from the `state`, `timeline` and |
| 235 | +[`summary`](https://github.com/matrix-org/matrix-doc/issues/688) sections to |
| 236 | +calculate the room name, topic and other fields provided in this MSC. |
| 237 | + |
| 238 | +Furthermore, the membership counts in the summary field are only included, if |
| 239 | +the client is using lazy loading. This MSC provides similar information as |
| 240 | +calling `/sync`, but to allow it to work for unjoined rooms it only uses information |
| 241 | + from the stripped state. Additionally, it excludes `m.heroes` as well as membership |
| 242 | +events, since those are not included in the stripped state of a room. (A client |
| 243 | +can call `/joined_members` to receive those if needed. It may still make sense |
| 244 | +to include heroes so that clients could construct a human-friendly room display |
| 245 | +name in case both the name and the canonical alias are absent; but solving the |
| 246 | +security implications with that may better be left to a separate MSC.) |
| 247 | + |
| 248 | +### The `/state` API |
| 249 | + |
| 250 | +The `/state` API could be used, but the response is much bigger than needed, |
| 251 | +can't be cached as easily and may need more requests. This also doesn't work |
| 252 | +over federation (yet). The variant of this API, which returns the full state of |
| 253 | +a room, also does not return stripped events, which prevents it from being used |
| 254 | +by non-members. The event for specific events DOES return stripped events, but |
| 255 | +could not provide a member count for a room. |
| 256 | + |
| 257 | +### Proper peeking |
| 258 | + |
| 259 | +Peeking could solve this too, but with additional overhead and |
| 260 | +[MSC2753](https://github.com/matrix-org/matrix-doc/pull/2753) is much more |
| 261 | +complex. You need to add a peek and remember to remove it. For many usecases you |
| 262 | +just want to do one request to get info about a room, no history and no updates. |
| 263 | +This MSC solves that by reusing the existing hierarchy APIs, returns a |
| 264 | +lightweight response and provides a convenient API instead. |
| 265 | + |
| 266 | +### A more batched API |
| 267 | + |
| 268 | +This API could take a list of rooms with included `via`s for each room instead |
| 269 | +of a single room (as a POST request). This may have performance benefits for the |
| 270 | +federation API and a client could then easily request a summary of all joined |
| 271 | +rooms. It could still request the summary of a single room by just including |
| 272 | +only a single room in the POST or a convenience GET could be provided by the |
| 273 | +server (that looks like this proposal). At the same time, a batched API is inherently |
| 274 | +more complex to implement for both clients and servers. Additionally, there are no |
| 275 | +known use cases yet that would benefit from batched access. |
| 276 | + |
| 277 | +### MSC3429: Individual room preview API (closed) |
| 278 | + |
| 279 | +[MSC3429](https://github.com/matrix-org/matrix-doc/pull/3429) is an alternative |
| 280 | +implementation, but it chooses a different layout. While this layout might make |
| 281 | +sense in the future, it is inconsistent with the APIs already in use, harder to |
| 282 | +use for clients (iterate array over directly including the interesting fields) |
| 283 | +and can't reuse the federation API. In my opinion an MSC in the future, that |
| 284 | +bases all summary APIs on a list of stripped events seems like the more |
| 285 | +reasonable approach to me and would make the APIs more extensible. |
| 286 | + |
| 287 | +## Security considerations |
| 288 | + |
| 289 | +This API may leak data, if implemented incorrectly or malicious servers could |
| 290 | +return wrong results for a summary. |
| 291 | + |
| 292 | +Those are the same concerns as on [MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946) |
| 293 | +or [MSC3173](https://github.com/matrix-org/matrix-doc/pull/3173). |
| 294 | + |
| 295 | +This API could also be used for denial of service type attacks. Appropriate |
| 296 | +ratelimiting and caching should be able to mitigate that. |
| 297 | + |
| 298 | +## Unstable prefix |
| 299 | + |
| 300 | +This uses the `im.nheko.summary` unstable prefix. As such the paths are prefixed |
| 301 | +with `unstable/im.nheko.summary`. |
| 302 | + |
| 303 | +- the client API will be |
| 304 | + `/_matrix/client/unstable/im.nheko.summary/summary/{roomIdOrAlias}`. |
| 305 | + |
| 306 | +Some implementations still use |
| 307 | + `/_matrix/client/unstable/im.nheko.summary/rooms/{roomIdOrAlias}/summary`, |
| 308 | + but this was a mistake in this MSC. Endpoints using aliases shouldn't be under /rooms. |
| 309 | + |
| 310 | +Additionally the fields `encryption` and `room_version` in the summaries are |
| 311 | +prefixed with `im.nheko.summary` as well since it is new. The latter might still |
| 312 | +be called `im.nheko.summary.version` in some implementations. |
0 commit comments