diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 0666c579..7a6c6be2 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -98,8 +98,10 @@ the ``newsfragments`` directory. The ``type`` can be one of the following: * ``deprecation`` - Used when deprecating something -All news fragments must have a brief summary explaining the change in the contents -of the file. The summary must end in a full stop to be in line with the style guide. +All news fragments must have a brief summary explaining the change in the +contents of the file. The summary must end in a full stop to be in line with +the style guide and and formatting must be done using `Restructured Text +`_. Changes that do not change the spec, such as changes to the build script, formatting, CSS, etc should not get a news fragment. diff --git a/api/check_examples.py b/api/check_examples.py index 0fb275b1..94f3495e 100755 --- a/api/check_examples.py +++ b/api/check_examples.py @@ -75,6 +75,12 @@ def check_response(filepath, request, code, response): filepath, request, code )) check_schema(filepath, example, schema) + except jsonschema.SchemaError as error: + for suberror in sorted(error.context, key=lambda e: e.schema_path): + print(list(suberror.schema_path), suberror.message, sep=", ") + raise ValueError("Error validating JSON schema for %r %r" % ( + request, code + ), e) except Exception as e: raise ValueError("Error validating JSON schema for %r %r" % ( request, code diff --git a/api/client-server/account-data.yaml b/api/client-server/account-data.yaml index 76b2b156..ae845b25 100644 --- a/api/client-server/account-data.yaml +++ b/api/client-server/account-data.yaml @@ -43,8 +43,8 @@ paths: name: userId required: true description: |- - The id of the user to set account_data for. The access token must be - authorized to make requests for this user id. + The ID of the user to set account_data for. The access token must be + authorized to make requests for this user ID. x-example: "@alice:example.com" - in: path type: string @@ -69,6 +69,41 @@ paths: The account_data was successfully added. tags: - User data + get: + summary: Get some account_data for the user. + description: |- + Get some account_data for the client. This config is only visible to the user + that set the account_data. + operationId: getAccountData + security: + - accessToken: [] + parameters: + - in: path + type: string + name: userId + required: true + description: |- + The ID of the user to get account_data for. The access token must be + authorized to make requests for this user ID. + x-example: "@alice:example.com" + - in: path + type: string + name: type + required: true + description: |- + The event type of the account_data to get. Custom types should be + namespaced to avoid clashes. + x-example: "org.example.custom.config" + responses: + 200: + description: + The account data content for the given type. + schema: + type: object + example: { + "custom_account_data_key": "custom_config_value"} + tags: + - User data "/user/{userId}/rooms/{roomId}/account_data/{type}": put: summary: Set some account_data for the user. @@ -85,15 +120,15 @@ paths: name: userId required: true description: |- - The id of the user to set account_data for. The access token must be - authorized to make requests for this user id. + The ID of the user to set account_data for. The access token must be + authorized to make requests for this user ID. x-example: "@alice:example.com" - in: path type: string name: roomId required: true description: |- - The id of the room to set account_data on. + The ID of the room to set account_data on. x-example: "!726s6s6q:example.com" - in: path type: string @@ -118,3 +153,45 @@ paths: The account_data was successfully added. tags: - User data + get: + summary: Get some account_data for the user. + description: |- + Get some account_data for the client on a given room. This config is only + visible to the user that set the account_data. + operationId: getAccountDataPerRoom + security: + - accessToken: [] + parameters: + - in: path + type: string + name: userId + required: true + description: |- + The ID of the user to set account_data for. The access token must be + authorized to make requests for this user ID. + x-example: "@alice:example.com" + - in: path + type: string + name: roomId + required: true + description: |- + The ID of the room to get account_data for. + x-example: "!726s6s6q:example.com" + - in: path + type: string + name: type + required: true + description: |- + The event type of the account_data to get. Custom types should be + namespaced to avoid clashes. + x-example: "org.example.custom.room.config" + responses: + 200: + description: + The account data content for the given type. + schema: + type: object + example: { + "custom_account_data_key": "custom_config_value"} + tags: + - User data diff --git a/api/client-server/capabilities.yaml b/api/client-server/capabilities.yaml new file mode 100644 index 00000000..a50908f7 --- /dev/null +++ b/api/client-server/capabilities.yaml @@ -0,0 +1,112 @@ +# Copyright 2019 New Vector Ltd +# +# 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 Capabiltiies API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/%CLIENT_MAJOR_VERSION% +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/capabilities": + get: + summary: Gets information about the server's capabilities. + description: |- + Gets information about the server's supported feature set + and other relevant capabilities. + operationId: getCapabilities + security: + - accessToken: [] + parameters: [] + responses: + 200: + description: + The capabilities of the server. + examples: + application/json: { + "capabilities": { + "m.change_password": { + "enabled": false + }, + "m.room_versions": { + "default": "1", + "available": { + "1": "stable", + "2": "stable", + "3": "unstable", + "test-version": "unstable" + } + }, + "com.example.custom.ratelimit": { + "max_requests_per_hour": 600 + } + } + } + schema: + type: object + required: ["capabilities"] + properties: + capabilities: + type: object + title: Capabilities + description: |- + The custom capabilities the server supports, using the + Java package naming convention. + additionalProperties: + type: object + properties: + "m.change_password": + type: object + description: |- + Capability to indicate if the user can change their password. + title: ChangePasswordCapability + properties: + enabled: + type: boolean + description: |- + True if the user can change their password, false otherwise. + example: false + required: ['enabled'] + "m.room_versions": + type: object + description: The room versions the server supports. + title: RoomVersionsCapability + properties: + default: + type: string + description: |- + The default room version the server is using for new rooms. + example: "1" + available: + type: object + description: |- + A detailed description of the room versions the server supports. + additionalProperties: + type: string + title: RoomVersionStability + enum: [stable, unstable] + description: The stability of the room version. + required: ['default', 'available'] + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - Capabilities diff --git a/api/client-server/create_room.yaml b/api/client-server/create_room.yaml index d361d973..bce61aad 100644 --- a/api/client-server/create_room.yaml +++ b/api/client-server/create_room.yaml @@ -244,6 +244,12 @@ paths: invalid: for example, the user's ``power_level`` is set below that necessary to set the room name (``errcode`` set to ``M_INVALID_ROOM_STATE``). + + - The homeserver doesn't support the requested room version, or + one or more users being invited to the new room are residents + of a homeserver which does not support the requested room version. + The ``errcode`` will be ``M_UNSUPPORTED_ROOM_VERSION`` in these + cases. schema: "$ref": "definitions/errors/error.yaml" tags: diff --git a/api/client-server/definitions/wellknown/full.yaml b/api/client-server/definitions/wellknown/full.yaml new file mode 100644 index 00000000..8d8f4038 --- /dev/null +++ b/api/client-server/definitions/wellknown/full.yaml @@ -0,0 +1,39 @@ +# Copyright 2019 New Vector Ltd +# +# 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. +title: Discovery Information +description: |- + Used by clients to determine the homeserver, identity server, and other + optional components they should be interacting with. +type: object +properties: + "m.homeserver": + $ref: "homeserver.yaml" + "m.identity_server": + $ref: "identity_server.yaml" +additionalProperties: + type: object + description: Application-dependent keys using Java package naming convention. +required: + - m.homeserver +example: { + "m.homeserver": { + "base_url": "https://matrix.example.com" + }, + "m.identity_server": { + "base_url": "https://identity.example.com" + }, + "org.example.custom.property": { + "app_url": "https://custom.app.example.org" + } +} diff --git a/api/client-server/inviting.yaml b/api/client-server/inviting.yaml index f312d5ce..dfa66b9b 100644 --- a/api/client-server/inviting.yaml +++ b/api/client-server/inviting.yaml @@ -82,6 +82,20 @@ paths: } schema: type: object + 400: + description: |- + + The request is invalid. A meaningful ``errcode`` and description + error text will be returned. Example reasons for rejection include: + + - The request body is malformed (``errcode`` set to ``M_BAD_JSON`` + or ``M_NOT_JSON``). + + - One or more users being invited to the room are residents of a + homeserver which does not support the requested room version. The + ``errcode`` will be ``M_UNSUPPORTED_ROOM_VERSION`` in these cases. + schema: + "$ref": "definitions/errors/error.yaml" 403: description: |- You do not have permission to invite the user to the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are: diff --git a/api/client-server/login.yaml b/api/client-server/login.yaml index 43aae5df..98914a06 100644 --- a/api/client-server/login.yaml +++ b/api/client-server/login.yaml @@ -139,7 +139,15 @@ paths: application/json: { "user_id": "@cheeky_monkey:matrix.org", "access_token": "abc123", - "device_id": "GHTYAJCE" + "device_id": "GHTYAJCE", + "well_known": { + "m.homeserver": { + "base_url": "https://example.org" + }, + "m.identity_server": { + "base_url": "https://id.example.org" + } + } } schema: type: object @@ -166,6 +174,14 @@ paths: description: |- ID of the logged-in device. Will be the same as the corresponding parameter in the request, if one was specified. + well_known: + type: object + description: |- + Optional client configuration provided by the server. If present, + clients SHOULD use the provided object to reconfigure themselves, + optionally validating the URLs within. This object takes the same + form as the one returned from .well-known autodiscovery. + "$ref": "definitions/wellknown/full.yaml" 400: description: |- Part of the request was invalid. For example, the login type may not be recognised. diff --git a/api/client-server/presence.yaml b/api/client-server/presence.yaml index 91b75c6a..874ac59b 100644 --- a/api/client-server/presence.yaml +++ b/api/client-server/presence.yaml @@ -136,103 +136,3 @@ paths: "$ref": "definitions/errors/error.yaml" tags: - Presence - "/presence/list/{userId}": - post: - summary: Add or remove users from this presence list. - description: |- - Adds or removes users from this presence list. - operationId: modifyPresenceList - security: - - accessToken: [] - parameters: - - in: path - type: string - name: userId - description: The user whose presence list is being modified. - required: true - x-example: "@alice:example.com" - - in: body - name: presence_diff - description: The modifications to make to this presence list. - required: true - schema: - type: object - example: { - "invite": [ - "@bob:matrix.org" - ], - "drop": [ - "@alice:matrix.org" - ] - } - properties: - invite: - type: array - description: A list of user IDs to add to the list. - items: - type: string - description: A list of user IDs. - drop: - type: array - description: A list of user IDs to remove from the list. - items: - type: string - description: A list of user IDs. - responses: - 200: - description: The list was updated. - examples: - application/json: { - } - schema: - type: object # empty json object - 429: - description: This request was rate-limited. - schema: - "$ref": "definitions/errors/rate_limited.yaml" - tags: - - Presence - get: - summary: Get presence events for this presence list. - description: |- - Retrieve a list of presence events for every user on this list. - operationId: getPresenceForList - parameters: - - in: path - type: string - name: userId - description: The user whose presence list should be retrieved. - required: true - x-example: "@alice:example.com" - responses: - 200: - description: A list of presence events for this list. - examples: - application/json: [ - { - "content": { - "last_active_ago": 395, - "presence": "offline", - "user_id": "@alice:matrix.org" - }, - "type": "m.presence" - }, - { - "content": { - "last_active_ago": 16874, - "presence": "online", - "user_id": "@marisa:matrix.org", - "currently_active": true - }, - "type": "m.presence" - } - ] - schema: - type: array - items: - type: object - title: PresenceEvent - allOf: - - "$ref": "definitions/event-schemas/schema/core-event-schema/event.yaml" - tags: - - Presence diff --git a/api/client-server/room_upgrades.yaml b/api/client-server/room_upgrades.yaml new file mode 100644 index 00000000..55ee14dc --- /dev/null +++ b/api/client-server/room_upgrades.yaml @@ -0,0 +1,93 @@ +# Copyright 2019 New Vector Ltd +# +# 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 Room Upgrades API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/%CLIENT_MAJOR_VERSION% +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/rooms/{roomId}/upgrade": + post: + summary: Upgrades a room to a new room version. + description: |- + Upgrades the given room to a particular room version. + operationId: upgradeRoom + security: + - accessToken: [] + parameters: + - in: path + type: string + name: roomId + required: true + description: The ID of the room to upgrade. + x-example: "!oldroom:example.org" + - in: body + name: body + required: true + description: The request body + schema: + type: object + properties: + new_version: + type: string + description: The new version for the room. + example: {"new_version": "2"} + required: [new_version] + responses: + 200: + description: The room was successfully upgraded. + examples: + application/json: { + "replacement_room": "!newroom:example.org" + } + schema: + type: object + properties: + replacement_room: + type: string + description: The ID of the new room. + required: [replacement_room] + 400: + description: |- + The request was invalid. One way this can happen is if the room version + requested is not supported by the homeserver. + examples: + application/json: { + "errcode": "M_UNSUPPORTED_ROOM_VERSION", + "error": "This server does not support that room version" + } + schema: + "$ref": "definitions/errors/error.yaml" + 403: + description: |- + The user is not permitted to upgrade the room. + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "You cannot upgrade this room" + } + schema: + "$ref": "definitions/errors/error.yaml" + tags: + - Room ugprades diff --git a/api/client-server/versions.yaml b/api/client-server/versions.yaml index d3aa0359..2464af1d 100644 --- a/api/client-server/versions.yaml +++ b/api/client-server/versions.yaml @@ -33,13 +33,29 @@ paths: Only the latest ``Z`` value will be reported for each supported ``X.Y`` value. i.e. if the server implements ``r0.0.0``, ``r0.0.1``, and ``r1.2.0``, it will report ``r0.0.1`` and ``r1.2.0``. + + The server may additionally advertise experimental features it supports + through ``unstable_features``. These features should be namespaced and + may optionally include version information within their name if desired. + Features listed here are not for optionally toggling parts of the Matrix + specification and should only be used to advertise support for a feature + which has not yet landed in the spec. For example, a feature currently + undergoing the proposal process may appear here and eventually be taken + off this list once the feature lands in the spec and the server deems it + reasonable to do so. Servers may wish to keep advertising features here + after they've been released into the spec to give clients a chance to + upgrade appropriately. Additionally, clients should avoid using unstable + features in their stable releases. operationId: getVersions responses: 200: description: The versions supported by the server. examples: application/json: { - "versions": ["r0.0.1"] + "versions": ["r0.0.1"], + "unstable_features": { + "org.example.my_feature": true + } } schema: type: object @@ -50,5 +66,15 @@ paths: items: type: string description: The supported versions + unstable_features: + type: object + description: |- + Experimental features the server supports. Features not listed here, + or the lack of this property all together, indicate that a feature is + not supported. + additionalProperties: + type: boolean + description: Whether or not the namespaced feature is supported. + required: ['versions'] tags: - Server administration diff --git a/api/client-server/wellknown.yaml b/api/client-server/wellknown.yaml index 24e190f9..b63bd041 100644 --- a/api/client-server/wellknown.yaml +++ b/api/client-server/wellknown.yaml @@ -38,28 +38,9 @@ paths: responses: 200: description: Server discovery information. - examples: - application/json: { - "m.homeserver": { - "base_url": "https://matrix.example.com" - }, - "m.identity_server": { - "base_url": "https://identity.example.com" - } - } schema: type: object - properties: - m.homeserver: - description: Information about the homeserver to connect to. - "$ref": "definitions/wellknown/homeserver.yaml" - m.identity_server: - description: Optional. Information about the identity server to connect to. - "$ref": "definitions/wellknown/identity_server.yaml" - additionalProperties: - description: Application-dependent keys using Java package naming convention. - required: - - m.homeserver + "$ref": "definitions/wellknown/full.yaml" 404: description: No server discovery information available. tags: diff --git a/api/server-server/backfill.yaml b/api/server-server/backfill.yaml index b196d17c..0da0e234 100644 --- a/api/server-server/backfill.yaml +++ b/api/server-server/backfill.yaml @@ -65,22 +65,6 @@ paths: event(s), up to the given limit. schema: $ref: "definitions/transaction.yaml" - # Override the example to show the response of the request a bit better - examples: - application/json: { - "$ref": "examples/transaction.json", - "pdus": [ - { - "$ref": "pdu.json", - "room_id": "!SomeRoom:matrix.org", - "event_id": "$abc123:matrix.org" - }, - { - "$ref": "pdu.json", - "room_id": "!SomeRoom:matrix.org" - }, - ] - } "/get_missing_events/{roomId}": post: summary: Retrieves events that the sender is missing @@ -114,14 +98,14 @@ paths: earliest_events: type: array description: |- - The latest events that the sender already has. These are skipped when retrieving + The latest event IDs that the sender already has. These are skipped when retrieving the previous events of ``latest_events``. items: type: string example: ["$missing_event:example.org"] latest_events: type: array - description: The events to retrieve the previous events for. + description: The event IDs to retrieve the previous events for. items: type: string example: ["$event_that_has_the_missing_event_as_a_previous_event:example.org"] @@ -136,13 +120,16 @@ paths: properties: events: type: array - description: The missing events. + description: |- + The missing events. The event format varies depending on the room version - check + the `room version specification`_ for precise event formats. items: - $ref: definitions/pdu.yaml + type: object + title: PDU required: ['events'] examples: application/json: { "events": [ - {"$ref": "examples/pdu.json"} + {"$ref": "examples/minimal_pdu.json"} ] } diff --git a/api/server-server/definitions/event-schemas/m.device_list_update.yaml b/api/server-server/definitions/event-schemas/m.device_list_update.yaml index f11e957e..d511bfae 100644 --- a/api/server-server/definitions/event-schemas/m.device_list_update.yaml +++ b/api/server-server/definitions/event-schemas/m.device_list_update.yaml @@ -21,7 +21,7 @@ description: |- # FIXME: It's very unclear why we have this API surface for synchronising # device lists, rather than just using a room (which could also be used for -# presence lists, profile info, etc). But documenting what we have for now... +# profile info, etc). But documenting what we have for now... allOf: - $ref: ../edu.yaml diff --git a/api/server-server/definitions/event-schemas/m.presence_accept.yaml b/api/server-server/definitions/event-schemas/m.presence_accept.yaml deleted file mode 100644 index 3ba78b47..00000000 --- a/api/server-server/definitions/event-schemas/m.presence_accept.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2018 New Vector Ltd -# -# 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: m.presence_accept -description: |- - An EDU representing approval for the observing user to subscribe to the - presence of the the observed user. -allOf: - - $ref: ../edu.yaml - - type: object - properties: - edu_type: - type: enum - enum: ['m.presence_accept'] - description: The string ``m.presence_accept`` - example: "m.presence_accept" - content: - type: object - description: The invite information. - title: Invite Information - properties: - observed_user: - type: string - description: |- - The user ID that has approved the ``observer_user`` to - subscribe to their presence. - example: "@alice:elsewhere.com" - observer_user: - type: string - description: |- - The user ID that requested to subscribe to the presence of - the ``observed_user``. - example: "@john:matrix.org" - required: ['observer_user', 'observed_user'] diff --git a/api/server-server/definitions/event-schemas/m.presence_deny.yaml b/api/server-server/definitions/event-schemas/m.presence_deny.yaml deleted file mode 100644 index 811c296a..00000000 --- a/api/server-server/definitions/event-schemas/m.presence_deny.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2018 New Vector Ltd -# -# 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: m.presence_deny -description: |- - An EDU representing a declination or revocation for the observing user - to subscribe to the presence of the observed user. -example: { - "origin": "example.org", - "destination": "elsewhere.org", - "edu_type": "m.presence_deny", - "content": { - "observed_user": "@alice:elsewhere.org", - "observer_user": "@john:example.org" - } -} -allOf: - - $ref: ../edu.yaml - - type: object - properties: - edu_type: - type: enum - enum: ['m.presence_deny'] - description: The string ``m.presence_deny`` - example: "m.presence_deny" - content: - type: object - description: The invite information. - title: Invite Information - properties: - observed_user: - type: string - description: |- - The user ID that has declined or revoked the ``observer_user`` from - subscribing to their presence. - example: "@alice:elsewhere.com" - observer_user: - type: string - description: |- - The user ID that requested to subscribe to the presence of - the ``observed_user``. - example: "@john:matrix.org" - required: ['observer_user', 'observed_user'] diff --git a/api/server-server/definitions/event-schemas/m.presence_invite.yaml b/api/server-server/definitions/event-schemas/m.presence_invite.yaml deleted file mode 100644 index a584897b..00000000 --- a/api/server-server/definitions/event-schemas/m.presence_invite.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2018 New Vector Ltd -# -# 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: m.presence_invite -description: |- - An EDU representing a request to subscribe to a user's presence. -allOf: - - $ref: ../edu.yaml - - type: object - properties: - edu_type: - type: enum - enum: ['m.presence_invite'] - description: The string ``m.presence_invite`` - example: "m.presence_invite" - content: - type: object - description: The invite information. - title: Invite Information - properties: - observed_user: - type: string - description: |- - The user ID the ``observer_user`` would like to subscribe - to the presence of. - example: "@alice:elsewhere.com" - observer_user: - type: string - description: |- - The user ID that is wishing to subscribe to the presence of - the ``observed_user``. - example: "@john:matrix.org" - required: ['observer_user', 'observed_user'] diff --git a/api/server-server/definitions/invite_event.yaml b/api/server-server/definitions/invite_event.yaml index d196339a..674ef2c9 100644 --- a/api/server-server/definitions/invite_event.yaml +++ b/api/server-server/definitions/invite_event.yaml @@ -1,4 +1,4 @@ -# Copyright 2018 New Vector Ltd +# Copyright 2018-2019 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,15 +13,14 @@ # limitations under the License. type: object title: Invite Event -description: An invite event +description: |- + An invite event. Note that events have a different format depending on the + room version - check the `room version specification`_ for precise event formats. allOf: - - $ref: "pdu.yaml" - type: object properties: - # Note: we override a bunch of parameters to change their descriptions sender: type: string - # TODO: Verify/clarify this - it doesn't seem right, given this is a 'regular' invite description: |- The matrix ID of the user who sent the original ``m.room.third_party_invite``. example: "@someone:example.org" @@ -46,7 +45,7 @@ allOf: type: object title: Membership Event Content description: |- - The content of the event, matching what is available in the + The content of the event, matching what is available in the `Client-Server API`_. Must include a ``membership`` of ``invite``. example: {"membership": "invite"} properties: @@ -55,33 +54,10 @@ allOf: description: The value ``invite``. example: "invite" required: ['membership'] - auth_events: - type: array - description: |- - An event reference list containing the authorization events that would - allow the member to be invited to the room. - items: - type: array - maxItems: 2 - minItems: 2 - items: - - type: string - title: Event ID - example: "$abc123:matrix.org" - - type: object - title: Event Hash - example: { - "sha256": "abase64encodedsha256hashshouldbe43byteslong" - } - properties: - sha256: - type: string - description: The event hash. - example: abase64encodedsha256hashshouldbe43byteslong - required: ['sha256'] - redacts: - type: string - description: Not used. required: - # Every other field is already flagged as required by the $ref - state_key + - sender + - origin + - origin_server_ts + - type + - content diff --git a/api/server-server/definitions/keys.yaml b/api/server-server/definitions/keys.yaml index 738e9e46..06619641 100644 --- a/api/server-server/definitions/keys.yaml +++ b/api/server-server/definitions/keys.yaml @@ -25,9 +25,9 @@ properties: verify_keys: type: object description: |- - Public keys of the homeserver for verifying digital signatures. - - The object's key is the algorithm and version combined (``ed25519`` being the + Public keys of the homeserver for verifying digital signatures. + + The object's key is the algorithm and version combined (``ed25519`` being the algorithm and ``abc123`` being the version in the example below). Together, this forms the Key ID. The version must have characters matching the regular expression ``[a-zA-Z0-9_]``. @@ -49,9 +49,9 @@ properties: old_verify_keys: type: object description: |- - The public keys that the server used to use and when it stopped using them. - - The object's key is the algorithm and version combined (``ed25519`` being the + The public keys that the server used to use and when it stopped using them. + + The object's key is the algorithm and version combined (``ed25519`` being the algorithm and ``0ldK3y`` being the version in the example below). Together, this forms the Key ID. The version must have characters matching the regular expression ``[a-zA-Z0-9_]``. @@ -90,17 +90,6 @@ properties: additionalProperties: type: string name: Encoded Signature Verification Key - tls_fingerprints: - type: array - description: Hashes of X.509 TLS certificates used by this server. - items: - type: object - title: TLS Fingerprint - properties: - sha256: - type: string - description: The `Unpadded Base64`_ encoded fingerprint. - example: "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw" valid_until_ts: type: integer format: int64 diff --git a/api/server-server/definitions/pdu.yaml b/api/server-server/definitions/pdu.yaml index d86b8538..87064c22 100644 --- a/api/server-server/definitions/pdu.yaml +++ b/api/server-server/definitions/pdu.yaml @@ -13,7 +13,7 @@ # limitations under the License. type: object title: Persistent Data Unit -description: A persistent data unit (event) +description: A persistent data unit (event) for room versions 1 and 2. example: $ref: "../examples/pdu.json" allOf: @@ -26,13 +26,13 @@ allOf: description: |- Content hashes of the PDU, following the algorithm specified in `Signing Events`_. example: { - "sha256": "thishashcoversallfieldsincasethisisredacted" + "sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted" } properties: sha256: type: string description: The hash. - example: thishashcoversallfieldsincasthisisredacted + example: ThisHashCoversAllFieldsInCaseThisIsRedacted required: ['sha256'] signatures: type: object @@ -40,7 +40,7 @@ allOf: Signatures for the PDU, following the algorithm specified in `Signing Events`_. example: { "example.com": { - "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus" + "ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent" } } additionalProperties: diff --git a/api/server-server/definitions/pdu_v3.yaml b/api/server-server/definitions/pdu_v3.yaml new file mode 100644 index 00000000..8d41fbda --- /dev/null +++ b/api/server-server/definitions/pdu_v3.yaml @@ -0,0 +1,73 @@ +# Copyright 2019 New Vector Ltd +# +# 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: Persistent Data Unit +description: A persistent data unit (event) for room version 3 and beyond. +example: + $ref: "../examples/pdu_v3.json" +allOf: + - $ref: "unsigned_pdu_base.yaml" + - type: object + properties: + auth_events: + type: array + items: + type: string + description: Event ID. + description: |- + Event IDs for the authorization events that would + allow this event to be in the room. + example: ["$base64EncodedHash", "$AnotherEvent"] + prev_events: + type: array + items: + type: string + description: Event ID. + description: |- + Event IDs for the most recent events in the room + that the homeserver was aware of when it made this event. + example: ["$base64EncodedHash", "$AnotherEvent"] + hashes: + type: object + title: Event Hash + description: |- + Content hashes of the PDU, following the algorithm specified in `Signing Events`_. + example: { + "sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted" + } + properties: + sha256: + type: string + description: The hash. + example: ThisHashCoversAllFieldsInCaseThisIsRedacted + required: ['sha256'] + signatures: + type: object + description: |- + Signatures for the PDU, following the algorithm specified in `Signing Events`_. + example: { + "example.com": { + "ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent" + } + } + additionalProperties: + type: object + title: Server Signatures + additionalProperties: + type: string + required: + - auth_events + - prev_events + - hashes + - signatures diff --git a/api/server-server/definitions/transaction.yaml b/api/server-server/definitions/transaction.yaml index 9833f785..08f3738a 100644 --- a/api/server-server/definitions/transaction.yaml +++ b/api/server-server/definitions/transaction.yaml @@ -26,12 +26,22 @@ properties: type: integer format: int64 description: |- - POSIX timestamp in milliseconds on originating homeserver when this + POSIX timestamp in milliseconds on originating homeserver when this transaction started. example: 1532991320875 pdus: type: array - description: List of persistent updates to rooms. Must not include more than 50 PDUs. + description: |- + List of persistent updates to rooms. Must not include more than 50 PDUs. Note that + events have a different format depending on the room version - check the + `room version specification`_ for precise event formats. items: - $ref: "pdu.yaml" + type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ contained in the transaction. The event format varies depending + on the room version - check the `room version specification`_ for precise event formats. + properties: [] + example: + $ref: "../examples/minimal_pdu.json" required: ['origin', 'origin_server_ts', 'pdus'] diff --git a/api/server-server/definitions/unsigned_pdu.yaml b/api/server-server/definitions/unsigned_pdu.yaml index c5ad801d..a27a21cd 100644 --- a/api/server-server/definitions/unsigned_pdu.yaml +++ b/api/server-server/definitions/unsigned_pdu.yaml @@ -1,4 +1,4 @@ -# Copyright 2018 New Vector Ltd +# Copyright 2018-2019 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,140 +16,13 @@ title: Unsigned Persistent Data Unit description: An unsigned persistent data unit (event) example: $ref: "../examples/unsigned_pdu.json" -properties: - event_id: - type: string - description: The event ID for the PDU. - example: "$a4ecee13e2accdadf56c1025:example.com" - room_id: - type: string - description: Room identifier. - example: "!abc123:matrix.org" - sender: - type: string - description: The ID of the user sending the event. - example: "@someone:matrix.org" - origin: - type: string - description: The ``server_name`` of the homeserver that created this event. - example: "matrix.org" - origin_server_ts: - type: integer - format: int64 - description: Timestamp in milliseconds on origin homeserver when this event was created. - example: 1234567890 - type: - type: string - description: Event type - example: "m.room.message" - state_key: - type: string - description: |- - If this key is present, the event is a state event, and it will replace previous events - with the same ``type`` and ``state_key`` in the room state. - example: "my_key" - content: - type: object - description: The content of the event. - example: {"key": "value"} - prev_events: - type: array - description: |- - Event IDs and reference hashes for the most recent events in the room - that the homeserver was aware of when it made this event. - items: - type: array - maxItems: 2 - minItems: 2 - items: - - type: string - title: Event ID - example: "$abc123:matrix.org" - - type: object - title: Event Hash - example: { - "sha256": "abase64encodedsha256hashshouldbe43byteslong" - } - properties: - sha256: - type: string - description: The event hash. - example: abase64encodedsha256hashshouldbe43byteslong - required: ['sha256'] - depth: - type: integer - description: |- - The maximum depth of the ``prev_events``, plus one. Must be less than the - maximum value for an integer (2^63 - 1). If the room's depth is already at - the limit, the depth must be set to the limit. - example: 12 - auth_events: - type: array - description: |- - Event IDs and reference hashes for the authorization events that would - allow this event to be in the room. - items: - type: array - maxItems: 2 - minItems: 2 - items: - - type: string - title: Event ID - example: "$abc123:matrix.org" - - type: object - title: Event Hash - example: { - "sha256": "abase64encodedsha256hashshouldbe43byteslong" - } - properties: - sha256: - type: string - description: The event hash. - example: abase64encodedsha256hashshouldbe43byteslong - required: ['sha256'] - redacts: - type: string - description: For redaction events, the ID of the event being redacted. - example: "$def456:matrix.org" - unsigned: - type: object - title: Example Unsigned Data - description: |- - Additional data added by the origin server but not covered by the ``signatures``. More - keys than those defined here may be used. - example: {"key": "value"} +allOf: + - $ref: "unsigned_pdu_base.yaml" + - type: object properties: - age: - type: integer - description: The number of milliseconds that have passed since this message was sent. - example: 4612 - replaces_state: + event_id: type: string - description: The event ID of the state event this event replaces. - example: "$state_event:example.org" - prev_sender: - type: string - description: The sender of the replaced state event. - example: "@someone:example.org" - prev_content: - type: object - description: The content of the replaced state event. - example: { - "membership": "join", - "displayname": "Bob" - } - redacted_because: - type: string - description: A reason for why the event was redacted. - example: "Inappropriate content" -required: - - event_id - - room_id - - sender - - origin - - origin_server_ts - - type - - content - - prev_events - - depth - - auth_events + description: The event ID for the PDU. + example: "$a4ecee13e2accdadf56c1025:example.com" + required: + - event_id diff --git a/api/server-server/definitions/unsigned_pdu_base.yaml b/api/server-server/definitions/unsigned_pdu_base.yaml new file mode 100644 index 00000000..283e4fed --- /dev/null +++ b/api/server-server/definitions/unsigned_pdu_base.yaml @@ -0,0 +1,151 @@ +# Copyright 2018 New Vector Ltd +# +# 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: Unsigned Persistent Data Unit +description: An unsigned persistent data unit (event) +example: + $ref: "../examples/unsigned_pdu_base.json" +properties: + room_id: + type: string + description: Room identifier. + example: "!abc123:matrix.org" + sender: + type: string + description: The ID of the user sending the event. + example: "@someone:matrix.org" + origin: + type: string + description: The ``server_name`` of the homeserver that created this event. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: Timestamp in milliseconds on origin homeserver when this event was created. + example: 1234567890 + type: + type: string + description: Event type + example: "m.room.message" + state_key: + type: string + description: |- + If this key is present, the event is a state event, and it will replace previous events + with the same ``type`` and ``state_key`` in the room state. + example: "my_key" + content: + type: object + description: The content of the event. + example: {"key": "value"} + prev_events: + type: array + description: |- + Event IDs and reference hashes for the most recent events in the room + that the homeserver was aware of when it made this event. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "Base64EncodedSha256HashesShouldBe43BytesLong" + } + properties: + sha256: + type: string + description: The event hash. + example: Base64EncodedSha256HashesShouldBe43BytesLong + required: ['sha256'] + depth: + type: integer + description: |- + The maximum depth of the ``prev_events``, plus one. Must be less than the + maximum value for an integer (2^63 - 1). If the room's depth is already at + the limit, the depth must be set to the limit. + example: 12 + auth_events: + type: array + description: |- + Event IDs and reference hashes for the authorization events that would + allow this event to be in the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "Base64EncodedSha256HashesShouldBe43BytesLong" + } + properties: + sha256: + type: string + description: The event hash. + example: Base64EncodedSha256HashesShouldBe43BytesLong + required: ['sha256'] + redacts: + type: string + description: For redaction events, the ID of the event being redacted. + example: "$def456:matrix.org" + unsigned: + type: object + title: Example Unsigned Data + description: |- + Additional data added by the origin server but not covered by the ``signatures``. More + keys than those defined here may be used. + example: {"key": "value"} + properties: + age: + type: integer + description: The number of milliseconds that have passed since this message was sent. + example: 4612 + replaces_state: + type: string + description: The event ID of the state event this event replaces. + example: "$state_event:example.org" + prev_sender: + type: string + description: The sender of the replaced state event. + example: "@someone:example.org" + prev_content: + type: object + description: The content of the replaced state event. + example: { + "membership": "join", + "displayname": "Bob" + } + redacted_because: + type: string + description: A reason for why the event was redacted. + example: "Inappropriate content" +required: + - event_id + - room_id + - sender + - origin + - origin_server_ts + - type + - content + - prev_events + - depth + - auth_events diff --git a/api/server-server/event_auth.yaml b/api/server-server/event_auth.yaml index 905b112f..0db0d401 100644 --- a/api/server-server/event_auth.yaml +++ b/api/server-server/event_auth.yaml @@ -20,7 +20,7 @@ host: localhost:8448 schemes: - https basePath: /_matrix/federation/v1 -consumes: +consumes: - application/json produces: - application/json @@ -58,10 +58,19 @@ paths: type: array description: |- The full set of authorization events that make up the state of - the room, and their authorization events, recursively. + the room, and their authorization events, recursively. Note that + events have a different format depending on the room version - + check the `room version specification`_ for precise event formats. items: - $ref: "definitions/pdu.yaml" - example: [{"$ref": "examples/pdu.json"}] + type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ contained in the auth chain. The event format + varies depending on the room version - check the `room version specification`_ + for precise event formats. + properties: [] + example: + $ref: "examples/minimal_pdu.json" required: ['auth_chain'] "/query_auth/{roomId}/{eventId}": post: @@ -98,10 +107,20 @@ paths: properties: auth_chain: type: array - description: The auth chain (the "remote auth"). + description: |- + The auth chain (the "remote auth"). Note that events have a different + format depending on the room version - check the `room version specification`_ + for precise event formats. items: - $ref: "definitions/pdu.yaml" - example: [{"$ref": "examples/pdu.json"}] + type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ contained in the auth chain. The event format + varies depending on the room version - check the `room version specification`_ + for precise event formats. + properties: [] + example: + $ref: "examples/minimal_pdu.json" missing: type: array description: |- @@ -142,14 +161,23 @@ paths: type: array description: |- The auth chain the receiver has, and used to determine the auth - chain differences (the "local auth"). + chain differences (the "local auth"). Note that events have a different + format depending on the room version - check the `room version specification`_ + for precise event formats. items: - $ref: "definitions/pdu.yaml" - example: [{"$ref": "examples/pdu.json"}] + type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ contained in the auth chain. The event format + varies depending on the room version - check the `room version specification`_ + for precise event formats. + properties: [] + example: + $ref: "examples/minimal_pdu.json" missing: type: array description: |- - The list of event IDs that the receiver believes it is missing, + The list of event IDs that the receiver believes it is missing, after comparing the "remote auth" and "local auth" chains. items: type: string diff --git a/api/server-server/events.yaml b/api/server-server/events.yaml index d23456d1..1f1a802d 100644 --- a/api/server-server/events.yaml +++ b/api/server-server/events.yaml @@ -59,17 +59,35 @@ paths: type: array description: |- The full set of authorization events that make up the state - of the room, and their authorization events, recursively. + of the room, and their authorization events, recursively. Note that + events have a different format depending on the room version - + check the `room version specification`_ for precise event formats. items: - $ref: "definitions/pdu.yaml" - example: [{"$ref": "examples/pdu.json"}] + type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ contained in the auth chain. The event format + varies depending on the room version - check the `room version specification`_ + for precise event formats. + properties: [] + example: + $ref: "examples/minimal_pdu.json" pdus: type: array description: |- - The fully resolved state of the room at the given event. + The fully resolved state of the room at the given event. Note that + events have a different format depending on the room version - + check the `room version specification`_ for precise event formats. items: - $ref: "definitions/pdu.yaml" - example: [{"$ref": "examples/pdu.json"}] + type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ for the fully resolved state of the room. The event format + varies depending on the room version - check the `room version specification`_ + for precise event formats. + properties: [] + example: + $ref: "examples/minimal_pdu.json" required: ['auth_chain', 'pdus'] "/state_ids/{roomId}": get: diff --git a/api/server-server/examples/minimal_pdu.json b/api/server-server/examples/minimal_pdu.json new file mode 100644 index 00000000..f8b8efc3 --- /dev/null +++ b/api/server-server/examples/minimal_pdu.json @@ -0,0 +1,7 @@ +{ + "type": "m.room.minimal_pdu", + "room_id": "!somewhere:example.org", + "content": { + "see_room_version_spec": "The event format changes depending on the room version." + } +} diff --git a/api/server-server/examples/pdu_v3.json b/api/server-server/examples/pdu_v3.json new file mode 100644 index 00000000..6a454b4e --- /dev/null +++ b/api/server-server/examples/pdu_v3.json @@ -0,0 +1,19 @@ +{ + "$ref": "unsigned_pdu_base.json", + "hashes": { + "sha256": "thishashcoversallfieldsincasethisisredacted" + }, + "signatures": { + "example.com": { + "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus" + } + }, + "auth_events": [ + "$base64encodedeventid", + "$adifferenteventid" + ], + "prev_events": [ + "$base64encodedeventid", + "$adifferenteventid" + ] +} diff --git a/api/server-server/examples/server_key.json b/api/server-server/examples/server_key.json index bebd8445..ffdfcb5a 100644 --- a/api/server-server/examples/server_key.json +++ b/api/server-server/examples/server_key.json @@ -16,8 +16,5 @@ "ed25519:auto2": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU" } }, - "tls_fingerprints": [{ - "sha256": "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw" - }], "valid_until_ts": 1652262000000 -} \ No newline at end of file +} diff --git a/api/server-server/examples/transaction.json b/api/server-server/examples/transaction.json index bd8ac3dc..bbc661d9 100644 --- a/api/server-server/examples/transaction.json +++ b/api/server-server/examples/transaction.json @@ -1,5 +1,7 @@ { "origin": "matrix.org", "origin_server_ts": 1234567890, - "pdus": [{"$ref": "pdu.json"}] -} \ No newline at end of file + "pdus": [{ + "$ref": "minimal_pdu.json" + }] +} diff --git a/api/server-server/examples/unsigned_pdu.json b/api/server-server/examples/unsigned_pdu.json index f4d2e749..b886a365 100644 --- a/api/server-server/examples/unsigned_pdu.json +++ b/api/server-server/examples/unsigned_pdu.json @@ -1,27 +1,4 @@ { - "room_id": "!UcYsUzyxTGDxLBEvLy:example.org", - "sender": "@alice:example.com", - "origin": "example.com", - "event_id": "$a4ecee13e2accdadf56c1025:example.com", - "origin_server_ts": 1404838188000, - "depth": 12, - "auth_events": [ - [ - "$af232176:example.org", - {"sha256": "abase64encodedsha256hashshouldbe43byteslong"} - ] - ], - "type": "m.room.message", - "prev_events": [ - [ - "$af232176:example.org", - {"sha256": "abase64encodedsha256hashshouldbe43byteslong"} - ] - ], - "content": { - "key": "value" - }, - "unsigned": { - "age": 4612 - } -} \ No newline at end of file + "$ref": "unsigned_pdu_base.json", + "event_id": "$a4ecee13e2accdadf56c1025:example.com" +} diff --git a/api/server-server/examples/unsigned_pdu_base.json b/api/server-server/examples/unsigned_pdu_base.json new file mode 100644 index 00000000..4826ccef --- /dev/null +++ b/api/server-server/examples/unsigned_pdu_base.json @@ -0,0 +1,26 @@ +{ + "room_id": "!UcYsUzyxTGDxLBEvLy:example.org", + "sender": "@alice:example.com", + "origin": "example.com", + "origin_server_ts": 1404838188000, + "depth": 12, + "auth_events": [ + [ + "$af232176:example.org", + {"sha256": "abase64encodedsha256hashshouldbe43byteslong"} + ] + ], + "type": "m.room.message", + "prev_events": [ + [ + "$af232176:example.org", + {"sha256": "abase64encodedsha256hashshouldbe43byteslong"} + ] + ], + "content": { + "key": "value" + }, + "unsigned": { + "age": 4612 + } +} diff --git a/api/server-server/invites.yaml b/api/server-server/invites-v1.yaml similarity index 86% rename from api/server-server/invites.yaml rename to api/server-server/invites-v1.yaml index 6d905e17..2ad0f220 100644 --- a/api/server-server/invites.yaml +++ b/api/server-server/invites-v1.yaml @@ -1,4 +1,4 @@ -# Copyright 2018 New Vector Ltd +# Copyright 2018-2019 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ host: localhost:8448 schemes: - https basePath: /_matrix/federation/v1 -consumes: +consumes: - application/json produces: - application/json @@ -32,9 +32,18 @@ paths: summary: Invites a remote user to a room description: |- Invites a remote user to a room. Once the event has been signed by both the inviting - homeserver and the invited homeserver, it can be sent to all of the servers in the + homeserver and the invited homeserver, it can be sent to all of the servers in the room by the inviting homeserver. - operationId: sendInvite + + Servers should prefer to use the v2 API for invites instead of the v1 API. Servers + which receive a v1 invite request must assume that the room version is either ``"1"`` + or ``"2"``. + + Note that events have a different format depending on the room version - check the + `room version specification`_ for precise event formats. **The request and response + bodies here describe the common event fields in more detail and may be missing other + required fields for a PDU.** + operationId: sendInviteV1 security: - signedRequest: [] parameters: @@ -103,9 +112,12 @@ paths: } ] example: { - "$ref": "examples/pdu.json", + "$ref": "examples/minimal_pdu.json", "type": "m.room.member", "state_key": "@joe:elsewhere.com", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", "unsigned": { "invite_room_state": [ { @@ -139,7 +151,8 @@ paths: 200: description: |- The event with the invited server's signature added. All other fields of the events - should remain untouched. + should remain untouched. Note that events have a different format depending on the + room version - check the `room version specification`_ for precise event formats. schema: type: array minItems: 2 @@ -160,9 +173,12 @@ paths: 200, { "event": { - "$ref": "examples/pdu.json", + "$ref": "examples/minimal_pdu.json", "type": "m.room.member", "state_key": "@someone:example.org", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", "unsigned": { "invite_room_state": [ { diff --git a/api/server-server/invites-v2.yaml b/api/server-server/invites-v2.yaml new file mode 100644 index 00000000..c459a848 --- /dev/null +++ b/api/server-server/invites-v2.yaml @@ -0,0 +1,246 @@ +# Copyright 2018-2019 New Vector Ltd +# +# 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 Invite User To Room API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/invite/{roomId}/{eventId}": + put: + summary: Invites a remote user to a room + description: |- + .. Note:: + This API is nearly identical to the v1 API with the exception of the request + body being different, and the response format fixed. + + Invites a remote user to a room. Once the event has been signed by both the inviting + homeserver and the invited homeserver, it can be sent to all of the servers in the + room by the inviting homeserver. + + This endpoint is preferred over the v1 API as it is more useful for servers. Senders + which receive a 400 or 404 response to this endpoint should retry using the v1 + API as the server may be older, if the room version is "1" or "2". + + Note that events have a different format depending on the room version - check the + `room version specification`_ for precise event formats. **The request and response + bodies here describe the common event fields in more detail and may be missing other + required fields for a PDU.** + operationId: sendInviteV2 + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID that the user is being invited to. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID for the invite event, generated by the inviting server. + required: true + x-example: "$abc123:example.org" + - in: body + name: body + type: object + required: true + schema: + type: object + properties: + room_version: + type: string + description: The version of the room where the user is being invited to. + example: "2" + event: + $ref: "definitions/invite_event.yaml" + invite_room_state: + type: array + description: |- + An optional list of simplified events to help the receiver of the invite + identify the room. The recommended events to include are the join rules, + canonical alias, avatar, and name of the room. + items: + type: object + title: Invite Room State Event + properties: + type: + type: string + description: The type of event. + example: "m.room.join_rules" + state_key: + type: string + description: The state key for the event. May be an empty string. + example: "" + content: + type: object + description: The content for the event. + sender: + type: string + description: The sender of the event. + example: "@someone:matrix.org" + required: ['type', 'state_key', 'content', 'sender'] + example: [ + { + "type": "m.room.join_rules", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "join_rule": "public" + } + } + ] + required: ['room_version', 'event'] + example: { + "room_version": "2", + "event": { + "$ref": "examples/minimal_pdu.json", + "type": "m.room.member", + "state_key": "@joe:elsewhere.com", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", + "content": { + "membership": "invite" + }, + "signatures": { + "example.com": { + "ed25519:key_version": "SomeSignatureHere" + }, + } + }, + "invite_room_state": [ + { + "type": "m.room.join_rules", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "join_rule": "public" + } + }, + { + "type": "m.room.name", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "name": "Cool New Room" + } + } + ] + } + responses: + 200: + description: |- + The event with the invited server's signature added. All other fields of the events + should remain untouched. Note that events have a different format depending on the + room version - check the `room version specification`_ for precise event formats. + schema: + type: object + description: An object containing the signed invite event. + title: Event Container + properties: + event: + $ref: "definitions/invite_event.yaml" + required: ['event'] + examples: + application/json: { + "event": { + "$ref": "examples/minimal_pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", + "unsigned": { + "invite_room_state": [ + { + "type": "m.room.join_rules", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "join_rule": "public" + } + }, + { + "type": "m.room.name", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "name": "Cool New Room" + } + } + ] + }, + "content": { + "membership": "invite" + }, + "signatures": { + "example.com": { + "ed25519:key_version": "SomeSignatureHere" + }, + "elsewhere.com": { + "ed25519:k3y_versi0n": "SomeOtherSignatureHere" + } + } + } + } + 403: + description: |- + The invite is not allowed. This could be for a number of reasons, including: + + * The sender is not allowed to send invites to the target user/homeserver. + * The homeserver does not permit anyone to invite its users. + * The homeserver refuses to participate in the room. + schema: + $ref: "../client-server/definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "User cannot invite the target user." + } + 400: + description: |- + The request is invalid or the room the server is attempting + to join has a version that is not listed in the ``ver`` + parameters. + + The error should be passed through to clients so that they + may give better feedback to users. + schema: + allOf: + - $ref: "../client-server/definitions/errors/error.yaml" + - type: object + properties: + room_version: + type: string + description: |- + The version of the room. Required if the ``errcode`` + is ``M_INCOMPATIBLE_ROOM_VERSION``. + examples: + application/json: { + "errcode": "M_INCOMPATIBLE_ROOM_VERSION", + "error": "Your homeserver does not support the features required to join this room", + "room_version": "3" + } diff --git a/api/server-server/joins.yaml b/api/server-server/joins.yaml index 6d97e3aa..1b5f4632 100644 --- a/api/server-server/joins.yaml +++ b/api/server-server/joins.yaml @@ -61,100 +61,80 @@ paths: responses: 200: description: |- - A template to be used for the rest of the `Joining Rooms`_ handshake. + A template to be used for the rest of the `Joining Rooms`_ handshake. Note that + events have a different format depending on the room version - check the + `room version specification`_ for precise event formats. **The response body + here describes the common event fields in more detail and may be missing other + required fields for a PDU.** schema: type: object properties: + room_version: + type: string + description: |- + The version of the room where the server is trying to join. If not provided, + the room version is assumed to be either "1" or "2". + example: "2" event: - allOf: - - $ref: "definitions/unsigned_pdu.yaml" - - description: |- - An unsigned template event. + description: |- + An unsigned template event. Note that events have a different format + depending on the room version - check the `room version specification`_ + for precise event formats. + type: object + title: Event Template + properties: + sender: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + origin: + type: string + description: The name of the resident homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the resident homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + content: type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "join"} properties: - # Note: we override a bunch of parameters to change their descriptions - sender: - type: string - description: The user ID of the joining member. - example: "@someone:example.org" - origin: - type: string - description: The name of the resident homeserver. - example: "matrix.org" - origin_server_ts: - type: integer - format: int64 - description: A timestamp added by the resident homeserver. - example: 1234567890 - type: - type: string - description: The value ``m.room.member``. - example: "m.room.member" - state_key: - type: string - description: The user ID of the joining member. - example: "@someone:example.org" - content: - type: object - title: Membership Event Content - description: The content of the event. - example: {"membership": "join"} - properties: - membership: - type: string - description: The value ``join``. - example: "join" - required: ['membership'] - depth: - type: integer - description: This field must be present but is ignored; it may be 0. - example: 12 - auth_events: - type: array - description: |- - An event reference list containing the authorization events that would - allow the member to join the room. This should normally be the - ``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules`` - events. - items: - type: array - maxItems: 2 - minItems: 2 - items: - - type: string - title: Event ID - example: "$abc123:matrix.org" - - type: object - title: Event Hash - example: { - "sha256": "abase64encodedsha256hashshouldbe43byteslong" - } - properties: - sha256: - type: string - description: The event hash. - example: abase64encodedsha256hashshouldbe43byteslong - required: ['sha256'] - redacts: + membership: type: string - description: Not used. - required: - # Every other field is already flagged as required by the $ref - - state_key + description: The value ``join``. + example: "join" + required: ['membership'] + required: + - state_key + - origin + - origin_server_ts + - type + - content + - sender examples: application/json: { - event: { - "$ref": "examples/unsigned_pdu.json", + "room_version": "2", + "event": { + "$ref": "examples/minimal_pdu.json", "type": "m.room.member", "state_key": "@someone:example.org", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", "content": { "membership": "join" - }, - "auth_events": [ - ["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], - ["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], - ["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}] - ] + } } } 400: @@ -186,7 +166,12 @@ paths: summary: Submit a signed join event to a resident server description: |- Submits a signed join event to the resident server for it - to accept it into the room's graph. + to accept it into the room's graph. Note that events have + a different format depending on the room version - check + the `room version specification`_ for precise event formats. + **The request and response body here describes the common + event fields in more detail and may be missing other required + fields for a PDU.** operationId: sendJoin security: - signedRequest: [] @@ -208,81 +193,54 @@ paths: type: object required: true schema: - allOf: - - $ref: "definitions/pdu.yaml" - - type: object + type: object + properties: + sender: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + origin: + type: string + description: The name of the joining homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the joining homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "join"} properties: - # Note: we override a bunch of parameters to change their descriptions - sender: + membership: type: string - description: The user ID of the joining member. - example: "@someone:example.org" - origin: - type: string - description: The name of the joining homeserver. - example: "matrix.org" - origin_server_ts: - type: integer - format: int64 - description: A timestamp added by the joining homeserver. - example: 1234567890 - type: - type: string - description: The value ``m.room.member``. - example: "m.room.member" - state_key: - type: string - description: The user ID of the joining member. - example: "@someone:example.org" - content: - type: object - title: Membership Event Content - description: The content of the event. - example: {"membership": "join"} - properties: - membership: - type: string - description: The value ``join``. - example: "join" - required: ['membership'] - depth: - type: integer - description: This field must be present but is ignored; it may be 0. - example: 12 - auth_events: - type: array - description: |- - An event reference list containing the authorization events that would - allow the member to join the room. - items: - type: array - maxItems: 2 - minItems: 2 - items: - - type: string - title: Event ID - example: "$abc123:matrix.org" - - type: object - title: Event Hash - example: { - "sha256": "abase64encodedsha256hashshouldbe43byteslong" - } - properties: - sha256: - type: string - description: The event hash. - example: abase64encodedsha256hashshouldbe43byteslong - required: ['sha256'] - redacts: - type: string - description: Not used. - required: - # Every other field is already flagged as required by the $ref - - state_key + description: The value ``join``. + example: "join" + required: ['membership'] + required: + - state_key + - sender + - origin + - origin_server_ts + - type + - content example: { - "$ref": "examples/pdu.json", + "$ref": "examples/minimal_pdu.json", "type": "m.room.member", "state_key": "@someone:example.org", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", "content": { "membership": "join" } @@ -308,25 +266,44 @@ paths: description: The resident server's DNS name. auth_chain: type: array - description: The auth chain. + description: |- + The auth chain. Note that events have a different format depending on + the room version - check the `room version specification`_ for precise + event formats. items: type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ that make up the auth chain. The event format varies depending + on the room version - check the `room version specification`_ for precise event formats. schema: - $ref: "definitions/pdu.yaml" + type: object + properties: [] + example: + $ref: "examples/minimal_pdu.json" state: type: array - description: The room state. + description: |- + The room state. The event format varies depending on the room version - + check the `room version specification`_ for precise event formats. items: type: object + title: PDU + description: |- + The `PDUs <#pdus>`_ for the fully resolved state of the room. The event format varies depending + on the room version - check the `room version specification`_ for precise event formats. schema: - $ref: "definitions/pdu.yaml" + type: object + properties: [] + example: + $ref: "examples/minimal_pdu.json" required: ["auth_chain", "state", "origin"] examples: application/json: [ 200, { "origin": "matrix.org", - "auth_chain": [{"$ref": "examples/pdu.json"}], - "state": [{"$ref": "examples/pdu.json"}] + "auth_chain": [{"$ref": "examples/minimal_pdu.json"}], + "state": [{"$ref": "examples/minimal_pdu.json"}] } ] diff --git a/api/server-server/keys_server.yaml b/api/server-server/keys_server.yaml index 8734f2ed..69985ab7 100644 --- a/api/server-server/keys_server.yaml +++ b/api/server-server/keys_server.yaml @@ -27,7 +27,7 @@ paths: get: summary: Get the homeserver's public key(s) description: |- - Gets the homeserver's published TLS fingerprints and signing keys. + Gets the homeserver's published signing keys. The homeserver may have any number of active keys and may have a number of old keys. @@ -49,7 +49,7 @@ paths: type: string description: |- **Deprecated**. Servers should not use this parameter and instead - opt to return all keys, not just the requested one. The key ID to + opt to return all keys, not just the requested one. The key ID to look up. required: false x-example: "ed25519:abc123" diff --git a/api/server-server/leaving.yaml b/api/server-server/leaving.yaml index bb4bbe3d..c088cb5d 100644 --- a/api/server-server/leaving.yaml +++ b/api/server-server/leaving.yaml @@ -52,97 +52,81 @@ paths: responses: 200: description: |- - A template to be used to call ``/send_leave``. + A template to be used to call ``/send_leave``. Note that + events have a different format depending on the room version - check the + `room version specification`_ for precise event formats. **The response body + here describes the common event fields in more detail and may be missing other + required fields for a PDU.** schema: schema: type: object properties: + room_version: + type: string + description: |- + The version of the room where the server is trying to leave. If not provided, + the room version is assumed to be either "1" or "2". + example: "2" event: - allOf: - - $ref: "definitions/unsigned_pdu.yaml" - - description: |- - An unsigned template event. + description: |- + An unsigned template event. Note that events have a different format + depending on the room version - check the `room version specification`_ + for precise event formats. + type: object + title: Event Template + properties: + sender: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + origin: + type: string + description: The name of the resident homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the resident homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + content: type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "leave"} properties: - # Note: we override a bunch of parameters to change their descriptions - sender: - type: string - description: The user ID of the leaving member. - example: "@someone:example.org" - origin: - type: string - description: The name of the resident homeserver. - example: "matrix.org" - origin_server_ts: - type: integer - format: int64 - description: A timestamp added by the resident homeserver. - example: 1234567890 - type: - type: string - description: The value ``m.room.member``. - example: "m.room.member" - state_key: - type: string - description: The user ID of the leaving member. - example: "@someone:example.org" - content: - type: object - title: Membership Event Content - description: The content of the event. - example: {"membership": "leave"} - properties: - membership: - type: string - description: The value ``leave``. - example: "leave" - required: ['membership'] - auth_events: - type: array - description: |- - An event reference list containing the authorization events that would - allow the member to leave the room. This should normally be the - ``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules`` - events. - items: - type: array - maxItems: 2 - minItems: 2 - items: - - type: string - title: Event ID - example: "$abc123:matrix.org" - - type: object - title: Event Hash - example: { - "sha256": "abase64encodedsha256hashshouldbe43byteslong" - } - properties: - sha256: - type: string - description: The event hash. - example: abase64encodedsha256hashshouldbe43byteslong - required: ['sha256'] - redacts: + membership: type: string - description: Not used. - required: - # Every other field is already flagged as required by the $ref - - state_key + description: The value ``leave``. + example: "leave" + required: ['membership'] + required: + - state_key + - sender + - origin + - origin_server_ts + - type + - content examples: application/json: { + "room_version": "2", "event": { - "$ref": "examples/unsigned_pdu.json", + "$ref": "examples/minimal_pdu.json", "type": "m.room.member", "state_key": "@someone:example.org", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", "content": { "membership": "leave" - }, - "auth_events": [ - ["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], - ["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], - ["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}] - ] + } } } 403: @@ -160,7 +144,12 @@ paths: summary: Submit a signed leave event to a resident server description: |- Submits a signed leave event to the resident server for it - to accept it into the room's graph. + to accept it into the room's graph. Note that events have + a different format depending on the room version - check + the `room version specification`_ for precise event formats. + **The request and response body here describes the common + event fields in more detail and may be missing other required + fields for a PDU.** operationId: sendLeave security: - signedRequest: [] @@ -182,81 +171,59 @@ paths: type: object required: true schema: - allOf: - - $ref: "definitions/pdu.yaml" - - type: object + type: object + properties: + sender: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + origin: + type: string + description: The name of the leaving homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the leaving homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "leave"} properties: - # Note: we override a bunch of parameters to change their descriptions - sender: - type: string - description: The user ID of the leaving member. - example: "@someone:example.org" - origin: - type: string - description: The name of the leaving homeserver. - example: "matrix.org" - origin_server_ts: - type: integer - format: int64 - description: A timestamp added by the leaving homeserver. - example: 1234567890 - type: - type: string - description: The value ``m.room.member``. - example: "m.room.member" - state_key: + membership: type: string - description: The user ID of the leaving member. - example: "@someone:example.org" - content: - type: object - title: Membership Event Content - description: The content of the event. - example: {"membership": "leave"} - properties: - membership: - type: string - description: The value ``leave``. - example: "leave" - required: ['membership'] - depth: - type: integer - description: This field must be present but is ignored; it may be 0. - example: 12 - auth_events: - type: array - description: |- - An event reference list containing the authorization events that would - allow the member to leave the room. - items: - type: array - maxItems: 2 - minItems: 2 - items: - - type: string - title: Event ID - example: "$abc123:matrix.org" - - type: object - title: Event Hash - example: { - "sha256": "abase64encodedsha256hashshouldbe43byteslong" - } - properties: - sha256: - type: string - description: The event hash. - example: abase64encodedsha256hashshouldbe43byteslong - required: ['sha256'] - redacts: - type: string - description: Not used. - required: - # Every other field is already flagged as required by the $ref - - state_key + description: The value ``leave``. + example: "leave" + required: ['membership'] + depth: + type: integer + description: This field must be present but is ignored; it may be 0. + example: 12 + required: + - state_key + - sender + - origin + - origin_server_ts + - type + - depth + - content example: { - "$ref": "examples/pdu.json", + "$ref": "examples/minimal_pdu.json", "type": "m.room.member", "state_key": "@someone:example.org", + "origin": "example.org", + "origin_server_ts": 1549041175876, + "sender": "@someone:example.org", "content": { "membership": "leave" } diff --git a/api/server-server/openid.yaml b/api/server-server/openid.yaml index 0eac48c8..0761618d 100644 --- a/api/server-server/openid.yaml +++ b/api/server-server/openid.yaml @@ -33,7 +33,7 @@ paths: User ID of the owner. operationId: exchangeOpenIdToken parameters: - - in: path + - in: query name: access_token type: string description: |- diff --git a/api/server-server/third_party_invite.yaml b/api/server-server/third_party_invite.yaml index 37c3a189..0b7aac5b 100644 --- a/api/server-server/third_party_invite.yaml +++ b/api/server-server/third_party_invite.yaml @@ -85,6 +85,7 @@ paths: third_party_invite: type: object description: The third party invite + title: Third Party Invite properties: display_name: type: string @@ -97,6 +98,7 @@ paths: description: |- A block of content which has been signed, which servers can use to verify the event. + title: Invite Signatures properties: signatures: type: object diff --git a/api/server-server/transactions.yaml b/api/server-server/transactions.yaml index 355be2c6..9cc8be75 100644 --- a/api/server-server/transactions.yaml +++ b/api/server-server/transactions.yaml @@ -37,6 +37,9 @@ paths: The sending server must wait and retry for a 200 OK response before sending a transaction with a different ``txnId`` to the receiving server. + + Note that events have a different format depending on the room version - check + the `room version specification`_ for precise event formats. operationId: sendTransaction security: - signedRequest: [] diff --git a/api/server-server/wellknown.yaml b/api/server-server/wellknown.yaml new file mode 100644 index 00000000..75676646 --- /dev/null +++ b/api/server-server/wellknown.yaml @@ -0,0 +1,52 @@ +# Copyright 2019 New Vector Ltd +# +# 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 Server Discovery API" + version: "1.0.0" +host: localhost:443 +schemes: + - https +basePath: /.well-known +produces: + - application/json +paths: + "/matrix/server": + get: + summary: Gets information about the delegated server for server-server communication. + description: |- + Gets information about the delegated server for server-server communication + between Matrix homeservers. Servers should follow 30x redirects, carefully + avoiding redirect loops, and use normal X.509 certificate validation. + responses: + 200: + description: + The delegated server information. The ``Content-Type`` for this response SHOULD + be ``application/json``, however servers parsing the response should assume that + the body is JSON regardless of type. Failures parsing the JSON or invalid data + provided in the resulting parsed JSON should not result in discovery failure - + consult the server discovery process for information on how to continue. + examples: + application/json: { + "m.server": "delegated.example.com:1234" + } + schema: + type: object + properties: + "m.server": + type: string + description: |- + The server name to delegate server-server communciations to, with optional + port. The delegated server name uses the same grammar as + `server names in the appendices <../appendices.html#server-name>`_. diff --git a/changelogs/client_server/newsfragments/1786.feature b/changelogs/client_server/newsfragments/1786.feature new file mode 100644 index 00000000..6f21778c --- /dev/null +++ b/changelogs/client_server/newsfragments/1786.feature @@ -0,0 +1 @@ +Add support for advertising experimental features to clients. diff --git a/changelogs/client_server/newsfragments/1790.feature b/changelogs/client_server/newsfragments/1790.feature new file mode 100644 index 00000000..26dccd05 --- /dev/null +++ b/changelogs/client_server/newsfragments/1790.feature @@ -0,0 +1 @@ +Add a mechanism for servers to redirect clients to an alternative homeserver after logging in. diff --git a/changelogs/client_server/newsfragments/1791.feature b/changelogs/client_server/newsfragments/1791.feature new file mode 100644 index 00000000..0a854c8f --- /dev/null +++ b/changelogs/client_server/newsfragments/1791.feature @@ -0,0 +1 @@ +Add room version upgrades. diff --git a/changelogs/client_server/newsfragments/1817.deprecation b/changelogs/client_server/newsfragments/1817.deprecation new file mode 100644 index 00000000..2c52d198 --- /dev/null +++ b/changelogs/client_server/newsfragments/1817.deprecation @@ -0,0 +1 @@ +Remove references to presence lists. diff --git a/changelogs/client_server/newsfragments/1829.feature b/changelogs/client_server/newsfragments/1829.feature new file mode 100644 index 00000000..107291f3 --- /dev/null +++ b/changelogs/client_server/newsfragments/1829.feature @@ -0,0 +1 @@ +Support optional features by having clients query for capabilities. diff --git a/changelogs/client_server/newsfragments/1838.clarification b/changelogs/client_server/newsfragments/1838.clarification new file mode 100644 index 00000000..b0f05203 --- /dev/null +++ b/changelogs/client_server/newsfragments/1838.clarification @@ -0,0 +1 @@ +Fix various spelling mistakes throughout the specification. diff --git a/changelogs/client_server/newsfragments/1853.clarification b/changelogs/client_server/newsfragments/1853.clarification new file mode 100644 index 00000000..b0f05203 --- /dev/null +++ b/changelogs/client_server/newsfragments/1853.clarification @@ -0,0 +1 @@ +Fix various spelling mistakes throughout the specification. diff --git a/changelogs/client_server/newsfragments/1860.clarification b/changelogs/client_server/newsfragments/1860.clarification new file mode 100644 index 00000000..b0f05203 --- /dev/null +++ b/changelogs/client_server/newsfragments/1860.clarification @@ -0,0 +1 @@ +Fix various spelling mistakes throughout the specification. diff --git a/changelogs/client_server/newsfragments/1873.new b/changelogs/client_server/newsfragments/1873.new new file mode 100644 index 00000000..724a4308 --- /dev/null +++ b/changelogs/client_server/newsfragments/1873.new @@ -0,0 +1 @@ +``GET /account_data`` routes. diff --git a/changelogs/client_server/newsfragments/1874.feature b/changelogs/client_server/newsfragments/1874.feature new file mode 100644 index 00000000..bdab5464 --- /dev/null +++ b/changelogs/client_server/newsfragments/1874.feature @@ -0,0 +1 @@ +Add ``M_RESOURCE_LIMIT_EXCEEDED`` as an error code for when homeservers exceed limits imposed on them. diff --git a/changelogs/client_server/newsfragments/1875.feature b/changelogs/client_server/newsfragments/1875.feature new file mode 100644 index 00000000..0a854c8f --- /dev/null +++ b/changelogs/client_server/newsfragments/1875.feature @@ -0,0 +1 @@ +Add room version upgrades. diff --git a/changelogs/client_server/newsfragments/1879.feature b/changelogs/client_server/newsfragments/1879.feature new file mode 100644 index 00000000..107291f3 --- /dev/null +++ b/changelogs/client_server/newsfragments/1879.feature @@ -0,0 +1 @@ +Support optional features by having clients query for capabilities. diff --git a/changelogs/client_server/newsfragments/1889.clarification b/changelogs/client_server/newsfragments/1889.clarification new file mode 100644 index 00000000..5026dab3 --- /dev/null +++ b/changelogs/client_server/newsfragments/1889.clarification @@ -0,0 +1 @@ +Add the missing `m.push_rules` event schema. diff --git a/changelogs/client_server/newsfragments/1903.feature b/changelogs/client_server/newsfragments/1903.feature new file mode 100644 index 00000000..1c64d826 --- /dev/null +++ b/changelogs/client_server/newsfragments/1903.feature @@ -0,0 +1 @@ +Emit ``M_UNSUPPORTED_ROOM_VERSION`` error codes where applicable on ``/createRoom`` and ``/invite`` APIs. diff --git a/changelogs/client_server/newsfragments/1933.clarification b/changelogs/client_server/newsfragments/1933.clarification new file mode 100644 index 00000000..b0f05203 --- /dev/null +++ b/changelogs/client_server/newsfragments/1933.clarification @@ -0,0 +1 @@ +Fix various spelling mistakes throughout the specification. diff --git a/changelogs/identity_service/newsfragments/1853.clarification b/changelogs/identity_service/newsfragments/1853.clarification new file mode 100644 index 00000000..b0f05203 --- /dev/null +++ b/changelogs/identity_service/newsfragments/1853.clarification @@ -0,0 +1 @@ +Fix various spelling mistakes throughout the specification. diff --git a/changelogs/server_server.rst b/changelogs/server_server.rst index e69de29b..a21da177 100644 --- a/changelogs/server_server.rst +++ b/changelogs/server_server.rst @@ -0,0 +1,16 @@ +r0.1.1 +====== + +Spec Clarifications +------------------- + +- Remove legacy references to TLS fingerprints. (`#1844 `_) +- Clarify that servers should not fail to contact servers if ``/.well-known`` fails. (`#1855 `_) + + +r0.1.0 +====== + +This is the first release of the Server Server (Federation) specification. +It includes support for homeservers being able to interact with other +homeservers in a decentralized and standard way. diff --git a/changelogs/server_server/newsfragments/1904.clarification b/changelogs/server_server/newsfragments/1904.clarification new file mode 100644 index 00000000..94174ebd --- /dev/null +++ b/changelogs/server_server/newsfragments/1904.clarification @@ -0,0 +1 @@ +Fix the `access_token` parameter in the open_id endpoint. diff --git a/event-schemas/examples/m.push_rules b/event-schemas/examples/m.push_rules new file mode 100644 index 00000000..e4f0a959 --- /dev/null +++ b/event-schemas/examples/m.push_rules @@ -0,0 +1,192 @@ +{ + "$ref": "core/event.json", + "type": "m.push_rules", + "content": { + "global": { + "content": [ + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight" + } + ], + "default": true, + "enabled": true, + "pattern": "alice", + "rule_id": ".m.rule.contains_user_name" + } + ], + "override": [ + { + "actions": [ + "dont_notify" + ], + "conditions": [], + "default": true, + "enabled": false, + "rule_id": ".m.rule.master" + }, + { + "actions": [ + "dont_notify" + ], + "conditions": [ + { + "key": "content.msgtype", + "kind": "event_match", + "pattern": "m.notice" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.suppress_notices" + } + ], + "room": [], + "sender": [], + "underride": [ + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "ring" + }, + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.call.invite" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.call" + }, + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight" + } + ], + "conditions": [ + { + "kind": "contains_display_name" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.contains_display_name" + }, + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "is": "2", + "kind": "room_member_count" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.room_one_to_one" + }, + { + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.member" + }, + { + "key": "content.membership", + "kind": "event_match", + "pattern": "invite" + }, + { + "key": "state_key", + "kind": "event_match", + "pattern": "@alice:example.com" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.invite_for_me" + }, + { + "actions": [ + "notify", + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.member" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.member_event" + }, + { + "actions": [ + "notify", + { + "set_tweak": "highlight", + "value": false + } + ], + "conditions": [ + { + "key": "type", + "kind": "event_match", + "pattern": "m.room.message" + } + ], + "default": true, + "enabled": true, + "rule_id": ".m.rule.message" + } + ] + } + } +} diff --git a/event-schemas/examples/m.room.create b/event-schemas/examples/m.room.create index 29b6237a..e33dbc3b 100644 --- a/event-schemas/examples/m.room.create +++ b/event-schemas/examples/m.room.create @@ -5,6 +5,10 @@ "content": { "creator": "@example:example.org", "room_version": "1", - "m.federate": true + "m.federate": true, + "predecessor": { + "event_id": "$something:example.org", + "room_id": "!oldroom:example.org" + } } } diff --git a/event-schemas/examples/m.room.tombstone b/event-schemas/examples/m.room.tombstone new file mode 100644 index 00000000..b6224476 --- /dev/null +++ b/event-schemas/examples/m.room.tombstone @@ -0,0 +1,9 @@ +{ + "$ref": "core/state_event.json", + "type": "m.room.tombstone", + "state_key": "", + "content": { + "body": "This room has been replaced", + "replacement_room": "!newroom:example.org" + } +} diff --git a/event-schemas/schema/m.push_rules b/event-schemas/schema/m.push_rules new file mode 100644 index 00000000..6fde9e14 --- /dev/null +++ b/event-schemas/schema/m.push_rules @@ -0,0 +1,21 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml +description: Describes all push rules for this user. +properties: + content: + properties: + global: + type: object + title: Ruleset + description: The global ruleset + allOf: + - $ref: ../../api/client-server/definitions/push_ruleset.yaml + type: object + type: + enum: + - m.push_rules + type: string +title: Push rules +type: object + diff --git a/event-schemas/schema/m.room.create b/event-schemas/schema/m.room.create index d12b9ccd..a6fe331e 100644 --- a/event-schemas/schema/m.room.create +++ b/event-schemas/schema/m.room.create @@ -14,6 +14,18 @@ properties: room_version: description: The version of the room. Defaults to ``"1"`` if the key does not exist. type: string + predecessor: + description: A reference to the room this room replaces, if the previous room was upgraded. + type: object + title: Previous Room + properties: + room_id: + type: string + description: The ID of the old room. + event_id: + type: string + description: The event ID of the last known event in the old room. + required: [room_id, event_id] required: - creator type: object diff --git a/event-schemas/schema/m.room.tombstone b/event-schemas/schema/m.room.tombstone new file mode 100644 index 00000000..0fd8ba45 --- /dev/null +++ b/event-schemas/schema/m.room.tombstone @@ -0,0 +1,27 @@ +--- +allOf: + - $ref: core-event-schema/state_event.yaml +description: 'A state event signifying that a room has been upgraded to a different room version, and that clients should go there.' +properties: + content: + properties: + body: + type: string + description: A server-defined message. + replacement_room: + type: string + description: The new room the client should be visiting. + required: + - replacement_room + - body + type: object + state_key: + description: A zero-length string. + pattern: '^$' + type: string + type: + enum: + - m.room.tombstone + type: string +title: Indication that the room has been upgraded. +type: object diff --git a/meta/releasing_a_spec.md b/meta/releasing_a_spec.md index 2d9cd34e..f186c4be 100644 --- a/meta/releasing_a_spec.md +++ b/meta/releasing_a_spec.md @@ -18,16 +18,18 @@ The remainder of the process is as follows: 1. Activate your Python 3 virtual environment. 1. Having checked out the new release branch, navigate your way over to `./changelogs`. 1. Follow the release instructions provided in the README.md located there. -1. Update the changelog section of the specification you're releasing to make a reference - to the new version. 1. Update any version/link references across all specifications. -1. Ensure the `targets.yml` file lists the version correctly. -1. Commit the changes and PR them to master. -1. Tag the release with the format `client_server/r0.4.0`. -1. Add the changes to the matrix-org/matrix.org repository (for historic tracking). +1. Generate the specification using `./scripts/gendoc.py`, specifying all the + API versions at the time of generation. For example: `./scripts/gendoc.py -c r0.4.0 -s r0.1.0 -i r0.1.0 #etc` +1. PR the changes to the matrix-org/matrix.org repository (for historic tracking). * This is done by making a PR to the `unstyled_docs/spec` folder for the version and specification you're releasing. * Don't forget to symlink the new release as `latest`. + * For the client-server API, don't forget to generate the swagger JSON by using + `./scripts/dump-swagger.py -c r0.4.0`. This will also need symlinking to `latest`. +1. Commit the changes and PR them to master. **Wait for review from the spec core team.** + * Link to your matrix-org/matrix.org so both can be reviewed at the same time. +1. Tag the release with the format `client_server/r0.4.0`. 1. Perform a release on GitHub to tag the release. 1. Yell from the mountaintop to the world about the new release. diff --git a/proposals/1501-room-version-upgrades.md b/proposals/1501-room-version-upgrades.md index 3a726250..7bd310ea 100644 --- a/proposals/1501-room-version-upgrades.md +++ b/proposals/1501-room-version-upgrades.md @@ -48,7 +48,7 @@ When this is called, the server: "state_key": "", "room_id": "!QtykxKocfsgujksjgd:matrix.org", "content": { - "version": "2", + "room_version": "2", "predecessor": { "room_id": "!cURbaf:matrix.org", "event_id": "$1235135aksjgdkg:matrix.org" diff --git a/proposals/1659-event-id-as-hashes.md b/proposals/1659-event-id-as-hashes.md new file mode 100644 index 00000000..1187d95e --- /dev/null +++ b/proposals/1659-event-id-as-hashes.md @@ -0,0 +1,130 @@ +# Changing Event IDs to be Hashes + +## Motivation + +Having event IDs separate from the hashes leads to issues when a server receives +multiple events with the same event ID but different reference hashes. While +APIs could be changed to better support dealing with this situation, it is +easier and nicer to simply drop the idea of a separate event ID entirely, and +instead use the reference hash of an event as its ID. + +## Identifier Format + +Currently hashes in our event format include the hash name, allowing servers to +choose which hash functions to use. The idea here was to allow a gradual change +between hash functions without the need to globally coordinate shifting from one +hash function to another. + +However now that room versions exist, changing hash functions can be achieved by +bumping the room version. Using this method would allow using a simple string as +the event ID rather than a full structure, significantly easing their usage. + +One side effect of this would be that there would be no indication about which +hash function was actually used, and it would need to be inferred from the room +version. To aid debuggability it may be worth encoding the hash function into +the ID format. + +**Conclusion:** Don't encode the hash function, since the hash will depend on +the version specific redaction algorithm anyway. + +The proposal is therefore that the event IDs are a sha256 hash, encoded using +[unpadded +Base64](https://matrix.org/docs/spec/appendices.html#unpadded-base64), and +prefixed with `$` (to aid distinguishing different types of identifiers). For +example, an event ID might be: `$CD66HAED5npg6074c6pDtLKalHjVfYb2q4Q3LZgrW6o`. + +The hash is calculated in the same way as previous event reference hashes were, +which is: + +1. Redact the event +2. Remove `signatures` field from the event +3. Serialize the event to canonical JSON +4. Compute the hash of the JSON bytes + +Event IDs will no longer be included as part of the event, and so must be +calculated by servers receiving the event. + + +## Changes to Event Formats + +As well as changing the format of event IDs, we also change the format of the +`auth_events` and `prev_events` keys in events to simply be lists of event IDs +(rather than being lists of tuples). + +A full event would therefore look something like (note that this is just an +illustrative example, and that the hashes are not correct): + +```json +{ + "auth_events": [ + "$5hdALbO+xIhzcLTxCkspx5uqry9wO8322h/OI9ApnHE", + "$Ga0DBIICBsWIZbN292ATv8fTHIGGimwjb++w+zcHLRo", + "$zc4ip/DpPI9FZVLM1wN9RLqN19vuVBURmIqAohZ1HXg", + ], + "content": { + "body": "Here is the message content", + "msgtype": "m.message" + }, + "depth": 6, + "hashes": { + "sha256": "M6/LmcMMJKc1AZnNHsuzmf0PfwladVGK2Xbz+sUTN9k" + }, + "origin": "localhost:8800", + "origin_server_ts": 1548094046693, + "prev_events": [ + "$MoOzCuB/sacqHAvgBNOLICiGLZqGT4zB16MSFOuiO0s", + ], + "room_id": "!eBrhCHJWOgqrOizwwW:localhost:8800", + "sender": "@anon-20190121_180719-33:localhost:8800", + "signatures": { + "localhost:8800": { + "ed25519:a_iIHH": "N7hwZjvHyH6r811ebZ4wwLzofKhJuIAtrQzaD3NZbf4WQNijXl5Z2BNB047aWIQCS1JyFOQKPVom4et0q9UOAA" + } + }, + "type": "m.room.message" +} +``` + +## Changes to existing APIs + +All APIs that accept event IDs must accept event IDs in the new format. + +For S2S API, whenever a server needs to parse an event from a request or +response they must either already know the room version *or* be told the room +version in the request/response. There are separate MSCs to update APIs where +necessary. + +For C2S API, the only change clients will see is that the event IDs have changed +format. Clients should already be treating event IDs as opaque strings, so no +changes should be required. Servers must add the `event_id` when sending the +event to clients, however. + +Note that the `auth_events` and `prev_events` fields aren't sent to clients, and +so the changes proposed above won't affect clients. + + +## Protocol Changes + +The `auth_events` and `prev_events` fields on an event need to be changed from a +list of tuples to a list of strings, i.e. remove the old event ID and simply +have the list of hashes. + +The auth rules also need to change: + +- The event no longer needs to be signed by the domain of the event ID (but + still needs to be signed by the sender’s domain) + +- We currently allow redactions if the domain of the redaction event ID + matches the domain of the event ID it is redacting; which allows self + redaction. This check is removed and redaction events are always accepted. + Instead, the redaction event only takes effect and is sent down to clients + if/when the original event is received, and the domain of the events' + senders match. (While this is clearly suboptimal, it is the only practical + suggestion) + + +## Room Version + +There will be a new room version v3 that is the same as v2 except uses the new +event format proposed above. v3 will be marked as 'stable' as defined in [MSC1804](https://github.com/matrix-org/matrix-doc/blob/travis/msc/room-version-client-advertising/proposals/1804-advertising-capable-room-versions.md) + diff --git a/proposals/1708-well-known-for-federation.md b/proposals/1708-well-known-for-federation.md index 8105a638..c9f90631 100644 --- a/proposals/1708-well-known-for-federation.md +++ b/proposals/1708-well-known-for-federation.md @@ -43,9 +43,9 @@ certificate validation, and following 30x redirects (being careful to avoid redirect loops). If the request does not return a 200, continue to step 4, otherwise: -The response must have a `Content-Type` of `application/json`, and must be -valid JSON which follows the structure documented below. Otherwise, the -request is aborted. +The response must be valid JSON which follows the structure documented +below. Otherwise, continue to the next step in the discovery process. It is +NOT necessary for the response to have a `Content-Type` of `application/json`. If the response is valid, the `m.server` property is parsed as `[:]`, and processed as follows: diff --git a/proposals/1753-capabilities.md b/proposals/1753-capabilities.md index ec5169ab..5a56eaca 100644 --- a/proposals/1753-capabilities.md +++ b/proposals/1753-capabilities.md @@ -40,8 +40,10 @@ As a starting point, a single capability identifier is proposed: change the user's password via the `POST /_matrix/client/r0/account/password` API. -The value of the `capabilities` object in the response should be the empty -object. +The value of the `capabilities` object in the response should contain a single +boolean flag, `enabled`, to indicate whether a password change is possible. If +the capability is not listed, the client should assume that password changes +are possible. ### Fallback behaviour diff --git a/proposals/1794-federation-v2-invites.md b/proposals/1794-federation-v2-invites.md new file mode 100644 index 00000000..a2f2bb35 --- /dev/null +++ b/proposals/1794-federation-v2-invites.md @@ -0,0 +1,78 @@ +# MSC 1794 - Federation v2 Invite API + +This proposal adds a new `/invite` API to federation that supports different +room versions. + +## Motivation + +It is planned for future room versions to be able to change the format of events +in various ways. To support this, all servers must know the room version +whenever they need to parse an event. Currently the `/invite` API does not +include the room version, so the target server would be unable to parse the event included in the payload. + +## Proposal + +Add a new version of the invite API under the prefix `/_matrix/federation/v2`, +which has a payload of: + +``` +PUT /_matrix/federation/v2/invite// + +{ + "room_version": , + "event": { ... }, + "invite_room_state": [ ... ] +} +``` + +The differences between this and `v1` are: + +1. The payload in `v1` is the event, while in `v2` the event is instead placed + under an `"event"` key. This is for consistency with other APIs, and to allow + extra data to be added to the request payload separately from the event. +2. A required field called `"room_version"` is added that specifies the room + version. +3. The `"invite_room_state"` is moved from the `unsigned` section of the event + to a top level key. The key `invite_room_state` being in the `event.unsigned` + was a hack due to point 1. above. + + +The response is identical to `v1`, except that: + +1. If the receiving server does not support the given room version the + appropriate incompatible-room-version error is returned, in the same way + as e.g. for `/make_join` APIs. +2. The response payload is no longer in the format of `[200, { ... }]`, and is + instead simply the `{ ... }` portion. This fixes a historical accident to + bring the invite API into line with the rest of the federation API. + + +If a call to `v2` `/invite` results in an unrecognised request exception **AND** +the room version is `1` or `2` then the sending server should retry the request +with the `v1` API. + + +## Alternatives + + +### Reusing V1 API + +One alternative is to add a `room_version` query string parameter to the `v1` +`/invite` API in a similar way as for the `/make_join` APIs. However, older +servers would ignore the query string parameter while processing an incoming +`/invite` request, resulting in the server attempting to parse the event in the +old `v1` format. This would likely result in either a `400` or `500` response, +which the sending server could interpret as the receiving server not supporting +the room version. + +This method, however, is fragile and could easily mask legitimate `400` and +`500` errors that are not due to not supporting the room version. + + +### Using V1 API for V1 room versions + +Instead of all servers attempting to use the new API and falling back if the API +is not found, servers could instead always use the current API for V1 and V2 +rooms. + +However, this would not allow us to deprecate the `v1` API. diff --git a/proposals/1804-advertising-capable-room-versions.md b/proposals/1804-advertising-capable-room-versions.md new file mode 100644 index 00000000..684bd524 --- /dev/null +++ b/proposals/1804-advertising-capable-room-versions.md @@ -0,0 +1,50 @@ +# Proposal for advertising capable room versions to clients + +Currently clients need to guess at which room versions the server supports, if any. This is particularly +difficult to do as it generally requires knowing the state of the ecosystem and what versions are +available and how keen users are to upgrade their servers and clients. The impossible judgement call +for when to encourage people to upgrade shouldn't be impossible, or even a judgement call. + + +## Proposal + +Building off of [MSC1753](https://github.com/matrix-org/matrix-doc/pull/1753) (capabilities API) and +the [recommendations laid out for room versions](https://github.com/matrix-org/matrix-doc/pull/1773/files#diff-1436075794bb304492ca6953a6692cd0R463), +this proposal suggests a `m.room_versions` capability be introduced like the following: + +```json +{ + "capabilities": { + "m.room_versions": { + "default": "1", + "available": { + "1": "stable", + "2": "stable", + "state-res-v2-test": "unstable", + "event-ids-as-hashes": "unstable", + "3": "future-scifi-label" + } + } + } +} +``` + +Clients are encouraged to make use of this capability to determine if the server supports a given +version, and at what level of stability. Anything not flagged explicitly as `stable` should be treated +as `unstable` (ie: `future-scifi-label` is the same as `unstable`). + +The default version is the version that the server is using to create new rooms with. Clients can +make the assumption that the default version is a stable version. + +Clients should encourage people with sufficient permissions to perform an upgrade to upgrade their +rooms to the `default` room version when the room is using an `unstable` version. + + +## Potential issues + +Changes aren't pushed to the client, which means clients may want to poll this endpoint on some +heuristic instead. For example, clients may want to poll the endpoint weekly or when the user relaunches +the client. Clients may also wish to provide users a way to upgrade without considering the capabilities +of the server, expecting that the server may not support the user-provided version - the intention +being such a feature would be used by eager room administrators which do not want to relaunch their +client, for example. diff --git a/proposals/1812-federation-make-membership.md b/proposals/1812-federation-make-membership.md new file mode 100644 index 00000000..7960bf42 --- /dev/null +++ b/proposals/1812-federation-make-membership.md @@ -0,0 +1,21 @@ +# MSC 1813 - Federation Make Membership Room Version + +This proposal adds a new `room_version` field to the responses of `/make_leave` +and `/make_join` APIs. + +## Motivation + +It is planned for future room versions to be able to change the format of events +in various ways. To support this, all servers must know the room version +whenever they need to parse an event. Currently the `/make_*` APIs do not +include the room version in the response, so the requesting server is unable to +parse the event included in the response body. + +## Proposal + +Add a new `room_version` field to the response body of the `v1` `/make_join` and +`/make_leave` APIs, which describes the room version. + +For backwards compatibility servers must correctly handle responses that do not +include the new field. In which case the room version is assumed to be one of +either `1` or `2`. diff --git a/proposals/1819-remove-presence-lists.md b/proposals/1819-remove-presence-lists.md new file mode 100644 index 00000000..7c542509 --- /dev/null +++ b/proposals/1819-remove-presence-lists.md @@ -0,0 +1,55 @@ +# Remove references to presence lists + +[Presence](https://matrix.org/docs/spec/client_server/r0.4.0.html#id107) lists +allow a given user the ability to subscribe to other users and be alerted to +their current presence status. + +While spec'd, no established client has implemented support and the only server +side implementation raises privacy concerns. + +The proposal is to simply remove references to presence lists with a view to +revisiting the same ideas in the future. + +This MSC addresses +[#1810](https://github.com/matrix-org/matrix-doc/issues/1810) + +## Proposal + +Presence lists seem like a good fit for ['MSC1769: Extensible profiles as +rooms'](https://github.com/matrix-org/matrix-doc/pull/1769) proposal, meaning +that the current design will most likely be superceded. + +Additionally, no major client has implemented the behaviour to date and the +only server implementation of presence lists (Synapse) auto-accepts invites +leading to privacy concerns in the wild. + +With this in mind the most pragmatic solution is to remove presence lists ahead +of the r0 release. + +Specifically:- + +CS API: Remove +* [POST + /_matrix/client/r0/presence/list/{userId}](https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-presence-list-userid) +* [GET + /_matrix/client/r0/presence/list/{userId}](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-presence-list-userid) + +SS API: Remove + * [m.presence_invite](https://matrix.org/docs/spec/server_server/unstable.html#m-presence-invite-schema) + * [m.presence_accept](https://matrix.org/docs/spec/server_server/unstable.html#m-presence-accept-schema) + * [m.presence_deny](https://matrix.org/docs/spec/server_server/unstable.html#m-presence-deny-schema) + + +## Tradeoffs + +Ideally this proposal would also come with an alternative design for this +functionality. Out of pragmatism the proposal only covers removal of what is +there today. + + +## Conclusions + +This is a common sense attempt to remove unused portions of the spec ahead of +an r0 release. It does not suggest that the ability to subscribe to the +presence of others is undesirable and assumes that this behvaiour will return +again in some form. diff --git a/proposals/1831-srv-after-wellknown.md b/proposals/1831-srv-after-wellknown.md new file mode 100644 index 00000000..4dc4da53 --- /dev/null +++ b/proposals/1831-srv-after-wellknown.md @@ -0,0 +1,25 @@ +# Proposal to do SRV lookups after .well-known to discover homeservers + +Currently there is a logistical error proposed by [MSC1708](https://github.com/matrix-org/matrix-doc/pull/1708) +which results in some homeservers unable to migrate to the new functionality +proposed by [MSC1711](https://github.com/matrix-org/matrix-doc/pull/1711). This +can happen if the delegated homeserver cannot obtain a valid TLS certificate for +the domain, and an SRV record is used for backwards compatibility reasons. + +Specifically, in order to be compatible with requests from both Synapse 0.34 and 1.0, +servers can have both a SRV and a .well-known file, with Synapse presenting a certificate +corresponding to the target of the .well-known. Synapse 0.34 is then happy because it +will follow the SRV (and won't care about the incorrect certificate); Synapse 1.0 is +happy because it will follow the .well-known (and will see the correct cert). + +## Proposal + +We change the order of operations to perform a .well-known lookup before falling +back to resolving the SRV record. This allows for domains to delegate to other +hostnames and maintains backwards compatibility with older homeservers. + +## Tradeoffs + +More HTTP hits will be made due to the .well-known lookup being first. This is +somewhat mitigated by servers caching the responses appropriately, and using +connection pools where possible. diff --git a/proposals/1866-invite-unsupported-version-error-code.md b/proposals/1866-invite-unsupported-version-error-code.md new file mode 100644 index 00000000..98098637 --- /dev/null +++ b/proposals/1866-invite-unsupported-version-error-code.md @@ -0,0 +1,14 @@ +# MSC 1866 - Unsupported Room Version Error Code for Invites + +It is currently unspecified what error code should be relayed to clients when +they attempt to invite a user on a remote server that does not support the room +version. + +The proposal is to reuse the `M_UNSUPPORTED_ROOM_VERSION` error code that is +currently returned by the create room API. + +Strictly, the error returned by the create room API would mean the local server +didn't support the room version, while for the invite API it would mean the +remote server didn't. However, there is sufficient overlap that it makes sense +to reuse the same error code and rely on the context to differentiate the two +cases. diff --git a/proposals/1915-unbind-identity-server-param.md b/proposals/1915-unbind-identity-server-param.md new file mode 100644 index 00000000..053552f6 --- /dev/null +++ b/proposals/1915-unbind-identity-server-param.md @@ -0,0 +1,107 @@ +# MSC 1915 - Add unbind 3PID APIs + +Note that this is a simplified version of MSC1194. + + +## Motivation + +Currently we do not have a reasonable route for a user to unbind/remove a 3PID +from their account, particularly when deactivating their account. Users have an +expectation to be able to do this, and thus we should have an API to provide it. + +This is meant as a simple extension to the current APIs, and so this explicitly +does not try and solve any existing usability concerns. + + +## API Changes + +### Client-Server 3PID Delete API + +Add an `id_server` param to `POST /_matrix/client/r0/account/3pid/delete` API, +which matches the 3PID creation APIs. + +The new `id_server` parameter is optional and if missing the server will attempt +to unbind from the identity server used when originally binding the 3pid (if +known by the homeserver). + +The 200 response is a JSON object with an `id_server_unbind_result` field whose +value is either `success` or `no-support`, where the latter indicates that the +identity server (IS) does not support unbinding 3PIDs directly. If the identity +server returns an error then that should be returned to the client. + +Example: + +``` +POST /_matrix/client/r0/account/3pid/delete HTTP/1.1 + +{ + "medium": "email", + "address": "foobar@example.com", + "id_server": "https://matrix.org +} + +HTTP/1.1 200 OK +{ + "id_server_unbind_result": "success" +} +``` + +### Client-Server Deactivate account API + +Add an `id_server` param to `POST /_matrix/client/r0/account/deactivate` API, +with the same semantics as above. This is used to unbind any bound threepids +from the given identity server. + + +### Identity Server 3PID Unbind API + +Add `POST /_matrix/identity/api/v1/unbind` with `mxid` and `threepid` fields. +The `mxid` is the user's `user_id` and `threepid` is a dict with the usual +`medium` and `address` fields. + +If the server returns a 400, 404 or 501 HTTP error code then the homeserver +should assume that the identity server doesn't support the `/unbind` API, unless +it returns a specific matrix error response (i.e. the body is a JSON object with +`error` and `errcode` fields). + +The identity server should authenticate the request in one of two ways: + +1. The request is signed by the homeserver which controls the `user_id`. +2. The request includes the `sid` and `client_secret` params (as per `/bind`), + which proves ownership of the given 3PID. + +Example: + +``` +POST /_matrix/identity/api/v1/unbind HTTP/1.1 + +{ + "mxid": "@foobar:example.com", + "threepid": { + "medium": "email", + "address": "foobar@example.com" + } +} + +HTTP/1.1 200 OK + +{} +``` + +# Trade-offs + +A homeserver can unbind any 3PID associated with one of its users, and +specifically does not require a re-validation of control of the 3PID. This means +that users have to trust that their homeserver will not arbitrarily remove valid +3PIDs, however users must already trust their homeserver to a large extent. The +flip side is that this provides a mechanism for homeservers and users to remove +3PIDs directed at their user IDs that they no longer (or never did) have control +over. + +Removing a 3PID does not require user interactive auth (UIA), which opens a +potential attack whereby a logged in device can remove all associated 3PIDs and +then log out all devices. If the user has forgotten their password they would no +longer be able to reset their password via a 3PID (e.g. email), resulting in +losing access to their account. However, given that clients and servers have +implemented these APIs in the wild this is considered a sufficient edge case +that adding UIA is unlikely to be worthwhile. diff --git a/scripts/gendoc.py b/scripts/gendoc.py index 8310ad58..72e3047c 100755 --- a/scripts/gendoc.py +++ b/scripts/gendoc.py @@ -457,7 +457,7 @@ def main(targets, dest_dir, keep_intermediates, substitutions): rst_file = os.path.join(tmp_dir, "spec_%s.rst" % (target_name,)) if version_label: - d = os.path.join(dest_dir, target_name) + d = os.path.join(dest_dir, target_name.split('@')[0]) if not os.path.exists(d): os.mkdir(d) html_file = os.path.join(d, "%s.html" % version_label) diff --git a/scripts/templating/matrix_templates/sections.py b/scripts/templating/matrix_templates/sections.py index 185970c9..5961aa24 100644 --- a/scripts/templating/matrix_templates/sections.py +++ b/scripts/templating/matrix_templates/sections.py @@ -17,8 +17,11 @@ from batesian.sections import Sections import inspect import json import os +import logging +logger = logging.getLogger(__name__) + class MatrixSections(Sections): # pass through git ver so it'll be dropped in the input file @@ -28,26 +31,14 @@ class MatrixSections(Sections): def render_git_rev(self): return self.units.get("git_version")["revision"] - def render_client_server_changelog(self): - changelogs = self.units.get("changelogs") - return changelogs["client_server"] - - # TODO: We should make this a generic variable instead of having to add functions all the time. - def render_push_gateway_changelog(self): - changelogs = self.units.get("changelogs") - return changelogs["push_gateway"] - - def render_identity_service_changelog(self): - changelogs = self.units.get("changelogs") - return changelogs["identity_service"] - - def render_server_server_changelog(self): - changelogs = self.units.get("changelogs") - return changelogs["server_server"] - - def render_application_service_changelog(self): + def render_changelogs(self): + rendered = {} changelogs = self.units.get("changelogs") - return changelogs["application_service"] + for spec, changelog_text in changelogs.items(): + spec_var = "%s_changelog" % spec + logger.info("Rendering changelog for spec: %s" % spec) + rendered[spec_var] = changelog_text + return rendered def _render_events(self, filterFn, sortFn): template = self.env.get_template("events.tmpl") diff --git a/scripts/templating/matrix_templates/units.py b/scripts/templating/matrix_templates/units.py index 11a9d441..c1755119 100644 --- a/scripts/templating/matrix_templates/units.py +++ b/scripts/templating/matrix_templates/units.py @@ -774,7 +774,7 @@ class MatrixUnits(Units): "Privileged server plugins", ), TypeTableRow( "`Identity Service API `_", - "unstable", + is_ver, "Mapping of third party IDs to Matrix IDs", ), TypeTableRow( "`Push Gateway API `_", @@ -903,74 +903,112 @@ class MatrixUnits(Units): return schema - def load_changelogs(self): + def load_changelogs(self, substitutions): + """Loads the changelog unit for later rendering in a section. + + Args: + substitutions: dict of variable name to value. Provided by the gendoc script. + + Returns: + A dict of API name ("client_server", for example) to changelog. + """ changelogs = {} - for f in os.listdir(CHANGELOG_DIR): - if not f.endswith(".rst"): - continue - path = os.path.join(CHANGELOG_DIR, f) - name = f[:-4] - - # If there's a directory with the same name, we'll try to generate - # a towncrier changelog and prepend it to the general changelog. - tc_path = os.path.join(CHANGELOG_DIR, name) - tc_lines = [] - if os.path.isdir(tc_path): - logger.info("Generating towncrier changelog for: %s" % name) - p = subprocess.Popen( - ['towncrier', '--version', 'Unreleased Changes', '--name', name, '--draft'], - cwd=tc_path, - stderr=subprocess.PIPE, - stdout=subprocess.PIPE, - ) - stdout, stderr = p.communicate() - if p.returncode != 0: - # Something broke - dump as much information as we can - logger.error("Towncrier exited with code %s" % p.returncode) - logger.error(stdout.decode('UTF-8')) - logger.error(stderr.decode('UTF-8')) - raw_log = "" - else: - raw_log = stdout.decode('UTF-8') + # The APIs and versions we'll prepare changelogs for. We use the substitutions + # to ensure that we pick up the right version for generated documentation. This + # defaults to "unstable" as a version for incremental generated documentation (CI). + prepare_versions = { + "server_server": substitutions.get("%SERVER_RELEASE_LABEL%", "unstable"), + "client_server": substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable"), + "identity_service": substitutions.get("%IDENTITY_RELEASE_LABEL%", "unstable"), + "push_gateway": substitutions.get("%PUSH_GATEWAY_RELEASE_LABEL%", "unstable"), + "application_service": substitutions.get("%APPSERVICE_RELEASE_LABEL%", "unstable"), + } - # This is a bit of a hack, but it does mean that the log at least gets *something* - # to tell us it broke - if not raw_log.startswith("Unreleased Changes"): - logger.error("Towncrier appears to have failed to generate a changelog") - logger.error(raw_log) - raw_log = "" - tc_lines = raw_log.splitlines() + # Changelogs are split into two places: towncrier for the unstable changelog and + # the RST file for historical versions. If the prepare_versions dict above has + # a version other than "unstable" specified for an API, we'll use the historical + # changelog and otherwise generate the towncrier log in-memory. - title_part = None + for api_name, target_version in prepare_versions.items(): + logger.info("Generating changelog for %s at %s" % (api_name, target_version,)) changelog_lines = [] - with open(path, "r", encoding="utf-8") as f: - lines = f.readlines() + if target_version == 'unstable': + # generate towncrier log + changelog_lines = self._read_towncrier_changelog(api_name) + else: + # read in the existing RST changelog + changelog_lines = self._read_rst_changelog(api_name) + + # Parse the changelog lines to find the header we're looking for and therefore + # the changelog body. prev_line = None - for line in (tc_lines + lines): + title_part = None + changelog_body_lines = [] + for line in changelog_lines: if prev_line is None: prev_line = line continue - if not title_part: - # find the title underline (at least 3 =) - if re.match("^[=]{3,}$", line.strip()): - title_part = prev_line - continue - prev_line = line - else: # have title, get body (stop on next title or EOF) - if re.match("^[=]{3,}$", line.strip()): - # we added the title in the previous iteration, pop it - # then bail out. - changelog_lines.pop() - break - # Don't generate subheadings (we'll keep the title though) - if re.match("^[-]{3,}$", line.strip()): - continue - changelog_lines.append(" " + line + '\n') - changelogs[name] = "".join(changelog_lines) + if re.match("^[=]{3,}$", line.strip()): + # the last line was a header - use that as our new title_part + title_part = prev_line.strip() + continue + if re.match("^[-]{3,}$", line.strip()): + # the last line is a subheading - drop this line because it's the underline + # and that causes problems with rendering. We'll keep the header text though. + continue + if line.strip().startswith(".. "): + # skip comments + continue + if title_part == target_version: + # if we made it this far, append the line to the changelog body. We indent it so + # that it renders correctly in the section. We also add newlines so that there's + # intentionally blank lines that make rst2html happy. + changelog_body_lines.append(" " + line + '\n') + + if len(changelog_body_lines) > 0: + changelogs[api_name] = "".join(changelog_body_lines) + else: + raise ValueError("No changelog for %s at %s" % (api_name, target_version,)) + # return our `dict[api_name] => changelog` as the last step. return changelogs + def _read_towncrier_changelog(self, api_name): + tc_path = os.path.join(CHANGELOG_DIR, api_name) + if os.path.isdir(tc_path): + logger.info("Generating towncrier changelog for: %s" % api_name) + p = subprocess.Popen( + ['towncrier', '--version', 'unstable', '--name', api_name, '--draft'], + cwd=tc_path, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + stdout, stderr = p.communicate() + if p.returncode != 0: + # Something broke - dump as much information as we can + logger.error("Towncrier exited with code %s" % p.returncode) + logger.error(stdout.decode('UTF-8')) + logger.error(stderr.decode('UTF-8')) + raw_log = "" + else: + raw_log = stdout.decode('UTF-8') + + # This is a bit of a hack, but it does mean that the log at least gets *something* + # to tell us it broke + if not raw_log.startswith("unstable"): + logger.error("Towncrier appears to have failed to generate a changelog") + logger.error(raw_log) + raw_log = "" + return raw_log.splitlines() + return [] + + def _read_rst_changelog(self, api_name): + logger.info("Reading changelog RST for %s" % api_name) + rst_path = os.path.join(CHANGELOG_DIR, "%s.rst" % api_name) + with open(rst_path, 'r', encoding="utf-8") as f: + return f.readlines() + def load_unstable_warnings(self, substitutions): warning = """ .. WARNING:: diff --git a/specification/appendices/base64.rst b/specification/appendices/base64.rst index d046e0fc..a918c2f9 100644 --- a/specification/appendices/base64.rst +++ b/specification/appendices/base64.rst @@ -52,6 +52,6 @@ Examples of strings encoded using unpadded Base64:: UNPADDED_BASE64("foobar") = "Zm9vYmFy" When decoding Base64, implementations SHOULD accept input with or without -padding characters whereever possible, to ensure maximum interoperability. +padding characters wherever possible, to ensure maximum interoperability. .. _`RFC 4648`: https://tools.ietf.org/html/rfc4648 diff --git a/specification/appendices/identifier_grammar.rst b/specification/appendices/identifier_grammar.rst index bb5f9297..729246d0 100644 --- a/specification/appendices/identifier_grammar.rst +++ b/specification/appendices/identifier_grammar.rst @@ -16,6 +16,12 @@ Identifier Grammar ------------------ +Some identifiers are specific to given room versions, please refer to the +`room versions specification`_ for more information. + +.. _`room versions specification`: index.html#room-versions + + Server Name ~~~~~~~~~~~ @@ -28,7 +34,7 @@ following grammar:: server_name = hostname [ ":" port ] - port = *DIGIT + port = 1*5DIGIT hostname = IPv4address / "[" IPv6address "]" / dns-name @@ -78,38 +84,6 @@ Some recommendations for a choice of server name follow: * The length of the complete server name should not exceed 230 characters. * Server names should not use upper-case characters. - -Room Versions -~~~~~~~~~~~~~ - -Room versions are used to change properties of rooms that may not be compatible -with other servers. For example, changing the rules for event authorization would -cause older servers to potentially end up in a split-brain situation due to them -not understanding the new rules. - -A room version is defined as a string of characters which MUST NOT exceed 32 -codepoints in length. Room versions MUST NOT be empty and SHOULD contain only -the characters ``a-z``, ``0-9``, ``.``, and ``-``. - -Room versions are not intended to be parsed and should be treated as opaque -identifiers. Room versions consisting only of the characters ``0-9`` and ``.`` -are reserved for future versions of the Matrix protocol. - -The complete grammar for a legal room version is:: - - room_version = 1*room_version_char - room_version_char = DIGIT - / %x61-7A ; a-z - / "-" / "." - -Examples of valid room versions are: - -* ``1`` (would be reserved by the Matrix protocol) -* ``1.2`` (would be reserved by the Matrix protocol) -* ``1.2-beta`` -* ``com.example.version`` - - Common Identifier Format ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -229,7 +203,7 @@ a homeserver creating a user ID for a new user based on the username passed to Implementations are free to do this mapping however they choose. Since the user ID is opaque except to the implementation which created it, the only -requirement is that the implemention can perform the mapping +requirement is that the implementation can perform the mapping consistently. However, we suggest the following algorithm: 1. Encode character strings as UTF-8. @@ -260,18 +234,17 @@ A room has exactly one room ID. A room ID has the format:: !opaque_id:domain -An event has exactly one event ID. An event ID has the format:: +An event has exactly one event ID. The format of an event ID depends upon the +`room version specification `_. - $opaque_id:domain - -The ``domain`` of a room/event ID is the `server name`_ of the homeserver which +The ``domain`` of a room ID is the `server name`_ of the homeserver which created the room/event. The domain is used only for namespacing to avoid the risk of clashes of identifiers between different homeservers. There is no implication that the room or event in question is still available at the corresponding homeserver. Event IDs and Room IDs are case-sensitive. They are not meant to be human -readable. +readable. They are intended to be treated as fully opaque strings by clients. .. TODO-spec What is the grammar for the opaque part? https://matrix.org/jira/browse/SPEC-389 @@ -327,7 +300,7 @@ matrix.to navigation .. NOTE:: This namespacing is in place pending a ``matrix://`` (or similar) URI scheme. - This is **not** meant to be interpreted as an available web service - see + This is **not** meant to be interpreted as an available web service - see below for more details. Rooms, users, aliases, and groups may be represented as a "matrix.to" URI. @@ -343,21 +316,34 @@ in RFC 3986: The identifier may be a room ID, room alias, user ID, or group ID. The extra parameter is only used in the case of permalinks where an event ID is referenced. The matrix.to URI, when referenced, must always start with ``https://matrix.to/#/`` -followed by the identifier. +followed by the identifier. Clients should not rely on matrix.to URIs falling back to a web server if accessed and instead should perform some sort of action within the client. For example, if the user were to click on a matrix.to URI for a room alias, the client may open a view for the user to participate in the room. +The components of the matrix.to URI (```` and ````) +are to be percent-encoded as per RFC 3986. + Examples of matrix.to URIs are: -* Room alias: ``https://matrix.to/#/#somewhere:example.org`` -* Room: ``https://matrix.to/#/!somewhere:example.org`` -* Permalink by room: ``https://matrix.to/#/!somewhere:example.org/$event:example.org`` -* Permalink by room alias: ``https://matrix.to/#/#somewhere:example.org/$event:example.org`` -* User: ``https://matrix.to/#/@alice:example.org`` -* Group: ``https://matrix.to/#/+example:example.org`` +* Room alias: ``https://matrix.to/#/%23somewhere%3Aexample.org`` +* Room: ``https://matrix.to/#/!somewhere%3Aexample.org`` +* Permalink by room: ``https://matrix.to/#/!somewhere%3Aexample.org/%24event%3Aexample.org`` +* Permalink by room alias: ``https://matrix.to/#/#somewhere:example.org/%24event%3Aexample.org`` +* User: ``https://matrix.to/#/%40alice%3Aexample.org`` +* Group: ``https://matrix.to/#/%2Bexample%3Aexample.org`` + +.. Note:: + Historically, clients have not produced URIs which are fully encoded. Clients should + try to interpret these cases to the best of their ability. For example, an unencoded + room alias should still work within the client if possible. + +.. Note:: + Clients should be aware that decoding a matrix.to URI may result in extra slashes + appearing due to some `room versions `_. These slashes + should normally be encoded when producing matrix.to URIs, however. .. Note:: Room ID permalinks are unroutable as there is no reliable domain to send requests diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 4df83814..f9f815f7 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -73,7 +73,7 @@ MUST be encoded as UTF-8. Clients are authenticated using opaque ``access_token`` strings (see `Client Authentication`_ for details), passed as a query string parameter on all requests. -The names of the API endponts for the HTTP transport follow a convention of +The names of the API endpoints for the HTTP transport follow a convention of using underscores to separate words (for example ``/delete_devices``). The key names in JSON objects passed over the API also follow this convention. @@ -158,7 +158,7 @@ Other error codes the client might encounter are: Sent when the room alias given to the ``createRoom`` API is already in use. :``M_INVALID_ROOM_STATE``: - Sent when the intial state given to the ``createRoom`` API is invalid. + Sent when the initial state given to the ``createRoom`` API is invalid. :``M_THREEPID_IN_USE``: Sent when a threepid given to an API cannot be used because the same threepid is already in use. @@ -210,10 +210,18 @@ Other error codes the client might encounter are: The resource being requested is reserved by an application service, or the application service making the request has not created the resource. +:``M_RESOURCE_LIMIT_EXCEEDED``: + The request cannot be completed because the homeserver has reached a resource + limit imposed on it. For example, a homeserver held in a shared hosting environment + may reach a resource limit if it starts using too much memory or disk space. The + error MUST have an ``admin_contact`` field to provide the user receiving the error + a place to reach out to. Typically, this error will appear on routes which attempt + to modify state (eg: sending messages, account data, etc) and not routes which only + read state (eg: ``/sync``, get account data, etc). + .. TODO: More error codes (covered by other issues) .. * M_CONSENT_NOT_GIVEN - GDPR: https://github.com/matrix-org/matrix-doc/issues/1512 .. * M_CANNOT_LEAVE_SERVER_NOTICE_ROOM - GDPR: https://github.com/matrix-org/matrix-doc/issues/1254 -.. * M_RESOURCE_LIMIT_EXCEEDED - Limits: https://github.com/matrix-org/matrix-doc/issues/1504 .. _sect:txn_ids: @@ -636,7 +644,7 @@ To use this authentication type, clients should submit an auth dict as follows: where the ``identifier`` property is a user identifier object, as described in `Identifier types`_. -For example, to authenticate using the user's Matrix ID, clients whould submit: +For example, to authenticate using the user's Matrix ID, clients would submit: .. code:: json @@ -650,7 +658,7 @@ For example, to authenticate using the user's Matrix ID, clients whould submit: "session": "" } -Alternatively reply using a 3pid bound to the user's account on the homeserver +Alternatively reply using a 3PID bound to the user's account on the homeserver using the |/account/3pid|_ API rather then giving the ``user`` explicitly as follows: @@ -667,7 +675,7 @@ follows: "session": "" } -In the case that the homeserver does not know about the supplied 3pid, the +In the case that the homeserver does not know about the supplied 3PID, the homeserver must respond with 403 Forbidden. Google ReCaptcha @@ -928,10 +936,10 @@ Third-party ID :Type: ``m.id.thirdparty`` :Description: - The user is identified by a third-party identifer in canonicalised form. + The user is identified by a third-party identifier in canonicalised form. -A client can identify a user using a 3pid associated with the user's account on -the homeserver, where the 3pid was previously associated using the +A client can identify a user using a 3PID associated with the user's account on +the homeserver, where the 3PID was previously associated using the |/account/3pid|_ API. See the `3PID Types`_ Appendix for a list of Third-party ID media. @@ -987,7 +995,7 @@ request as follows: "password": "" } -Alternatively, a client can use a 3pid bound to the user's account on the +Alternatively, a client can use a 3PID bound to the user's account on the homeserver using the |/account/3pid|_ API rather then giving the ``user`` explicitly, as follows: @@ -1002,7 +1010,7 @@ explicitly, as follows: "password": "" } -In the case that the homeserver does not know about the supplied 3pid, the +In the case that the homeserver does not know about the supplied 3PID, the homeserver must respond with ``403 Forbidden``. To log in using a login token, clients should submit a ``/login`` request as @@ -1070,6 +1078,107 @@ Current account information {{whoami_cs_http_api}} +Capabilities negotiation +------------------------ + +A homeserver may not support certain operations and clients must be able to +query for what the homeserver can and can't offer. For example, a homeserver +may not support users changing their password as it is configured to perform +authentication against an external system. + +The capabilities advertised through this system are intended to advertise +functionality which is optional in the API, or which depend in some way on +the state of the user or server. This system should not be used to advertise +unstable or experimental features - this is better done by the ``/versions`` +endpoint. + +Some examples of what a reasonable capability could be are: + +* Whether the server supports user presence. + +* Whether the server supports optional features, such as the user or room + directories. + +* The rate limits or file type restrictions imposed on clients by the server. + +Some examples of what should **not** be a capability are: + +* Whether the server supports a feature in the ``unstable`` specification. + +* Media size limits - these are handled by the ``/media/%CLIENT_MAJOR_VERSION%/config`` + API. + +* Optional encodings or alternative transports for communicating with the + server. + +Capabilities prefixed with ``m.`` are reserved for definition in the Matrix +specification while other values may be used by servers using the Java package +naming convention. The capabilities supported by the Matrix specification are +defined later in this section. + +{{capabilities_cs_http_api}} + + +``m.change_password`` capability +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This capability has a single flag, ``enabled``, which indicates whether or not +the user can use the ``/account/password`` API to change their password. If not +present, the client should assume that password changes are possible via the +API. When present, clients SHOULD respect the capability's ``enabled`` flag +and indicate to the user if they are unable to change their password. + +An example of the capability API's response for this capability is:: + + { + "capabilities": { + "m.change_password": { + "enabled": false + } + } + } + + +``m.room_versions`` capability +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This capability describes the default and available room versions a server +supports, and at what level of stability. Clients should make use of this +capability to determine if users need to be encouraged to upgrade their rooms. + +An example of the capability API's response for this capability is:: + + { + "capabilities": { + "m.room_versions": { + "default": "1", + "available": { + "1": "stable", + "2": "stable", + "3": "unstable", + "custom-version": "unstable" + } + } + } + } + +This capability mirrors the same restrictions of `room versions`_ to describe +which versions are stable and unstable. Clients should assume that the ``default`` +version is ``stable``. Any version not explicitly labelled as ``stable`` in the +``available`` versions is to be treated as ``unstable``. For example, a version +listed as ``future-stable`` should be treated as ``unstable``. + +The ``default`` version is the version the server is using to create new rooms. +Clients should encourage users with sufficient permissions in a room to upgrade +their room to the ``default`` version when the room is using an ``unstable`` +version. + +When this capability is not listed, clients should use ``"1"`` as the default +and only stable ``available`` room version. + +.. _`room versions`: ../index.html#room-versions + + Pagination ---------- @@ -1166,7 +1275,21 @@ point in time:: [E0]->[E1]->[E2]->[E3]->[E4]->[E5] +.. WARNING:: + + The format of events can change depending on room version. Check the + `room version specification`_ for specific details on what to expect for + event formats. Examples contained within the client-server specification + are expected to be compatible with all specified room versions, however + some differences may still apply. + + For this version of the specification, clients only need to worry about + the event ID format being different depending on room version. Clients + should not be parsing the event ID, and instead be treating it as an + opaque string. No changes should be required to support the currently + available room versions. +.. _`room version specification`: ../index.html#room-versions Types of room events ~~~~~~~~~~~~~~~~~~~~ diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 1bcdeff0..88c7a8ab 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -22,10 +22,10 @@ Identity Service API The Matrix client-server and server-server APIs are largely expressed in Matrix user identifiers. From time to time, it is useful to refer to users by other -("third-party") identifiers, or "3pid"s, e.g. their email address or phone +("third-party") identifiers, or "3PID"s, e.g. their email address or phone number. This Identity Service Specification describes how mappings between third-party identifiers and Matrix user identifiers can be established, -validated, and used. This description technically may apply to any 3pid, but in +validated, and used. This description technically may apply to any 3PID, but in practice has only been applied specifically to email addresses and phone numbers. .. contents:: Table of Contents @@ -150,9 +150,9 @@ Identity is a privacy-sensitive issue. While the identity server exists to provide identity information, access should be restricted to avoid leaking potentially sensitive data. In particular, being able to construct large-scale connections between identities should be avoided. To this end, in general APIs -should allow a 3pid to be mapped to a Matrix user identity, but not in the other -direction (i.e. one should not be able to get all 3pids associated with a Matrix -user ID, or get all 3pids associated with a 3pid). +should allow a 3PID to be mapped to a Matrix user identity, but not in the other +direction (i.e. one should not be able to get all 3PIDs associated with a Matrix +user ID, or get all 3PIDs associated with a 3PID). Web browser clients ------------------- @@ -204,10 +204,10 @@ Establishing associations The flow for creating an association is session-based. -Within a session, one may prove that one has ownership of a 3pid. +Within a session, one may prove that one has ownership of a 3PID. Once this has been established, the user can form an association between that -3pid and a Matrix user ID. Note that this association is only proved one way; -a user can associate *any* Matrix user ID with a validated 3pid, +3PID and a Matrix user ID. Note that this association is only proved one way; +a user can associate *any* Matrix user ID with a validated 3PID, i.e. I can claim that any email address I own is associated with @billg:microsoft.com. @@ -255,11 +255,11 @@ General Invitation storage ------------------ -An identity server can store pending invitations to a user's 3pid, which will -be retrieved and can be either notified on or look up when the 3pid is +An identity server can store pending invitations to a user's 3PID, which will +be retrieved and can be either notified on or look up when the 3PID is associated with a Matrix user ID. -At a later point, if the owner of that particular 3pid binds it with a Matrix user +At a later point, if the owner of that particular 3PID binds it with a Matrix user ID, the identity server will attempt to make an HTTP POST to the Matrix user's homeserver via the `/3pid/onbind`_ endpoint. The request MUST be signed with a long-term private key for the identity server. @@ -279,4 +279,4 @@ this isn't possible. .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 .. _`3PID Types`: ../appendices.html#pid-types .. _`Signing JSON`: ../appendices.html#signing-json -.. _`/3pid/onbind`: ../server_server/unstable.html#put-matrix-federation-v1-3pid-onbind +.. _`/3pid/onbind`: ../server_server/r0.1.1.html#put-matrix-federation-v1-3pid-onbind diff --git a/specification/index.rst b/specification/index.rst index bb918739..19c5ad22 100644 --- a/specification/index.rst +++ b/specification/index.rst @@ -40,10 +40,15 @@ The specification consists of the following parts: {{apis}} +Additionally, this introduction page contains the key baseline information required to +understand the specific APIs, including the sections on `room versions`_ +and `overall architecture <#architecture>`_. + The `Appendices `_ contain supplemental information not specific to one of the above APIs. -The `Matrix Client-Server API Swagger Viewer `_ is useful for browsing the Client-Server API. +The `Matrix Client-Server API Swagger Viewer `_ +is useful for browsing the Client-Server API. Introduction to the Matrix APIs ------------------------------- @@ -128,6 +133,8 @@ To propose a change to the Matrix Spec, see the explanations at `Proposals for Spec Changes to Matrix `_. +.. _`architecture`: + Architecture ------------ @@ -189,7 +196,7 @@ allocated the account and has the form:: @localpart:domain -See `'Identifier Grammar' the appendices `_ for full details of +See `'Identifier Grammar' in the appendices `_ for full details of the structure of user IDs. Devices @@ -315,8 +322,8 @@ The following conceptual diagram shows an | Content: { JSON object } | |....................................| -Federation maintains *shared data structures* per-room between multiple home -servers. The data is split into ``message events`` and ``state events``. +Federation maintains *shared data structures* per-room between multiple +homeservers. The data is split into ``message events`` and ``state events``. Message events: These describe transient 'once-off' activity in a room such as an @@ -418,6 +425,75 @@ dedicated API. The API is symmetrical to managing Profile data. Would it really be overengineered to use the same API for both profile & private user data, but with different ACLs? +.. _`room versions`: + +Room Versions +------------- + +Rooms are central to how Matrix operates, and have strict rules for what +is allowed to be contained within them. Rooms can also have various +algorithms that handle different tasks, such as what to do when two or +more events collide in the underlying DAG. To allow rooms to be improved +upon through new algorithms or rules, "room versions" are employed to +manage a set of expectations for each room. New room versions are assigned +as needed. + +There is no implicit ordering or hierarchy to room versions, and their principles +are immutable once placed in the specification. Although there is a recommended +set of versions, some rooms may benefit from features introduced by other versions. +Rooms move between different versions by "upgrading" to the desired version. Due +to versions not being ordered or hierarchical, this means a room can "upgrade" +from version 2 to version 1, if it is so desired. + +Room version grammar +~~~~~~~~~~~~~~~~~~~~ + +Room versions are used to change properties of rooms that may not be compatible +with other servers. For example, changing the rules for event authorization would +cause older servers to potentially end up in a split-brain situation due to not +understanding the new rules. + +A room version is defined as a string of characters which MUST NOT exceed 32 +codepoints in length. Room versions MUST NOT be empty and SHOULD contain only +the characters ``a-z``, ``0-9``, ``.``, and ``-``. + +Room versions are not intended to be parsed and should be treated as opaque +identifiers. Room versions consisting only of the characters ``0-9`` and ``.`` +are reserved for future versions of the Matrix protocol. + +The complete grammar for a legal room version is:: + + room_version = 1*room_version_char + room_version_char = DIGIT + / %x61-7A ; a-z + / "-" / "." + +Examples of valid room versions are: + +* ``1`` (would be reserved by the Matrix protocol) +* ``1.2`` (would be reserved by the Matrix protocol) +* ``1.2-beta`` +* ``com.example.version`` + +Complete list of room versions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Room versions are divided into two distinct groups: stable and unstable. Stable +room versions may be used by rooms safely. Unstable room versions are everything +else which is either not listed in the specification or flagged as unstable for +some other reason. Versions can switch between stable and unstable periodically +for a variety of reasons, including discovered security vulnerabilities and age. + +Clients should not ask room administrators to upgrade their rooms if the room is +running a stable version. Servers SHOULD use room version 1 as the default room +version when creating new rooms. + +The available room versions are: + +* `Version 1 `_ - **Stable**. The current version of most rooms. +* `Version 2 `_ - **Stable**. Implements State Resolution Version 2. +* `Version 3 `_ - **Stable**. Introduces events whose IDs are the event's hash. + Specification Versions ---------------------- diff --git a/specification/modules/account_data.rst b/specification/modules/account_data.rst index f0bf285f..a67e503a 100644 --- a/specification/modules/account_data.rst +++ b/specification/modules/account_data.rst @@ -27,7 +27,7 @@ The account_data may be either global or scoped to a particular rooms. Events ------ -The client recieves the account data as events in the ``account_data`` sections +The client receives the account data as events in the ``account_data`` sections of a ``/sync``. These events can also be received in a ``/events`` response or in the diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index 72bfae35..59d241b6 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -291,8 +291,8 @@ v string **Required.** Version of the encrypted attachments ========= ========= ============================================================ Parameter Type Description ========= ========= ============================================================ -key string **Required.** Key type. Must be ``oct``. -key_opts [string] **Required.** Key operations. Must at least contain +kty string **Required.** Key type. Must be ``oct``. +key_ops [string] **Required.** Key operations. Must at least contain ``encrypt`` and ``decrypt``. alg string **Required.** Algorithm. Must be ``A256CTR``. k string **Required.** The key, encoded as urlsafe unpadded base64. @@ -450,7 +450,7 @@ previously-received ``request`` message with the same ``request_id`` and .. NOTE:: Key sharing can be a big attack vector, thus it must be done very carefully. - A reasonable stategy is for a user's client to only send keys requested by the + A reasonable strategy is for a user's client to only send keys requested by the verified devices of the same user. Key exports @@ -496,18 +496,9 @@ passphrase, and is created as follows: Key export format <<<<<<<<<<<<<<<<< -The exported sessions are formatted as a JSON object of type ``ExportData`` +The exported sessions are formatted as a JSON array of ``SessionData`` objects described as follows: -``ExportData`` - -=============== ================= ============================================== -Parameter Type Description -=============== ================= ============================================== -sessions ``[SessionData]`` Required. The sessions that are being - exported. -=============== ================= ============================================== - ``SessionData`` .. table:: @@ -529,7 +520,7 @@ sessions ``[SessionData]`` Required. The sessions that are being device which initiated the session originally. sender_claimed_keys {string: Required. The Ed25519 key of the - integer} device which initiated the session + string} device which initiated the session originally. session_id string Required. The ID of the session. session_key string Required. The key for the session. @@ -696,7 +687,7 @@ An event encrypted using Megolm has the following format: "sender_key": "", "device_id": "", "session_id": "", - "ciphertext": "" + "ciphertext": "" } } diff --git a/specification/modules/instant_messaging.rst b/specification/modules/instant_messaging.rst index 2adade5e..86daa700 100644 --- a/specification/modules/instant_messaging.rst +++ b/specification/modules/instant_messaging.rst @@ -185,25 +185,14 @@ reduced through clients making use of the transaction ID they used to send a particular event. The transaction ID used will be included in the event's ``unsigned`` data as ``transaction_id`` when it arrives through the event stream. -Clients unable to make use of the transaction ID are more likely to experience -flickering due to the following two scenarios, however the effect can be mitigated -to a degree: - -- The client sends a message and the remote echo arrives on the event stream - *after* the request to send the message completes. -- The client sends a message and the remote echo arrives on the event stream - *before* the request to send the message completes. - -In the first scenario, the client will receive an event ID when the request to -send the message completes. This ID can be used to identify the duplicate event -when it arrives on the event stream. However, in the second scenario, the event -arrives before the client has obtained an event ID. This makes it impossible to -identify it as a duplicate event. This results in the client displaying the -message twice for a fraction of a second before the the original request to send -the message completes. Once it completes, the client can take remedial actions -to remove the duplicate event by looking for duplicate event IDs. A future version -of the client-server API will resolve this by attaching the transaction ID of the -sending request to the event itself. +Clients unable to make use of the transaction ID are likely to experience +flickering when the remote echo arrives on the event stream *before* +the request to send the message completes. In that case the event +arrives before the client has obtained an event ID, making it impossible to +identify it as a remote echo. This results in the client displaying the message +twice for some time (depending on the server responsiveness) before the original +request to send the message completes. Once it completes, the client can take +remedial actions to remove the duplicate event by looking for duplicate event IDs. Calculating the display name for a user diff --git a/specification/modules/mentions.rst b/specification/modules/mentions.rst index 4501b776..dc078f3b 100644 --- a/specification/modules/mentions.rst +++ b/specification/modules/mentions.rst @@ -44,7 +44,7 @@ In addition to using the appropriate ``matrix.to URI`` for the mention, clients should use the following guidelines when making mentions in events to be sent: -* When mentioning users, use the user's potentially ambigious display name for +* When mentioning users, use the user's potentially ambiguous display name for the anchor's text. If the user does not have a display name, use the user's ID. diff --git a/specification/modules/presence.rst b/specification/modules/presence.rst index 822b298d..53a33550 100644 --- a/specification/modules/presence.rst +++ b/specification/modules/presence.rst @@ -26,14 +26,8 @@ Each user has the concept of presence information. This encodes: This information is collated from both per-device (``online``, ``idle``, ``last_active``) and per-user (status) data, aggregated by the user's homeserver -and transmitted as an ``m.presence`` event. This is one of the few events which -are sent *outside the context of a room*. Presence events are sent to all users -who subscribe to this user's presence through a presence list or by sharing -membership of a room. - -A presence list is a list of user IDs whose presence the user wants to follow. -To be added to this list, the user being added must be invited by the list owner -who must accept the invitation. +and transmitted as an ``m.presence`` event. Presence events are sent to +interested parties where users share a room membership. User's presence state is represented by the ``presence`` key, which is an enum of one of the following: @@ -53,17 +47,10 @@ Events Client behaviour ---------------- -Clients can manually set/get their presence/presence list using the HTTP APIs -listed below. +Clients can manually set/get their presence using the HTTP APIs listed below. {{presence_cs_http_api}} -Server behaviour ----------------- - -Each user's homeserver stores a "presence list" per user. Once a user accepts -a presence list, both user's HSes must track the subscription. - Last active ago ~~~~~~~~~~~~~~~ The server maintains a timestamp of the last time it saw a pro-active event from diff --git a/specification/modules/push.rst b/specification/modules/push.rst index 008eaa8b..9830729a 100644 --- a/specification/modules/push.rst +++ b/specification/modules/push.rst @@ -660,6 +660,8 @@ When a user changes their push rules a ``m.push_rules`` event is sent to all clients in the ``account_data`` section of their next ``/sync`` request. The content of the event is the current push rules for the user. +{{m_push_rules_event}} + Examples ++++++++ diff --git a/specification/modules/room_upgrades.rst b/specification/modules/room_upgrades.rst new file mode 100644 index 00000000..f1861f72 --- /dev/null +++ b/specification/modules/room_upgrades.rst @@ -0,0 +1,78 @@ +.. Copyright 2019 New Vector Ltd +.. +.. 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. + +Room Upgrades +============= + +.. _module:room-upgrades: + +From time to time, a room may need to be upgraded to a different room version for a +variety for reasons. This module defines a way for rooms to upgrade to a different +room version when needed. + +Events +------ + +{{m_room_tombstone_event}} + +Client behaviour +---------------- + +Clients which understand ``m.room.tombstone`` events and the ``predecessor`` field on +``m.room.create`` events should communicate to the user that the room was upgraded. +One way of accomplishing this would be hiding the old room from the user's room list +and showing banners linking between the old and new room - ensuring that permalinks +work when referencing the old room. Another approach may be to virtually merge the +rooms such that the old room's timeline seamlessly continues into the new timeline +without the user having to jump between the rooms. + +{{room_upgrades_cs_http_api}} + +Server behaviour +---------------- + +When the client requests to upgrade a known room to a known version, the server: + +1. Checks that the user has permission to send ``m.room.tombstone`` events in the room. +2. Creates a replacement room with a ``m.room.create`` event containing a ``predecessor`` + field and the applicable ``room_version``. +3. Replicates transferable state events to the new room. The exact details for what is + transferred is left as an implementation detail, however the recommended state events + to transfer are: + + * ``m.room.server_acl`` + * ``m.room.encryption`` + * ``m.room.name`` + * ``m.room.avatar`` + * ``m.room.topic`` + * ``m.room.guest_access`` + * ``m.room.history_visibility`` + * ``m.room.join_rules`` + * ``m.room.power_levels`` + + Membership events should not be transferred to the new room due to technical limitations + of servers not being able to impersonate people from other homeservers. Additionally, + servers should not transfer state events which are sensitive to who sent them, such as + events outside of the Matrix namespace where clients may rely on the sender to match + certain criteria. + +4. Moves any local aliases to the new room. +5. Sends a ``m.room.tombstone`` event to the old room to indicate that it is not intended + to be used any further. +6. If possible, the power levels in the old room should also be modified to prevent sending + of events and inviting new users. For example, setting ``events_default`` and ``invite`` + to the greater of ``50`` and ``users_default + 1``. + +When a user joins the new room, the server should automatically transfer/replicate some of +the user's personalized settings such as notifications, tags, etc. diff --git a/specification/modules/tags.rst b/specification/modules/tags.rst index 739ead2c..a4b0becf 100644 --- a/specification/modules/tags.rst +++ b/specification/modules/tags.rst @@ -48,7 +48,7 @@ The tag namespace is defined as follows: * The namespace ``u.*`` is reserved for user-defined tags. The portion of the string after the ``u.`` is defined to be the display name of this tag. No other semantics should be inferred from tags in this namespace. -* A client or app willing to use special tags for advanced functionnality should namespace them similarly to state keys: ``tld.name.*`` +* A client or app willing to use special tags for advanced functionality should namespace them similarly to state keys: ``tld.name.*`` * Any tag in the ``tld.name.*`` form but not matching the namespace of the current client should be ignored * Any tag not matching the above rules should be interpreted as a user tag from the ``u.*`` namespace, as if the name had already had ``u.`` stripped from the start (ie. the name of the tag is used as the diff --git a/specification/modules/third_party_invites.rst b/specification/modules/third_party_invites.rst index df71d215..3e11d929 100644 --- a/specification/modules/third_party_invites.rst +++ b/specification/modules/third_party_invites.rst @@ -229,7 +229,7 @@ verification must still be performed, so the attack surface here is minimized. Security considerations ----------------------- -There are a number of privary and trust implications to this module. +There are a number of privacy and trust implications to this module. It is important for user privacy that leaking the mapping between a matrix user ID and a third party identifier is hard. In particular, being able to look up diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst index 44fcf4b1..81a3b89a 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -324,7 +324,7 @@ Name GitHub Label Description =============================== ============================= ==================================== Proposal Drafting and Feedback N/A A proposal document which is still work-in-progress but is being shared to incorporate feedback. Please prefix your proposal's title with ``[WIP]`` to make it easier for reviewers to skim their notifications list. Proposal In Review proposal-in-review A proposal document which is now ready and waiting for review by the Spec Core Team and community -Proposed Final Comment Period proposed-final-comment-period Currently awaiting signoff of a majority of team members in order to enter the final comment period +Proposed Final Comment Period proposed-final-comment-period Currently awaiting signoff of a 75% majority of team members in order to enter the final comment period Final Comment Period final-comment-period A proposal document which has reached final comment period either for merge, closure or postponement Final Commment Period Complete finished-final-comment-period The final comment period has been completed. Waiting for a demonstration implementation Spec PR Missing spec-pr-missing The proposal has been agreed, and proven with a demonstration implementation. Waiting for a PR against the Spec diff --git a/specification/push_gateway.rst b/specification/push_gateway.rst index a77d43db..cba38b6c 100644 --- a/specification/push_gateway.rst +++ b/specification/push_gateway.rst @@ -84,7 +84,7 @@ This describes the format used by "HTTP" pushers to send notifications of events to Push Gateways. If the endpoint returns an HTTP error code, the homeserver SHOULD retry for a reasonable amount of time using exponential backoff. -When pushing notifications for events, the hoemserver is expected to include all of +When pushing notifications for events, the homeserver is expected to include all of the event-related fields in the ``/notify`` request. When the homeserver is performing a push where the ``format`` is ``"event_id_only"``, only the ``event_id``, ``room_id``, ``counts``, and ``devices`` are required to be populated. diff --git a/specification/rooms/v1.rst b/specification/rooms/v1.rst new file mode 100644 index 00000000..1c7a56c4 --- /dev/null +++ b/specification/rooms/v1.rst @@ -0,0 +1,297 @@ +.. Copyright 2017,2019 New Vector Ltd +.. +.. 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. + +Room Version 1 +============== + +This room version is the first ever version for rooms, and contains the building +blocks for other room versions. + +.. contents:: Table of Contents +.. sectnum:: + +Server implementation components +-------------------------------- + +.. WARNING:: + The information contained in this section is strictly for server implementors. + Applications which use the Client-Server API are generally unaffected by the + details contained here, and can safely ignore their presence. + + +The algorithms defined here should only apply to version 1 rooms. Other algorithms +may be used by other room versions, and as such servers should be aware of which +version room they are dealing with prior to executing a given algorithm. + +.. WARNING:: + Although room version 1 is the most popular room version, it is known to have + undesirable effects. Servers implementing support for room version 1 should be + aware that restrictions should be generally relaxed and that inconsistencies + may occur until room version 2 (or later) is ready and adopted. + +State resolution +~~~~~~~~~~~~~~~~ + +.. WARNING:: + This section documents the state resolution algorithm as implemented by + Synapse as of December 2017 (and therefore the de-facto Matrix protocol). + However, this algorithm is known to have some problems. + +The room state :math:`S'(E)` after an event :math:`E` is defined in terms of +the room state :math:`S(E)` before :math:`E`, and depends on whether +:math:`E` is a state event or a message event: + +* If :math:`E` is a message event, then :math:`S'(E) = S(E)`. + +* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except + that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key`` + is replaced by :math:`E`'s ``event_id``. + +The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of +states :math:`\{ S'(E'), S'(E''), … \}` consisting of the states after each of +:math:`E`'s ``prev_event``\s :math:`\{ E', E'', … \}`. + +The *resolution* of a set of states is defined as follows. The resolved state +is built up in a number of passes; here we use :math:`R` to refer to the +results of the resolution so far. + +* Start by setting :math:`R` to the union of the states to be resolved, + excluding any *conflicting* events. + +* First we resolve conflicts between ``m.room.power_levels`` events. If there + is no conflict, this step is skipped, otherwise: + + * Assemble all the ``m.room.power_levels`` events from the states to + be resolved into a list. + + * Sort the list by ascending ``depth`` then descending ``sha1(event_id)``. + + * Add the first event in the list to :math:`R`. + + * For each subsequent event in the list, check that the event would be + allowed by the authorization rules for a room in state :math:`R`. If the + event would be allowed, then update :math:`R` with the event and continue + with the next event in the list. If it would not be allowed, stop and + continue below with ``m.room.join_rules`` events. + +* Repeat the above process for conflicts between ``m.room.join_rules`` events. + +* Repeat the above process for conflicts between ``m.room.member`` events. + +* No other events affect the authorization rules, so for all other conflicts, + just pick the event with the highest depth and lowest ``sha1(event_id)`` that + passes authentication in :math:`R` and add it to :math:`R`. + +A *conflict* occurs between states where those states have different +``event_ids`` for the same ``(state_type, state_key)``. The events thus +affected are said to be *conflicting* events. + + +Authorization rules +~~~~~~~~~~~~~~~~~~~ + +The types of state events that affect authorization are: + +- ``m.room.create`` +- ``m.room.member`` +- ``m.room.join_rules`` +- ``m.room.power_levels`` +- ``m.room.third_party_invite`` + +The rules are as follows: + +1. If type is ``m.room.create``: + + a. If it has any previous events, reject. + b. If the domain of the ``room_id`` does not match the domain of the + ``sender``, reject. + c. If ``content.room_version`` is present and is not a recognised version, + reject. + d. If ``content`` has no ``creator`` field, reject. + e. Otherwise, allow. + +#. Reject if event has ``auth_events`` that: + + a. have duplicate entries for a given ``type`` and ``state_key`` pair + #. have entries whose ``type`` and ``state_key`` don't match those + specified by the `auth events selection`_ algorithm described in the + server specification. + +#. If event does not have a ``m.room.create`` in its ``auth_events``, reject. + +#. If type is ``m.room.aliases``: + + a. If event has no ``state_key``, reject. + b. If sender's domain doesn't matches ``state_key``, reject. + c. Otherwise, allow. + +#. If type is ``m.room.member``: + + a. If no ``state_key`` key or ``membership`` key in ``content``, reject. + + #. If ``membership`` is ``join``: + + i. If the only previous event is an ``m.room.create`` + and the ``state_key`` is the creator, allow. + + #. If the ``sender`` does not match ``state_key``, reject. + + #. If the ``sender`` is banned, reject. + + #. If the ``join_rule`` is ``invite`` then allow if membership state + is ``invite`` or ``join``. + + #. If the ``join_rule`` is ``public``, allow. + + #. Otherwise, reject. + + #. If ``membership`` is ``invite``: + + i. If ``content`` has ``third_party_invite`` key: + + #. If *target user* is banned, reject. + + #. If ``content.third_party_invite`` does not have a + ``signed`` key, reject. + + #. If ``signed`` does not have ``mxid`` and ``token`` keys, reject. + + #. If ``mxid`` does not match ``state_key``, reject. + + #. If there is no ``m.room.third_party_invite`` event in the + current room state with ``state_key`` matching ``token``, reject. + + #. If ``sender`` does not match ``sender`` of the + ``m.room.third_party_invite``, reject. + + #. If any signature in ``signed`` matches any public key in the + ``m.room.third_party_invite`` event, allow. The public keys are + in ``content`` of ``m.room.third_party_invite`` as: + + #. A single public key in the ``public_key`` field. + #. A list of public keys in the ``public_keys`` field. + + #. Otherwise, reject. + + #. If the ``sender``'s current membership state is not ``join``, reject. + + #. If *target user*'s current membership state is ``join`` or ``ban``, + reject. + + #. If the ``sender``'s power level is greater than or equal to the *invite + level*, allow. + + #. Otherwise, reject. + + #. If ``membership`` is ``leave``: + + i. If the ``sender`` matches ``state_key``, allow if and only if that user's + current membership state is ``invite`` or ``join``. + + #. If the ``sender``'s current membership state is not ``join``, reject. + + #. If the *target user*'s current membership state is ``ban``, and the + ``sender``'s power level is less than the *ban level*, reject. + + #. If the ``sender``'s power level is greater than or equal to the *kick + level*, and the *target user*'s power level is less than the + ``sender``'s power level, allow. + + #. Otherwise, reject. + + #. If ``membership`` is ``ban``: + + i. If the ``sender``'s current membership state is not ``join``, reject. + + #. If the ``sender``'s power level is greater than or equal to the *ban + level*, and the *target user*'s power level is less than the + ``sender``'s power level, allow. + + #. Otherwise, reject. + + #. Otherwise, the membership is unknown. Reject. + +#. If the ``sender``'s current membership state is not ``join``, reject. + +#. If type is ``m.room.third_party_invite``: + + a. Allow if and only if ``sender``'s current power level is greater than + or equal to the *invite level*. + +#. If the event type's *required power level* is greater than the ``sender``'s power + level, reject. + +#. If the event has a ``state_key`` that starts with an ``@`` and does not match + the ``sender``, reject. + +#. If type is ``m.room.power_levels``: + + a. If ``users`` key in ``content`` is not a dictionary with keys that are + valid user IDs with values that are integers (or a string that is an + integer), reject. + + #. If there is no previous ``m.room.power_levels`` event in the room, allow. + + #. For each of the keys ``users_default``, ``events_default``, + ``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as + each entry being changed under the ``events`` or ``users`` keys: + + i. If the current value is higher than the ``sender``'s current power level, + reject. + + #. If the new value is higher than the ``sender``'s current power level, + reject. + + #. For each entry being changed under the ``users`` key, other than the + ``sender``'s own entry: + + i. If the current value is equal to the ``sender``'s current power level, + reject. + + #. Otherwise, allow. + +#. If type is ``m.room.redaction``: + + a. If the ``sender``'s power level is greater than or equal to the *redact + level*, allow. + + #. If the domain of the ``event_id`` of the event being redacted is the same + as the domain of the ``event_id`` of the ``m.room.redaction``, allow. + + #. Otherwise, reject. + +#. Otherwise, allow. + +.. NOTE:: + + Some consequences of these rules: + + * Unless you are a member of the room, the only permitted operations (apart + from the initial create/join) are: joining a public room; accepting or + rejecting an invitation to a room. + + * To unban somebody, you must have power level greater than or equal to both + the kick *and* ban levels, *and* greater than the target user's power + level. + +Event format +~~~~~~~~~~~~ + +Events in version 1 rooms have the following structure: + +{{definition_ss_pdu}} + + +.. _`auth events selection`: ../../server_server/r0.1.1.html#auth-events-selection +.. _`Signing Events`: ../../server_server/r0.1.1.html#signing-events diff --git a/specification/rooms/v2.rst b/specification/rooms/v2.rst new file mode 100644 index 00000000..247f6909 --- /dev/null +++ b/specification/rooms/v2.rst @@ -0,0 +1,204 @@ +.. Copyright 2018-2019 New Vector Ltd +.. +.. 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. + +Room Version 2 +============== + +This room version builds off of `version 1 `_ with an improved state +resolution algorithm. + +.. contents:: Table of Contents +.. sectnum:: + +Server implementation components +-------------------------------- + +.. WARNING:: + The information contained in this section is strictly for server implementors. + Applications which use the Client-Server API are generally unaffected by the + details contained here, and can safely ignore their presence. + + +Room version 2 uses the base components of `room version 1 `_, changing +only the state resolution algorithm. + + +State resolution +~~~~~~~~~~~~~~~~ + +The room state :math:`S'(E)` after an event :math:`E` is defined in terms of +the room state :math:`S(E)` before :math:`E`, and depends on whether +:math:`E` is a state event or a message event: + +* If :math:`E` is a message event, then :math:`S'(E) = S(E)`. + +* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except + that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key`` + is replaced by :math:`E`'s ``event_id``. + +The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of +states :math:`\{ S'(E_1), S'(E_2), … \}` consisting of the states after each of +:math:`E`'s ``prev_event``\s :math:`\{ E_1, E_2, … \}`, where the resolution of +a set of states is given in the algorithm below. + +Definitions ++++++++++++ + +The state resolution algorithm for version 2 rooms uses the following +definitions, given the set of room states :math:`\{ S_1, S_2, \ldots \}`: + +Power events + A *power event* is a state event with type ``m.room.power_levels`` or + ``m.room.join_rules``, or a state event with type ``m.room.member`` where the + ``membership`` is ``leave`` or ``ban`` and the ``sender`` does not match the + ``state_key``. The idea behind this is that power events are events that might + remove someone's ability to do something in the room. + +Unconflicted state map and conflicted state set + The *unconflicted state map* is the state where the value of each key exists + and is the same in each state :math:`S_i`. The *conflicted state set* is the + set of all other state events. Note that the unconflicted state map only has + one event per ``(event_type, state_key)``, whereas the conflicted state set + may have multiple events. + +Auth difference + The *auth difference* is calculated by first calculating the full auth chain + for each state :math:`S_i`, that is the union of the auth chains for each + event in :math:`S_i`, and then taking every event that doesn't appear in + every auth chain. If :math:`C_i` is the full auth chain of :math:`S_i`, then + the auth difference is :math:`\cup C_i - \cap C_i`. + +Full conflicted set + The *full conflicted set* is the union of the conflicted state set and the + auth difference. + +Reverse topological power ordering + The *reverse topological power ordering* of a set of events is the + lexicographically smallest topological ordering based on the DAG formed by + auth events. The reverse topological power ordering is ordered from earliest + event to latest. For comparing two topological orderings to determine which + is the lexicographically smallest, the following comparison relation on + events is used: for events :math:`x` and :math:`y`, :math:`x`_ with an improved event format. + +.. note: + All requirements listed in this room version specification are scoped to rooms + which actually use this room version. For example, a requirement of "all APIs must + accept the new event format" does in fact apply to all APIs, but only so much as + where the contextual room of the request is using this room version. Rooms using + other room versions should not be affected by these sweeping requirements. + +.. contents:: Table of Contents +.. sectnum:: + + +Client considerations +--------------------- + +This room version changes the format for event IDs sent to clients. Clients should be +aware that these event IDs may contain slashes and other potentially problematic +characters. Clients should be treating event IDs as opaque identifiers and should not +be attempting to parse them into a usable form, just like with other room versions. + +Clients should expect to see event IDs changed from the format of ``$randomstring:example.org`` +to something like ``$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk`` (note the lack of +domain and the potentially problematic slash). + + +Server implementation components +-------------------------------- + +.. WARNING:: + The information contained in this section is strictly for server implementors. + Applications which use the Client-Server API are generally unaffected by the + intricacies contained here. The section above regarding client considerations + is the resource that Client-Server API use cases should reference. + + +Room version 3 uses the state resolution algorithm defined in `room version 2 `_, +and the event format defined here. + +Event IDs +~~~~~~~~~ + +.. admonition:: Rationale + + In other room versions (namely version 1 and 2) the event ID is a distinct field + from the remainder of the event, which must be tracked as such. This leads to + complications where servers receive multiple events with the same ID in either the + same or different rooms where the server cannot easily keep track of which event it + should be using. By removing the use of a dedicated event ID, servers are required + to track the hashes on an event to determine its ID. + +The event ID is the `reference hash`_ of the event encoded using `Unpadded Base64`_, +prefixed with ``$``. A resulting event ID using this approach should look similar to +``$CD66HAED5npg6074c6pDtLKalHjVfYb2q4Q3LZgrW6o``. + +Event IDs should not be sent over federation to servers when the room uses +this room version. On the receiving end of an event, the server should compute +the relevant event ID for itself. + +Additionally, the ``auth_events`` and ``prev_events`` have had a format change +compared to other room versions to make it easier to handle. Instead of a tuple +of values, they are now plain lists of events. + +{{definition_ss_pdu_v3}} + +Changes to APIs +~~~~~~~~~~~~~~~ + +Due to the event ID being removed from the event, some APIs need to change. All +APIs which currently accept an event ID must do so with the new format. Servers +must append the calculated event ID to all events sent to clients where an event +ID would normally be expected. + +Because the format of events has changed, servers must be aware of the room version +where the event resides so that the server may parse and handle the event. The +federation API has taken this concern into consideration by ensuring that servers +are aware of (or can find) the room version during a request. + +Authorization rules for events +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The authorization rules for a given event have changed in this room version due +to the change in event format: + +* The event no longer needs to be signed by the domain of the event ID (as there + is no domain in the event ID), but still needs to be signed by the sender's + domain. + +* In past room versions, redactions were only permitted to enter the DAG if the + sender's domain matched the domain in the event ID being redacted, or the sender + had appropriate permissions per the power levels. Due to servers now not being + able to determine where an event came from during event authorization, redaction + events are always accepted (provided the event is allowed by ``events`` and + ``events_default`` in the power levels). However, servers should not apply or send + redactions to clients until both the redaction event and original event have been + seen, and are valid. Servers should only apply redactions to events where the + sender's domains match, or the sender of the redaction has the appropriate + permissions per the power levels. + + +The remaining rules are the same as `room version 1 `_. + + +.. _`Unpadded Base64`: ../appendices.html#unpadded-base64 +.. _`Canonical JSON`: ../appendices.html#canonical-json +.. _`Signing Events`: ../server_server/r0.1.1.html#signing-events +.. _`reference hash`: ../server_server/r0.1.1.html#reference-hashes diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index c7ed6da3..b765e36a 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -1,6 +1,5 @@ .. Copyright 2016 OpenMarket Ltd -.. Copyright 2017 New Vector Ltd -.. Copyright 2018 New Vector Ltd +.. Copyright 2017-2019 New Vector Ltd .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -17,9 +16,7 @@ Federation API ============== -.. WARNING:: - This API is unstable and will change without warning or discussion while - we work towards a r0 release (scheduled for August 2018). +{{unstable_warning_block_SERVER_RELEASE_LABEL}} Matrix homeservers use the Federation APIs (also known as server-server APIs) to communicate with each other. Homeservers use these APIs to push messages to @@ -77,13 +74,14 @@ This version of the specification is generated from For the full historical changelog, see https://github.com/matrix-org/matrix-doc/blob/master/changelogs/server_server.rst - Other versions of this specification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following other versions are also available, in reverse chronological order: - `HEAD `_: Includes all changes since the latest versioned release. +- `r0.1.1 `_ +- `r0.1.0 `_ Server discovery ---------------- @@ -91,35 +89,107 @@ Server discovery Resolving server names ~~~~~~~~~~~~~~~~~~~~~~ -Each matrix homeserver is identified by a server name consisting of a hostname +Each Matrix homeserver is identified by a server name consisting of a hostname and an optional port, as described by the `grammar -<../appendices.html#server-name>`_. Server names should be resolved to an IP -address and port using the following process: - -* If the hostname is an IP literal, then that IP address should be used, - together with the given port number, or 8448 if no port is given. - -* Otherwise, if the port is present, then an IP address is discovered by - looking up an AAAA or A record for the hostname, and the specified port is - used. - -* If the hostname is not an IP literal and no port is given, the server is - discovered by first looking up a ``_matrix._tcp`` SRV record for the - hostname, which may give a hostname (to be looked up using AAAA or A queries) - and port. If the SRV record does not exist, then the server is discovered by - looking up an AAAA or A record on the hostname and taking the default - fallback port number of 8448. - - Homeservers may use SRV records to load balance requests between multiple TLS - endpoints or to failover to another endpoint if an endpoint fails. - -When making requests to servers, use the hostname of the target server in the -``Host`` header, regardless of any hostname given in the SRV record. For -example, if the server name is ``example.org``, and the SRV record resolves to -``matrix.example.org``, the ``Host`` header in the request should be -``example.org``. If an explicit port was given in the server name, it should be -included in the ``Host`` header; otherwise, no port number should be given in -the ``Host`` header. +<../appendices.html#server-name>`_. Where applicable, a delegated server name +uses the same grammar. + +Server names are resolved to an IP address and port to connect to, and have +various conditions affecting which certificates and ``Host`` headers to send. +The process overall is as follows: + +.. Note from the author: The repetitive "use this Host header and this cert" + comments are intentional. The process is overall quite complicated, and + explaining explicitly what requests look like at each step helps ease the + understanding and ensure everyone is on the same page. Implementations + are of course welcome to realize where the process can be optimized, and + do so - just ensure that the result is the same! + +1. If the hostname is an IP literal, then that IP address should be used, + together with the given port number, or 8448 if no port is given. The + target server must present a valid certificate for the IP address. + The ``Host`` header in the request should be set to the server name, + including the port if the server name included one. + +2. If the hostname is not an IP literal, and the server name includes an + explicit port, resolve the IP address using AAAA or A records. Requests + are made to the resolved IP address and given port with a ``Host`` header + of the original server name (with port). The target server must present a + valid certificate for the hostname. + +3. If the hostname is not an IP literal, a regular HTTPS request is made + to ``https:///.well-known/matrix/server``, expecting the + schema defined later in this section. 30x redirects should be followed, + however redirection loops should be avoided. Responses (successful or + otherwise) to the ``/.well-known`` endpoint should be cached by the + requesting server. Servers should respect the cache control headers + present on the response, or use a sensible default when headers are not + present. The recommended sensible default is 24 hours. Servers should + additionally impose a maximum cache time for responses: 48 hours is + recommended. Errors are recommended to be cached for up to an hour, + and servers are encouraged to exponentially back off for repeated + failures. The schema of the ``/.well-known`` request is later in this + section. If the response is invalid (bad JSON, missing properties, non-200 + response, etc), skip to step 4. If the response is valid, the ``m.server`` + property is parsed as ``[:]`` and + processed as follows: + + * If ```` is an IP literal, then that IP address + should be used together with the ```` or 8448 if no + port is provided. The target server must present a valid TLS certificate + for the IP address. Requests must be made with a ``Host`` header containing + the IP address, including the port if one was provided. + + * If ```` is not an IP literal, and ```` + is present, an IP address is discovered by looking up an AAAA or A + record for ````. The resulting IP address is + used, alongside the ````. Requests must be made with a + ``Host`` header of ``:``. The + target server must present a valid certificate for ````. + + * If ```` is not an IP literal and no + ```` is present, an SRV record is looked up for + ``_matrix._tcp.``. This may result in another + hostname (to be resolved using AAAA or A records) and port. Requests + should be made to the resolved IP address and port with a ``Host`` + header containing the ````. The target server + must present a valid certificate for ````. + + * If no SRV record is found, an IP address is resolved using AAAA + or A records. Requests are then made to the resolve IP address + and a port of 8448, using a ``Host`` header of ````. + The target server must present a valid certificate for ````. + +4. If the ``/.well-known`` request resulted in an error response, a server + is found by resolving an SRV record for ``_matrix._tcp.``. This + may result in a hostname (to be resolved using AAAA or A records) and + port. Requests are made to the resolved IP address and port, using 8448 + as a default port, with a ``Host`` header of ````. The target + server must present a valid certificate for ````. + +5. If the ``/.well-known`` request returned an error response, and the SRV + record was not found, an IP address is resolved using AAAA and A records. + Requests are made to the resolved IP address using port 8448 and a ``Host`` + header containing the ````. The target server must present a + valid certificate for ````. + + +The TLS certificate provided by the target server must be signed by a known +Certificate Authority. Servers are ultimately responsible for determining +the trusted Certificate Authorities, however are strongly encouraged to +rely on the operating system's judgement. Servers can offer administrators +a means to override the trusted authorities list. Servers can additionally +skip the certificate validation for a given whitelist of domains or netmasks +for the purposes of testing or in networks where verification is done +elsewhere, such as with ``.onion`` addresses. Servers should respect SNI +when making requests where possible: a SNI should be sent for the certificate +which is expected, unless that certificate is expected to be an IP address in +which case SNI is not supported and should not be sent. + +Servers are encouraged to make use of the +`Certificate Transparency `_ project. + +{{wellknown_ss_http_api}} Server implementation ~~~~~~~~~~~~~~~~~~~~~~ @@ -131,7 +201,7 @@ Retrieving server keys .. NOTE:: There was once a "version 1" of the key exchange. It has been removed from the - specification due to lack of significance. It may be reviewed `here + specification due to lack of significance. It may be reviewed `from the historical draft `_. Each homeserver publishes its public keys under ``/_matrix/key/v2/server/{keyId}``. @@ -153,12 +223,11 @@ server by querying other servers. Publishing Keys +++++++++++++++ -Homeservers publish the allowed TLS fingerprints and signing keys in a JSON +Homeservers publish their signing keys in a JSON object at ``/_matrix/key/v2/server/{key_id}``. The response contains a list of ``verify_keys`` that are valid for signing federation requests made by the homeserver and for signing events. It contains a list of ``old_verify_keys`` which -are only valid for signing events. Finally the response contains a list of TLS -certificate fingerprints to validate any connection made to the homeserver. +are only valid for signing events. {{keys_server_ss_http_api}} @@ -282,6 +351,8 @@ Transactions are limited in size; they can have at most 50 PDUs and 100 EDUs. {{transactions_ss_http_api}} +.. _`Persistent Data Unit schema`: + PDUs ---- @@ -328,7 +399,7 @@ following subset of the room state: ``m.room.third_party_invite`` event with ``state_key`` matching ``content.third_party_invite.signed.token``, if any. -{{definition_ss_pdu}} +For a full schema of what a PDU looks like, see the `room version specification`_. Checks performed on receipt of a PDU ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -380,189 +451,9 @@ Authorization rules The rules governing whether an event is authorized depends on a set of state. A given event is checked multiple times against different sets of state, as -specified above. The types of state events that affect authorization are: - -- ``m.room.create`` -- ``m.room.member`` -- ``m.room.join_rules`` -- ``m.room.power_levels`` -- ``m.room.third_party_invite`` - -The rules are as follows: - -1. If type is ``m.room.create``: - - a. If it has any previous events, reject. - b. If the domain of the ``room_id`` does not match the domain of the - ``sender``, reject. - c. If ``content.room_version`` is present and is not a recognised version, - reject. - d. If ``content`` has no ``creator`` field, reject. - e. Otherwise, allow. - -#. Reject if event has ``auth_events`` that: - - a. have duplicate entries for a given ``type`` and ``state_key`` pair - #. have entries whose ``type`` and ``state_key`` don't match those - specified by the `auth events selection`_ algorithm described above. - -#. If event does not have a ``m.room.create`` in its ``auth_events``, reject. - -#. If type is ``m.room.aliases``: - - a. If event has no ``state_key``, reject. - b. If sender's domain doesn't matches ``state_key``, reject. - c. Otherwise, allow. - -#. If type is ``m.room.member``: - - a. If no ``state_key`` key or ``membership`` key in ``content``, reject. - - #. If ``membership`` is ``join``: - - i. If the only previous event is an ``m.room.create`` - and the ``state_key`` is the creator, allow. - - #. If the ``sender`` does not match ``state_key``, reject. - - #. If the ``sender`` is banned, reject. - - #. If the ``join_rule`` is ``invite`` then allow if membership state - is ``invite`` or ``join``. - - #. If the ``join_rule`` is ``public``, allow. - - #. Otherwise, reject. - - #. If ``membership`` is ``invite``: - - i. If ``content`` has ``third_party_invite`` key: - - #. If *target user* is banned, reject. - - #. If ``content.third_party_invite`` does not have a - ``signed`` key, reject. - - #. If ``signed`` does not have ``mxid`` and ``token`` keys, reject. - - #. If ``mxid`` does not match ``state_key``, reject. - - #. If there is no ``m.room.third_party_invite`` event in the - current room state with ``state_key`` matching ``token``, reject. - - #. If ``sender`` does not match ``sender`` of the - ``m.room.third_party_invite``, reject. - - #. If any signature in ``signed`` matches any public key in the - ``m.room.third_party_invite`` event, allow. The public keys are - in ``content`` of ``m.room.third_party_invite`` as: - - #. A single public key in the ``public_key`` field. - #. A list of public keys in the ``public_keys`` field. - - #. Otherwise, reject. - - #. If the ``sender``'s current membership state is not ``join``, reject. - - #. If *target user*'s current membership state is ``join`` or ``ban``, - reject. - - #. If the ``sender``'s power level is greater than or equal to the *invite - level*, allow. - - #. Otherwise, reject. - - #. If ``membership`` is ``leave``: - - i. If the ``sender`` matches ``state_key``, allow if and only if that user's - current membership state is ``invite`` or ``join``. - - #. If the ``sender``'s current membership state is not ``join``, reject. - - #. If the *target user*'s current membership state is ``ban``, and the - ``sender``'s power level is less than the *ban level*, reject. - - #. If the ``sender``'s power level is greater than or equal to the *kick - level*, and the *target user*'s power level is less than the - ``sender``'s power level, allow. - - #. Otherwise, reject. - - #. If ``membership`` is ``ban``: - - i. If the ``sender``'s current membership state is not ``join``, reject. - - #. If the ``sender``'s power level is greater than or equal to the *ban - level*, and the *target user*'s power level is less than the - ``sender``'s power level, allow. - - #. Otherwise, reject. - - #. Otherwise, the membership is unknown. Reject. - -#. If the ``sender``'s current membership state is not ``join``, reject. - -#. If type is ``m.room.third_party_invite``: - - a. Allow if and only if ``sender``'s current power level is greater than - or equal to the *invite level*. - -#. If the event type's *required power level* is greater than the ``sender``'s power - level, reject. - -#. If the event has a ``state_key`` that starts with an ``@`` and does not match - the ``sender``, reject. - -#. If type is ``m.room.power_levels``: - - a. If ``users`` key in ``content`` is not a dictionary with keys that are - valid user IDs with values that are integers (or a string that is an - integer), reject. - - #. If there is no previous ``m.room.power_levels`` event in the room, allow. - - #. For each of the keys ``users_default``, ``events_default``, - ``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as - each entry being changed under the ``events`` or ``users`` keys: - - i. If the current value is higher than the ``sender``'s current power level, - reject. - - #. If the new value is higher than the ``sender``'s current power level, - reject. - - #. For each entry being changed under the ``users`` key, other than the - ``sender``'s own entry: - - i. If the current value is equal to the ``sender``'s current power level, - reject. - - #. Otherwise, allow. - -#. If type is ``m.room.redaction``: - - a. If the ``sender``'s power level is greater than or equal to the *redact - level*, allow. - - #. If the domain of the ``event_id`` of the event being redacted is the same - as the domain of the ``event_id`` of the ``m.room.redaction``, allow. - - #. Otherwise, reject. - -#. Otherwise, allow. - -.. NOTE:: - - Some consequences of these rules: - - * Unless you are a member of the room, the only permitted operations (apart - from the intial create/join) are: joining a public room; accepting or - rejecting an invitation to a room. - - * To unban somebody, you must have power level greater than or equal to both - the kick *and* ban levels, *and* greater than the target user's power - level. - +specified above. Each room version can have a different algorithm for how the +rules work, and which rules are applied. For more detailed information, please +see the `room version specification`_. Rejection +++++++++ @@ -752,191 +643,8 @@ is at the top):: Suppose E3 and E4 are both ``m.room.name`` events which set the name of the room. What should the name of the room be at E5? -Servers should follow one of the following recursively-defined algorithms, -depending on the room version, to determine the room state at a given point on -the DAG. - -State resolution algorithm for version 2 rooms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The room state :math:`S'(E)` after an event :math:`E` is defined in terms of -the room state :math:`S(E)` before :math:`E`, and depends on whether -:math:`E` is a state event or a message event: - -* If :math:`E` is a message event, then :math:`S'(E) = S(E)`. - -* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except - that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key`` - is replaced by :math:`E`'s ``event_id``. - -The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of -states :math:`\{ S'(E_1), S'(E_2), … \}` consisting of the states after each of -:math:`E`'s ``prev_event``\s :math:`\{ E_1, E_2, … \}`, where the resolution of -a set of states is given in the algorithm below. - -Definitions -+++++++++++ - -The state resolution algorithm for version 2 rooms uses the following -definitions, given the set of room states :math:`\{ S_1, S_2, \ldots \}`: - -Power events - A *power event* is a state event with type ``m.room.power_levels`` or - ``m.room.join_rules``, or a state event with type ``m.room.member`` where the - ``membership`` is ``leave`` or ``ban`` and the ``sender`` does not match the - ``state_key``. The idea behind this is that power events are events that have - may remove someone's ability to do something in the room. - -Unconflicted state map and conflicted state set - The *unconflicted state map* is the state where the value of each key exists - and is the same in each state :math:`S_i`. The *conflicted state set* is the - set of all other state events. Note that the unconflicted state map only has - one event per ``(event_type, state_key)``, whereas the conflicted state set - may have multiple events. - -Auth difference - The *auth difference* is calculated by first calculating the full auth chain - for each state :math:`S_i`, that is the union of the auth chains for each - event in :math:`S_i`, and then taking every event that doesn't appear in - every auth chain. If :math:`C_i` is the full auth chain of :math:`S_i`, then - the auth difference is :math:`\cup C_i - \cap C_i`. - -Full conflicted set - The *full conflicted set* is the union of the conflicted state set and the - auth difference. - -Reverse topological power ordering - The *reverse topological power ordering* of a set of events is the - lexicographically smallest topological ordering based on the DAG formed by - auth events. The reverse topological power ordering is ordered from earliest - event to latest. For comparing two topological orderings to determine which - is the lexicographically smallest, the following comparison relation on - events is used: for events :math:`x` and :math:`y`, :math:`x`_ will address this +issue. + + .. |/query/directory| replace:: ``/query/directory`` .. _/query/directory: #get-matrix-federation-v1-query-directory @@ -1500,3 +1224,4 @@ Example code .. _`Checking for a signature`: ../appendices.html#checking-for-a-signature .. _`Device Management module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#device-management .. _`End-to-End Encryption module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#end-to-end-encryption +.. _`room version specification`: ../index.html#room-versions diff --git a/specification/targets.yaml b/specification/targets.yaml index 4172e617..830449ae 100644 --- a/specification/targets.yaml +++ b/specification/targets.yaml @@ -26,6 +26,18 @@ targets: files: - push_gateway.rst version_label: "%PUSH_GATEWAY_RELEASE_LABEL%" + rooms@v1: # this is translated to be rooms/v1.html + files: + - rooms/v1.rst + version_label: v1 + rooms@v2: # this is translated to be rooms/v2.html + files: + - rooms/v2.rst + version_label: v2 + rooms@v3: # this is translated to be rooms/v3.html + files: + - rooms/v3.rst + version_label: v3 appendices: files: - appendices.rst @@ -70,6 +82,7 @@ groups: # reusable blobs of files when prefixed with 'group:' - modules/openid.rst - modules/server_acls.rst - modules/mentions.rst + - modules/room_upgrades.rst title_styles: ["=", "-", "~", "+", "^", "`", "@", ":"]