Add Spaces to the spec (#3610)

* First iteration of specifying Spaces

MSCs:
* https://github.com/matrix-org/matrix-doc/pull/3288
* https://github.com/matrix-org/matrix-doc/pull/2946
* https://github.com/matrix-org/matrix-doc/pull/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: https://github.com/matrix-org/synapse/pull/11667

* add changelogs

* Accuracy per review

* Apply suggestions from code review

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* fully prefix new endpoints

* Fully prefix endpoint in 3616 too

* Fix ordering example

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
Travis Ralston 2022-01-17 10:03:14 -07:00 committed by GitHub
parent 21882b6006
commit 9af83dfd41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 924 additions and 138 deletions

View file

@ -0,0 +1,70 @@
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: "PublicRoomsChunk"
properties:
aliases:
type: array
description: Aliases of the room. May be empty.
items:
type: string
example: ["#general:example.org"]
canonical_alias:
type: string
description: The canonical alias of the room, if any.
example: "#general:example.org"
name:
type: string
description: The name of the room, if any.
example: "General Chat"
num_joined_members:
type: integer
description: The number of members joined to the room.
example: 42
room_id:
type: string
description: The ID of the room.
example: "!abcdefg:example.org"
topic:
type: string
description: The topic of the room, if any.
example: "All things general"
world_readable:
type: boolean
description: Whether the room may be viewed by guest users without joining.
example: false
guest_can_join:
type: boolean
description: |-
Whether guest users may join the room and participate in it.
If they can, they will be subject to ordinary power level
rules like any other user.
example: true
avatar_url:
type: string
format: uri
description: The URL for the room's avatar, if one is set.
example: "mxc://example.org/abcdef"
join_rule:
type: string
description: |-
The room's join rule. When not present, the room is assumed to
be `public`.
example: "public"
required:
- room_id
- num_joined_members
- world_readable
- guest_can_join

View file

@ -22,61 +22,19 @@ properties:
description: |-
A paginated chunk of public rooms.
items:
type: object
title: "PublicRoomsChunk"
required:
- room_id
- num_joined_members
- world_readable
- guest_can_join
properties:
aliases:
type: array
description: |-
Aliases of the room. May be empty.
items:
type: string
canonical_alias:
type: string
description: |-
The canonical alias of the room, if any.
name:
type: string
description: |-
The name of the room, if any.
num_joined_members:
type: integer
description: |-
The number of members joined to the room.
room_id:
type: string
description: |-
The ID of the room.
topic:
type: string
description: |-
The topic of the room, if any.
world_readable:
type: boolean
description: |-
Whether the room may be viewed by guest users without joining.
guest_can_join:
type: boolean
description: |-
Whether guest users may join the room and participate in it.
If they can, they will be subject to ordinary power level
rules like any other user.
avatar_url:
type: string
format: uri
description: The URL for the room's avatar, if one is set.
join_rule:
type: string
description: |-
The room's join rule. When not present, the room is assumed to
be `public`. Note that rooms with `invite` join rules are not
expected here, but rooms with `knock` rules are given their
near-public nature.
allOf:
- $ref: "public_rooms_chunk.yaml"
- type: object
properties:
# Override description of join_rule
join_rule:
type: string
description: |-
The room's join rule. When not present, the room is assumed to
be `public`. Note that rooms with `invite` join rules are not
expected here, but rooms with `knock` rules are given their
near-public nature.
example: "public"
next_batch:
type: string
description: |-

View file

@ -0,0 +1,194 @@
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Client-Server Space Hierarchy API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/rooms/{roomId}/hierarchy":
get:
x-addedInMatrixVersion: "1.2"
summary: Retrieve a portion of a space tree.
description: |-
Paginates over the space tree in a depth-first manner to locate child rooms of a given space.
Where a child room is unknown to the local server, federation is used to fill in the details.
The servers listed in the `via` array should be contacted to attempt to fill in missing rooms.
Only [`m.space.child`](#mspacechild) state events of the room are considered. Invalid child
rooms and parent events are not covered by this endpoint.
operationId: getSpaceHierarchy
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room ID of the space to get a hierarchy for.
required: true
x-example: "!space:example.org"
- in: query
type: boolean
name: suggested_only
description: |-
Optional (default `false`) flag to indicate whether or not the server should only consider
suggested rooms. Suggested rooms are annotated in their [`m.space.child`](#mspacechild) event
contents.
x-example: true
- in: query
type: number
name: limit
description: |-
Optional limit for the maximum number of rooms to include per response. Must be an integer
greater than zero.
Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.
x-example: 20
- in: query
type: number
name: max_depth
description: |-
Optional limit for how far to go into the space. Must be a non-negative integer.
When reached, no further child rooms will be returned.
Servers should apply a default value, and impose a maximum value to avoid resource exhaustion.
x-example: 5
- in: query
type: string
name: from
description: |-
A pagination token from a previous result. If specified, `max_depth` and `suggested_only` cannot
be changed from the first request.
x-example: "next_batch_token"
responses:
200:
description: |-
A portion of the space tree, starting at the provided room ID.
examples:
application/json: {
"rooms": [
{
"room_id": "!space:example.org",
"avatar_url": "mxc://example.org/abcdef",
"guest_can_join": false,
"name": "The First Space",
"topic": "No other spaces were created first, ever",
"world_readable": true,
"join_rule": "public",
"room_type": "m.space",
"num_joined_members": 42,
"aliases": ["#general:example.org"],
"canonical_alias": "#general:example.org",
"children_state": [
{
"type": "m.space.child",
"state_key": "!a:example.org",
"content": {
"via": ["example.org"]
},
"sender": "@alice:example.org",
"origin_server_ts": 1629413349153
}
]
}
],
"next_batch": "next_batch_token"
}
schema:
type: object
properties:
rooms:
type: array
description: |-
The rooms for the current page, with the current filters.
items:
allOf:
- $ref: "definitions/public_rooms_chunk.yaml"
- type: object
properties:
room_type:
type: string
description: |-
The `type` of room (from [`m.room.create`](/client-server-api/#mroomcreate)), if any.
children_state:
type: array
description: |-
The [`m.space.child`](#mspacechild) events of the space-room, represented
as [Stripped State Events](#stripped-state) with an added `origin_server_ts` key.
If the room is not a space-room, this should be empty.
items:
allOf:
- $ref: "../../event-schemas/schema/core-event-schema/stripped_state.yaml"
- type: object
properties:
origin_server_ts:
type: number
format: int64
description: The `origin_server_ts` for the event.
required: [origin_server_ts]
required: [room_type, children_state]
next_batch:
type: string
description: |-
A token to supply to `from` to keep paginating the responses. Not present when there are
no further results.
required: [rooms]
403:
description: |-
The user cannot view or peek on the room. A meaningful `errcode`
and description error text will be returned. Example reasons for rejection are:
- The room is not set up for peeking.
- The user has been banned from the room.
- The room does not exist.
examples:
application/json: {
"errcode": "M_FORBIDDEN",
"error": "You are not allowed to view this room."
}
schema:
"$ref": "definitions/errors/error.yaml"
400:
description: |-
The request was invalid in some way. A meaningful `errcode`
and description error text will be returned. Example reasons for rejection are:
- The `from` token is unknown to the server.
- `suggested_only` or `max_depth` changed during pagination.
examples:
application/json: {
"errcode": "M_INVALID_PARAM",
"error": "suggested_only and max_depth cannot change on paginated requests"
}
schema:
"$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- Spaces

View file

@ -47,7 +47,9 @@ paths:
The service records persistently all of the above information.
It also generates an email containing all of this data, sent to the
`address` parameter, notifying them of the invitation.
`address` parameter, notifying them of the invitation. The email should
reference the `inviter_name`, `room_name`, `room_avatar`, and `room_type`
(if present) from the request here.
Also, the generated ephemeral public key will be listed as valid on
requests to `/_matrix/identity/v2/pubkey/ephemeral/isvalid`.
@ -115,6 +117,12 @@ paths:
type: string
description: The Content URI for the avatar of the user ID initiating the invite.
example: "mxc://example.org/an0th3rM3dia"
room_type:
type: string
description: |-
The `type` from the `m.room.create` event's `content`. If the create event doesn't
have a specified `type`, this field is not included.
example: "m.space"
required: ["medium", "address", "room_id", "sender"]
responses:
200:

View file

@ -143,63 +143,20 @@ paths:
chunk:
title: "PublicRoomsChunks"
type: array
description: |-
A paginated chunk of public rooms.
description: A paginated chunk of public rooms.
items:
type: object
title: "PublicRoomsChunk"
required:
- room_id
- num_joined_members
- world_readable
- guest_can_join
properties:
aliases:
type: array
description: |-
Aliases of the room. May be empty.
items:
type: string
canonical_alias:
type: string
description: |-
The canonical alias of the room, if any.
name:
type: string
description: |-
The name of the room, if any.
num_joined_members:
type: integer
description: |-
The number of members joined to the room.
room_id:
type: string
description: |-
The ID of the room.
topic:
type: string
description: |-
The topic of the room, if any.
world_readable:
type: boolean
description: |-
Whether the room may be viewed by guest users without joining.
guest_can_join:
type: boolean
description: |-
Whether guest users may join the room and participate in it.
If they can, they will be subject to ordinary power level
rules like any other user.
avatar_url:
type: string
description: The URL for the room's avatar, if one is set.
join_rule:
type: string
description: |-
The room's join rule. When not present, the room is assumed to
be `public`. Note that rooms with `invite` join rules are not
expected here, but rooms with `knock` rules are given their
near-public nature.
allOf:
- $ref: "../client-server/definitions/public_rooms_chunk.yaml"
- type: object
properties:
# Override description of join_rule
join_rule:
type: string
description: |-
The room's join rule. When not present, the room is assumed to
be `public`. Note that rooms with `invite` join rules are not
expected here, but rooms with `knock` rules are given their
near-public nature.
next_batch:
type: string
description: |-

View file

@ -0,0 +1,207 @@
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Federation Space Hierarchy API"
version: "1.0.0"
host: localhost:8448
schemes:
- https
basePath: /_matrix/federation/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/hierarchy/{roomId}":
get:
x-addedInMatrixVersion: "1.2"
summary: Retrieve a portion of a space tree.
description: |-
Federation version of the Client-Server [`GET /hierarchy`](/client-server-api/#get_matrixclientv1roomsroomidhierarchy)
endpoint. Unlike the Client-Server API version, this endpoint does not paginate. Instead, all
the space-room's children the requesting server could feasibly peek/join are returned. The
requesting server is responsible for filtering the results further down for the user's request.
Only [`m.space.child`](#mspacechild) state events of the room are considered. Invalid child
rooms and parent events are not covered by this endpoint.
Responses to this endpoint should be cached for a period of time.
operationId: getSpaceHierarchy
security:
- signedRequest: []
parameters:
- in: path
type: string
name: roomId
description: The room ID of the space to get a hierarchy for.
required: true
x-example: "!space:example.org"
- in: query
type: boolean
name: suggested_only
description: |-
Optional (default `false`) flag to indicate whether or not the server should only consider
suggested rooms. Suggested rooms are annotated in their [`m.space.child`](#mspacechild) event
contents.
x-example: true
responses:
200:
description: |-
The space room and its children.
examples:
application/json: {
"room": {
"room_id": "!space:example.org",
"avatar_url": "mxc://example.org/abcdef",
"guest_can_join": false,
"name": "The First Space",
"topic": "No other spaces were created first, ever",
"world_readable": true,
"join_rule": "public",
"room_type": "m.space",
"num_joined_members": 42,
"aliases": ["#general:example.org"],
"canonical_alias": "#general:example.org",
"allowed_room_ids": [],
"children_state": [
{
"type": "m.space.child",
"state_key": "!a:example.org",
"content": {
"via": ["remote.example.org"]
},
"sender": "@alice:example.org",
"origin_server_ts": 1629413349153
}
]
},
"inaccessible_children": [
"!secret:example.org"
],
"children": [
{
"room_id": "!second_room:example.org",
"avatar_url": "mxc://example.org/abcdef2",
"guest_can_join": false,
"name": "The ~~First~~ Second Space",
"topic": "Hello world",
"world_readable": true,
"join_rule": "restricted",
"room_type": "m.space",
"num_joined_members": 42,
"aliases": ["#general:example.org"],
"canonical_alias": "#general:example.org",
"allowed_room_ids": [
"!upstream:example.org"
],
"children_state": [
{
"type": "m.space.child",
"state_key": "!b:example.org",
"content": {
"via": ["remote.example.org"]
},
"sender": "@alice:example.org",
"origin_server_ts": 1629422222222
}
]
}
]
}
schema:
type: object
properties:
room:
description: A summary of the room requested.
allOf:
- $ref: "../client-server/definitions/public_rooms_chunk.yaml"
- type: object
properties:
room_type:
type: string
description: |-
The `type` of room (from [`m.room.create`](/client-server-api/#mroomcreate)), if any.
allowed_room_ids:
type: array
items:
type: string
description: |-
If the room is a [restricted room](#restricted-rooms), these are the room IDs which
are specified by the join rules. Empty or omitted otherwise.
children_state:
type: array
description: |-
The [`m.space.child`](/client-server-api/#mspacechild) events of the space-room, represented
as [Stripped State Events](/client-server-api/#stripped-state) with an added
`origin_server_ts` key.
If the room is not a space-room, this should be empty.
items:
allOf:
- $ref: "../../event-schemas/schema/core-event-schema/stripped_state.yaml"
- type: object
properties:
origin_server_ts:
type: number
format: int64
description: The `origin_server_ts` for the event.
required: [origin_server_ts]
required: [room_type, allowed_room_ids, children_state]
children:
description: |-
A summary of the space's children. Rooms which the requesting server cannot peek/join will
be excluded.
allOf:
- $ref: "../client-server/definitions/public_rooms_chunk.yaml"
- type: object
properties:
room_type:
type: string
description: |-
The `type` of room (from [`m.room.create`](/client-server-api/#mroomcreate)), if any.
allowed_room_ids:
type: array
items:
type: string
description: |-
If the room is a [restricted room](#restricted-rooms), these are the room IDs which
are specified by the join rules. Empty or omitted otherwise.
required: [room_type, allowed_room_ids, children_state]
inaccessible_children:
type: array
items:
type: string
description: |-
The list of room IDs the requesting server doesn't have a viable way to peek/join. Rooms which
the responding server cannot provide details on will be outright excluded from the response instead.
Assuming both the requesting and responding server are well behaved, the requesting server should
consider these room IDs as not accessible from anywhere. They should not be re-requested.
required: [room, children, inaccessible_children]
404:
description: |-
The room is not known to the server or the requesting server is unable to peek/join
it (if it were to attempt this).
examples:
application/json: {
"errcode": "M_NOT_FOUND",
"error": "Room does not exist."
}
schema:
"$ref": "../client-server/definitions/errors/error.yaml"
tags:
- Spaces

View file

@ -0,0 +1,10 @@
{
"$ref": "core/state_event.json",
"type": "m.space.child",
"state_key": "!roomid:example.org",
"content": {
"suggested": true,
"via": ["example.org", "other.example.org"],
"order": "lexicographically_compare_me"
}
}

View file

@ -0,0 +1,9 @@
{
"$ref": "core/state_event.json",
"type": "m.space.parent",
"state_key": "!parent_roomid:example.org",
"content": {
"canonical": true,
"via": ["example.org", "other.example.org"]
}
}

View file

@ -14,6 +14,12 @@ properties:
room_version:
description: The version of the room. Defaults to `"1"` if the key does not exist.
type: string
type:
description: |-
Optional [room type](#types) to denote a room's intended function outside of traditional conversation.
Unspecified room types are possible using [Namespaced Identifiers](/appendices/#common-namespaced-identifier-grammar).
type: string
predecessor:
description: A reference to the room this room replaces, if the previous room was upgraded.
type: object

View file

@ -0,0 +1,46 @@
---
allOf:
- $ref: core-event-schema/state_event.yaml
description: Defines the relationship of a child room to a space-room. Has no effect in rooms which are not [spaces](#spaces).
properties:
content:
properties:
via:
type: array
description: |-
A list of servers to try and join through. See also: [Routing](/appendices/#routing).
When not present or invalid, the child room is not considered to be part of the space.
items:
type: string
order:
type: string
maxLength: 50
pattern: '^[\x20-\x7E]+$'
description: |-
Optional string to define ordering among space children. These are lexicographically
compared against other children's `order`, if present.
Must consist of ASCII characters within the range `\x20` (space) and `\x7E` (`~`),
inclusive. Must not exceed 50 characters.
`order` values with the wrong type, or otherwise invalid contents, are to be treated
as though the `order` key was not provided.
See [Ordering](/client-server-api/#ordering-1) for information on how the ordering works.
suggested:
type: boolean
description: |-
Optional (default `false`) flag to denote whether the child is "suggested" or of interest
to members of the space. This is primarily intended as a rendering hint for clients to
display the room differently, such as eagerly rendering them in the room list.
type: object
state_key:
description: The child room ID being described.
type: string
type:
enum:
- m.space.child
type: string
title: Space child room
type: object

View file

@ -0,0 +1,32 @@
---
allOf:
- $ref: core-event-schema/state_event.yaml
description: Defines the relationship of a room to a parent space-room.
properties:
content:
properties:
via:
type: array
description: |-
A list of servers to try and join through. See also: [Routing](/appendices/#routing).
When not present or invalid, the room is not considered to be part of the parent space.
items:
type: string
canonical:
type: boolean
description: |-
Optional (default `false`) flag to denote this parent is the primary parent for the room.
When multiple `canonical` parents are found, the lowest parent when ordering by room ID
lexicographically by Unicode code-points should be used.
type: object
state_key:
description: The parent room ID.
type: string
type:
enum:
- m.space.parent
type: string
title: Room space parent
type: object