diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml index e98d7026..6e595120 100644 --- a/.buildkite/pipeline.yaml +++ b/.buildkite/pipeline.yaml @@ -9,3 +9,8 @@ steps: plugins: - docker#v3.0.1: image: "python:3.6" + + - label: "rebuild matrix.org" + trigger: "matrix-dot-org" + async: true + branches: "master" diff --git a/.circleci/config.yml b/.circleci/config.yml index 659380b0..b37478bf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -97,7 +97,7 @@ jobs: command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/api/client-server/index.html"; echo $DOCS_URL build-dev-scripts: docker: - - image: golang:1.8 + - image: golang:1.10 steps: - checkout - run: diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 7a6c6be2..fc5b146f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -26,10 +26,11 @@ For this to be effective, the APIs need to be present and working correctly in a server before they can be documented in the specification. This process can take some time to complete. -For this reason, we have not found the github pull-request model effective for -discussing changes to the specification. Instead, we have adopted the workflow -as described at https://matrix.org/docs/spec/proposals - *please read this for -details on how to contribute spec changes*. +Changes to the protocol (new endpoints, ideas, etc) need to go through the +`proposals process `_. Other changes, +such as fixing bugs, typos, or clarifying existing behaviour do not need a proposal. +If you're not sure, visit us at `#matrix-spec:matrix.org`_ +and ask. Other changes @@ -51,8 +52,7 @@ following: `_ label. (If there is any doubt about whether it is the spec or the implementations - that need fixing, please discuss it with us first in `#matrix-dev:matrix.org - `_.) + that need fixing, please discuss it with us first in `#matrix-spec:matrix.org`_.) * Clarifications to the specification which do not change the behaviour of Matrix servers or clients in a way which might introduce compatibility @@ -60,23 +60,23 @@ following: `clarification `_ label. - For example, recommendations for UI behaviour do not require a proposal - document. On the other hand, changes to event contents would be best - discussed in a proposal document even though no changes would be necessary to - server implementations. + For example, areas where the specification is unclear do not require a proposal + to fix. On the other hand, introducing new behaviour is best represented by a + proposal. -For such changes, please do just open a `pull request`_. +For such changes, please do just open a `pull request`_. If you're not sure if +your change is covered by the above, please visit `#matrix-spec:matrix.org` and +ask. -.. _pull request: https://help.github.com/articles/about-pull-requests +.. _`pull request`: https://help.github.com/articles/about-pull-requests +.. _`#matrix-spec:matrix.org`: https://matrix.to/#/#matrix-spec:matrix.org Adding to the changelog ~~~~~~~~~~~~~~~~~~~~~~~ -Currently only changes to the client-server API need to end up in a changelog. The -other APIs are not yet stable and therefore do not have a changelog. Adding to the -changelog can only be done after you've opened your pull request, so be sure to do -that first. +All API specifications require a changelog entry. Adding to the changelog can only +be done after you've opened your pull request, so be sure to do that first. The changelog is managed by Towncrier (https://github.com/hawkowl/towncrier) in the form of "news fragments". The news fragments for the client-server API are stored @@ -96,7 +96,7 @@ the ``newsfragments`` directory. The ``type`` can be one of the following: * ``breaking`` - Used when the change is not backwards compatible. -* ``deprecation`` - Used when deprecating something +* ``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 diff --git a/README.rst b/README.rst index b8847bfb..ace8ebdd 100644 --- a/README.rst +++ b/README.rst @@ -138,4 +138,4 @@ Issue tracking Issues with the Matrix specification are tracked in `GitHub `_. -See `meta/labels.rst `_ for notes on what the labels mean. +See `meta/github-labels.rst `_ for notes on what the labels mean. diff --git a/api/application-service/transactions.yaml b/api/application-service/transactions.yaml index 98181196..09f15276 100644 --- a/api/application-service/transactions.yaml +++ b/api/application-service/transactions.yaml @@ -56,7 +56,7 @@ paths: example: { "events": [ {"$ref": "../../event-schemas/examples/m.room.member"}, - {"$ref": "../../event-schemas/examples/m.room.message#m.text"} + {"$ref": "../../event-schemas/examples/m.room.message$m.text"} ] } description: Transaction information diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index 33ea9786..0e93e4cd 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -134,9 +134,27 @@ paths: 200: description: The addition was successful. examples: - application/json: {} + application/json: { + "submit_url": "https://example.org/path/to/submitToken" + } schema: type: object + properties: + submit_url: + type: string + description: |- + An optional field containing a URL where the client must + submit the validation token to, with identical parameters + to the Identity Service API's ``POST + /validate/email/submitToken`` endpoint. The homeserver must + send this token to the user (if applicable), who should + then be prompted to provide it to the client. + + If this field is not present, the client can assume that + verification will happen without the client's involvement + provided the homeserver advertises this specification version + in the ``/versions`` response (ie: r0.5.0). + example: "https://example.org/path/to/submitToken" 403: description: The credentials could not be verified with the identity server. examples: @@ -163,6 +181,14 @@ paths: schema: type: object properties: + id_server: + type: string + description: |- + The identity server to unbind from. If not provided, the homeserver + MUST use the ``id_server`` the identifier was added through. If the + homeserver does not know the original ``id_server``, it MUST return + a ``id_server_unbind_result`` of ``no-support``. + example: "example.org" medium: type: string description: The medium of the third party identifier being removed. @@ -180,41 +206,55 @@ paths: user. schema: type: object - properties: {} + properties: + id_server_unbind_result: + type: string + enum: + # XXX: I don't know why, but the order matters here so that "no-support" + # doesn't become "no- support" by the renderer. + - "no-support" + - "success" + description: |- + An indicator as to whether or not the homeserver was able to unbind + the 3PID from the identity server. ``success`` indicates that the + indentity server has unbound the identifier whereas ``no-support`` + indicates that the identity server refuses to support the request + or the homeserver was not able to determine an identity server to + unbind from. + example: "success" + required: + - id_server_unbind_result tags: - User data "/account/3pid/email/requestToken": post: summary: Begins the validation process for an email address for association with the user's account. description: |- - Proxies the Identity Service API ``validate/email/requestToken``, but - first checks that the given email address is **not** already associated - with an account on this homeserver. This API should be used to request - validation tokens when adding an email address to an account. This API's - parameters and response are identical to that of the |/register/email/requestToken|_ - endpoint. + The homeserver must check that the given email address is **not** + already associated with an account on this homeserver. This API should + be used to request validation tokens when adding an email address to an + account. This API's parameters and response are identical to that of + the |/register/email/requestToken|_ endpoint. The homeserver has the + choice of validating the email address itself, or proxying the request + to the ``/validate/email/requestToken`` Identity Service API as + identified by ``id_server``. It is imperative that the + homeserver keep a list of trusted Identity Servers and only proxies to + those that it trusts. operationId: requestTokenTo3PIDEmail parameters: - in: body name: body required: true schema: - allOf: - - $ref: "../identity/definitions/request_email_validation.yaml" - - type: object - properties: - id_server: - type: string - description: |- - The hostname of the identity server to communicate with. May - optionally include a port. - example: "id.example.com" - required: ['id_server'] + $ref: "./definitions/request_email_validation.yaml" responses: 200: - description: An email was sent to the given address. + description: |- + An email was sent to the given address. Note that this may be an + email containing the validation token or it may be informing the + user of an error. schema: - $ref: "../identity/definitions/sid.yaml" + $ref: "definitions/request_token_response.yaml" 403: description: |- The homeserver does not allow the third party identifier as a @@ -241,34 +281,28 @@ paths: post: summary: Begins the validation process for a phone number for association with the user's account. description: |- - Proxies the Identity Service API ``validate/msisdn/requestToken``, but - first checks that the given phone number is **not** already associated - with an account on this homeserver. This API should be used to request - validation tokens when adding a phone number to an account. This API's - parameters and response are identical to that of the |/register/msisdn/requestToken|_ - endpoint. + The homeserver must check that the given phone number is **not** + already associated with an account on this homeserver. This API should + be used to request validation tokens when adding a phone number to an + account. This API's parameters and response are identical to that of + the |/register/msisdn/requestToken|_ endpoint. The homeserver has the + choice of validating the phone number itself, or proxying the request + to the ``/validate/msisdn/requestToken`` Identity Service API as + identified by ``id_server``. It is imperative that the + homeserver keep a list of trusted Identity Servers and only proxies to + those that it trusts. operationId: requestTokenTo3PIDMSISDN parameters: - in: body name: body required: true schema: - allOf: - - $ref: "../identity/definitions/request_msisdn_validation.yaml" - - type: object - properties: - id_server: - type: string - description: |- - The hostname of the identity server to communicate with. May - optionally include a port. - example: "id.example.com" - required: ['id_server'] + $ref: "./definitions/request_msisdn_validation.yaml" responses: 200: description: An SMS message was sent to the given phone number. schema: - $ref: "../identity/definitions/sid.yaml" + $ref: "definitions/request_token_response.yaml" 403: description: |- The homeserver does not allow the third party identifier as a diff --git a/api/client-server/content-repo.yaml b/api/client-server/content-repo.yaml index 5f4e9111..a9a0c2f6 100644 --- a/api/client-server/content-repo.yaml +++ b/api/client-server/content-repo.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -41,7 +42,7 @@ paths: name: Content-Type type: string description: The content type of the file being uploaded - x-example: "Content-Type: audio/mpeg" + x-example: "Content-Type: application/pdf" - in: query type: string x-example: "War and Peace.pdf" @@ -51,24 +52,48 @@ paths: name: "" description: The content to be uploaded. required: true + x-example: "" # so the spec shows "" without quotes. schema: type: string example: "" format: byte responses: 200: - description: The MXC URI for the uploaded content. + description: The `MXC URI`_ for the uploaded content. schema: type: object required: ["content_uri"] properties: content_uri: type: string - description: "The MXC URI to the uploaded content." + description: "The `MXC URI`_ to the uploaded content." examples: application/json: { - "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" - } + "content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw" + } + 403: + description: |- + The user does not have permission to upload the content. Some reasons for this error include: + + - The server does not permit the file type. + - The user has reached a quota for uploaded content. + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "Cannot upload this content" + } + schema: + "$ref": "definitions/errors/error.yaml" + 413: + description: |- + The uploaded content is too large for the server. + examples: + application/json: { + "errcode": "M_TOO_LARGE", + "error": "Cannot upload files larger than 100mb" + } + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: @@ -103,7 +128,7 @@ paths: default: true description: | Indicates to the server that it should not attempt to fetch the media if it is deemed - remote. This is to prevent routing loops where the server contacts itself. Defaults to + remote. This is to prevent routing loops where the server contacts itself. Defaults to true if not provided. responses: 200: @@ -113,10 +138,23 @@ paths: description: "The content type of the file that was previously uploaded." type: "string" Content-Disposition: - description: "The name of the file that was previously uploaded, if set." + description: |- + The name of the file that was previously uploaded, if set. type: "string" schema: type: file + # This is a workaround for us not being able to say the response is required. + description: "**Required.** The bytes for the uploaded file." + 502: + description: |- + The content is too large for the server to serve. + examples: + application/json: { + "errcode": "M_TOO_LARGE", + "error": "Content is too large to serve" + } + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: @@ -125,7 +163,9 @@ paths: - Media "/download/{serverName}/{mediaId}/{fileName}": get: - summary: "Download content from the content repository as a given filename." + summary: |- + Download content from the content repository. This is the same as + the download endpoint above, except permitting a desired file name. operationId: getContentOverrideName produces: ["*/*"] parameters: @@ -148,8 +188,7 @@ paths: name: fileName x-example: filename.jpg required: true - description: | - The filename to give in the Content-Disposition + description: A filename to give in the ``Content-Disposition`` header. - in: query type: boolean name: allow_remote @@ -158,7 +197,7 @@ paths: default: true description: | Indicates to the server that it should not attempt to fetch the media if it is deemed - remote. This is to prevent routing loops where the server contacts itself. Defaults to + remote. This is to prevent routing loops where the server contacts itself. Defaults to true if not provided. responses: 200: @@ -168,10 +207,24 @@ paths: description: "The content type of the file that was previously uploaded." type: "string" Content-Disposition: - description: "The name of file given in the request" + description: |- + The ``fileName`` requested or the name of the file that was previously + uploaded, if set. type: "string" schema: type: file + # This is a workaround for us not being able to say the response is required. + description: "**Required.** The bytes for the uploaded file." + 502: + description: |- + The content is too large for the server to serve. + examples: + application/json: { + "errcode": "M_TOO_LARGE", + "error": "Content is too large to serve" + } + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: @@ -180,7 +233,9 @@ paths: - Media "/thumbnail/{serverName}/{mediaId}": get: - summary: "Download a thumbnail of the content from the content repository." + summary: |- + Download a thumbnail of content from the content repository. See the `thumbnailing <#thumbnails>`_ + section for more information. operationId: getContentThumbnail produces: ["image/jpeg", "image/png"] parameters: @@ -188,7 +243,7 @@ paths: type: string name: serverName required: true - x-example: matrix.org + x-example: example.org description: | The server name from the ``mxc://`` URI (the authoritory component) - in: path @@ -202,22 +257,26 @@ paths: type: integer x-example: 64 name: width + required: true description: |- - The *desired* width of the thumbnail. The actual thumbnail may not - match the size specified. + The *desired* width of the thumbnail. The actual thumbnail may be + larger than the size specified. - in: query type: integer x-example: 64 name: height + required: true description: |- - The *desired* height of the thumbnail. The actual thumbnail may not - match the size specified. + The *desired* height of the thumbnail. The actual thumbnail may be + larger than the size specified. - in: query type: string enum: ["crop", "scale"] name: method x-example: "scale" - description: The desired resizing method. + description: |- + The desired resizing method. See the `thumbnailing <#thumbnails>`_ + section for more information. - in: query type: boolean name: allow_remote @@ -226,7 +285,7 @@ paths: default: true description: | Indicates to the server that it should not attempt to fetch the media if it is deemed - remote. This is to prevent routing loops where the server contacts itself. Defaults to + remote. This is to prevent routing loops where the server contacts itself. Defaults to true if not provided. responses: 200: @@ -238,6 +297,40 @@ paths: enum: ["image/jpeg", "image/png"] schema: type: file + # This is a workaround for us not being able to say the response is required. + description: "**Required.** The bytes for the thumbnail." + 400: + description: |- + The request does not make sense to the server, or the server cannot thumbnail + the content. For example, the client requested non-integer dimensions or asked + for negatively-sized images. + examples: + application/json: { + "errcode": "M_UNKNOWN", + "error": "Cannot generate thumbnails for the requested content" + } + schema: + "$ref": "definitions/errors/error.yaml" + 413: + description: |- + The local content is too large for the server to thumbnail. + examples: + application/json: { + "errcode": "M_TOO_LARGE", + "error": "Content is too large to thumbnail" + } + schema: + "$ref": "definitions/errors/error.yaml" + 502: + description: |- + The remote content is too large for the server to thumbnail. + examples: + application/json: { + "errcode": "M_TOO_LARGE", + "error": "Content is too large to thumbnail" + } + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: @@ -256,7 +349,7 @@ paths: type: string x-example: "https://matrix.org" name: url - description: "The URL to get a preview of" + description: "The URL to get a preview of." required: true - in: query type: integer @@ -284,7 +377,7 @@ paths: "og:image": type: string description: |- - An MXC URI to the image. Omitted if there is no image. + An `MXC URI`_ to the image. Omitted if there is no image. examples: application/json: { "og:title": "Matrix Blog Post", @@ -328,7 +421,7 @@ paths: m.upload.size: type: integer format: int64 - description: |- + description: |- The maximum size an upload can be in bytes. Clients SHOULD use this as a guide when uploading content. If not listed or null, the size limit should be treated as unknown. diff --git a/api/client-server/definitions/device_keys.yaml b/api/client-server/definitions/device_keys.yaml index 888c93a4..41504aba 100644 --- a/api/client-server/definitions/device_keys.yaml +++ b/api/client-server/definitions/device_keys.yaml @@ -33,7 +33,7 @@ properties: type: string description: |- The encryption algorithms supported by this device. - example: ["m.olm.curve25519-aes-sha256", "m.megolm.v1.aes-sha"] + example: ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"] keys: type: object description: |- diff --git a/api/client-server/definitions/push_condition.yaml b/api/client-server/definitions/push_condition.yaml index 796a51f4..8752274e 100644 --- a/api/client-server/definitions/push_condition.yaml +++ b/api/client-server/definitions/push_condition.yaml @@ -16,16 +16,20 @@ title: PushCondition type: object properties: kind: - enum: - - event_match - - contains_display_name - - room_member_count type: string + description: |- + The kind of condition to apply. See `conditions <#conditions>`_ for + more information on the allowed kinds and how they work. key: type: string description: |- Required for ``event_match`` conditions. The dot-separated field of the event to match. + + Required for ``sender_notification_permission`` conditions. The field in + the power level event the user needs a minimum power level for. Fields + must be specified under the ``notifications`` property in the power level + event's ``content``. x-example: content.body pattern: type: string diff --git a/api/client-server/definitions/request_email_validation.yaml b/api/client-server/definitions/request_email_validation.yaml new file mode 100644 index 00000000..15bc5b3a --- /dev/null +++ b/api/client-server/definitions/request_email_validation.yaml @@ -0,0 +1,26 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +allOf: +- $ref: "../../identity/definitions/request_email_validation.yaml" +- type: object + properties: + id_server: + type: string + description: |- + The hostname of the identity server to communicate with. May optionally + include a port. This parameter is ignored when the homeserver handles + 3PID verification. + example: "id.example.com" + required: ["id_server"] diff --git a/api/client-server/definitions/request_msisdn_validation.yaml b/api/client-server/definitions/request_msisdn_validation.yaml new file mode 100644 index 00000000..370a10cc --- /dev/null +++ b/api/client-server/definitions/request_msisdn_validation.yaml @@ -0,0 +1,26 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +allOf: +- $ref: "../../identity/definitions/request_msisdn_validation.yaml" +- type: object + properties: + id_server: + type: string + description: |- + The hostname of the identity server to communicate with. May optionally + include a port. This parameter is ignored when the homeserver handles + 3PID verification. + example: "id.example.com" + required: ["id_server"] diff --git a/api/client-server/definitions/request_token_response.yaml b/api/client-server/definitions/request_token_response.yaml new file mode 100644 index 00000000..e47db8a0 --- /dev/null +++ b/api/client-server/definitions/request_token_response.yaml @@ -0,0 +1,37 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +properties: + sid: + type: string + description: |- + The session ID. Session IDs are opaque strings that must consist entirely + of the characters ``[0-9a-zA-Z.=_-]``. Their length must not exceed 255 + characters and they must not be empty. + example: "123abc" + submit_url: + type: string + description: |- + An optional field containing a URL where the client must submit the + validation token to, with identical parameters to the Identity Service + API's ``POST /validate/email/submitToken`` endpoint. The homeserver must + send this token to the user (if applicable), who should then be + prompted to provide it to the client. + + If this field is not present, the client can assume that verification + will happen without the client's involvement provided the homeserver + advertises this specification version in the ``/versions`` response + (ie: r0.5.0). + example: "https://example.org/path/to/submitToken" +required: ['sid'] diff --git a/api/client-server/definitions/room_event_filter.yaml b/api/client-server/definitions/room_event_filter.yaml index aa217d2b..880cb173 100644 --- a/api/client-server/definitions/room_event_filter.yaml +++ b/api/client-server/definitions/room_event_filter.yaml @@ -16,6 +16,20 @@ allOf: - type: object title: RoomEventFilter properties: + lazy_load_members: + type: boolean + description: |- + If ``true``, enables lazy-loading of membership events. See + `Lazy-loading room members <#lazy-loading-room-members>`_ + for more information. Defaults to ``false``. + include_redundant_members: + type: boolean + description: |- + If ``true``, sends all membership events for all events, even if they have already + been sent to the client. Does not + apply unless ``lazy_load_members`` is ``true``. See + `Lazy-loading room members <#lazy-loading-room-members>`_ + for more information. Defaults to ``false``. not_rooms: description: A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` diff --git a/api/client-server/definitions/sync_filter.yaml b/api/client-server/definitions/sync_filter.yaml index 65b18ba6..fc37fb48 100644 --- a/api/client-server/definitions/sync_filter.yaml +++ b/api/client-server/definitions/sync_filter.yaml @@ -16,7 +16,7 @@ title: Filter properties: event_fields: description: List of event fields to include. If this list is absent then all - fields are included. The entries may include '.' charaters to indicate sub-fields. + fields are included. The entries may include '.' characters to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content' object. A literal '.' character in a field name may be escaped using a '\\'. A server may include more fields than were requested. @@ -25,7 +25,7 @@ properties: type: array event_format: description: The format to use for events. 'client' will return the events in - a format suitable for clients. 'federation' will return the raw event as receieved + a format suitable for clients. 'federation' will return the raw event as received over federation. The default is 'client'. enum: - client @@ -47,7 +47,7 @@ properties: not_rooms: description: A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` - filter. This filter is applied before the filters in ``ephemeral``, + filter. This filter is applied before the filters in ``ephemeral``, ``state``, ``timeline`` or ``account_data`` items: type: string @@ -73,33 +73,6 @@ properties: allOf: - $ref: room_event_filter.yaml description: The state events to include for rooms. - properties: - lazy_load_members: - type: boolean - description: |- - If ``true``, the only ``m.room.member`` events returned in - the ``state`` section of the ``/sync`` response are those - which are definitely necessary for a client to display - the ``sender`` of the timeline events in that response. - If ``false``, ``m.room.member`` events are not filtered. - By default, servers should suppress duplicate redundant - lazy-loaded ``m.room.member`` events from being sent to a given - client across multiple calls to ``/sync``, given that most clients - cache membership events (see ``include_redundant_members`` - to change this behaviour). - include_redundant_members: - type: boolean - description: |- - If ``true``, the ``state`` section of the ``/sync`` response will - always contain the ``m.room.member`` events required to display - the ``sender`` of the timeline events in that response, assuming - ``lazy_load_members`` is enabled. This means that redundant - duplicate member events may be returned across multiple calls to - ``/sync``. This is useful for naive clients who never track - membership data. If ``false``, duplicate ``m.room.member`` events - may be suppressed by the server across multiple calls to ``/sync``. - If ``lazy_load_members`` is ``false`` this field is ignored. - timeline: allOf: - $ref: room_event_filter.yaml diff --git a/api/client-server/definitions/third_party_signed.yaml b/api/client-server/definitions/third_party_signed.yaml new file mode 100644 index 00000000..c9c761a1 --- /dev/null +++ b/api/client-server/definitions/third_party_signed.yaml @@ -0,0 +1,41 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Third Party Signed +description: |- + A signature of an ``m.third_party_invite`` token to prove that this user + owns a third party identity which has been invited to the room. +properties: + sender: + type: string + description: The Matrix ID of the user who issued the invite. + example: "@alice:example.org" + mxid: + type: string + description: The Matrix ID of the invitee. + example: "@bob:example.org" + token: + type: string + description: The state key of the m.third_party_invite event. + example: "random8nonce" + signatures: + type: object + description: A signatures object containing a signature of the entire signed object. + title: Signatures + example: { + "example.org": { + "ed25519:0": "some9signature" + } + } +required: ["sender", "mxid", "token", "signatures"] diff --git a/api/client-server/directory.yaml b/api/client-server/directory.yaml index 78ddfa29..2c7c8386 100644 --- a/api/client-server/directory.yaml +++ b/api/client-server/directory.yaml @@ -148,5 +148,14 @@ paths: } schema: type: object + 404: + description: There is no mapped room ID for this room alias. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Room alias #monkeys:example.org not found." + } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room directory diff --git a/api/client-server/event_context.yaml b/api/client-server/event_context.yaml index 91da3cf4..3bea351c 100644 --- a/api/client-server/event_context.yaml +++ b/api/client-server/event_context.yaml @@ -34,6 +34,9 @@ paths: This API returns a number of events that happened just before and after the specified event. This allows clients to get the context surrounding an event. + + *Note*: This endpoint supports lazy-loading of room member events. See + `Lazy-loading room members <#lazy-loading-room-members>`_ for more information. operationId: getEventContext security: - accessToken: [] @@ -101,65 +104,35 @@ paths: - "$ref": "definitions/event-schemas/schema/core-event-schema/state_event.yaml" examples: application/json: { - "end": "t29-57_2_0_2", - "events_after": [ - { - "age": 91911336, - "content": { - "body": "7", - "msgtype": "m.text" - }, - "event_id": "$14460306086CiUaL:localhost:8480", - "origin_server_ts": 1446030608551, - "room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480", - "type": "m.room.message", - "sender": "@test:localhost:8480" - } - ], - "events_before": [ - { - "age": 91911903, - "content": { - "body": "5", - "msgtype": "m.text" - }, - "event_id": "$14460306074UYTlh:localhost:8480", - "origin_server_ts": 1446030607984, - "room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480", - "type": "m.room.message", - "sender": "@test:localhost:8480" - } - ], - "start": "t27-54_2_0_2", - "state": [ - { - "age": 3123715284, - "content": { - "creator": "@test:localhost:8480" - }, - "event_id": "$14429988040dgQAE:localhost:8480", - "origin_server_ts": 1442998804603, - "room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480", - "state_key": "", - "type": "m.room.create", - "sender": "@test:localhost:8480" - }, - { - "age": 2067105053, - "content": { - "avatar_url": "mxc://localhost:8480/tVWZTAIIfqtXMZZtmGCkVjTD#auto", - "displayname": "Bob2", - "membership": "join" - }, - "event_id": "$14440554144URDbf:localhost:8480", - "origin_server_ts": 1444055414834, - "replaces_state": "$14440552472PgiGk:localhost:8480", - "room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480", - "state_key": "@test:localhost:8480", - "type": "m.room.member", - "sender": "@test:localhost:8480" - } - ] + "end": "t29-57_2_0_2", + "events_after": [ + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" } + ], + "event": { + "event_id": "$f3h4d129462ha:example.com", + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.message$m.image" + }, + "events_before": [ + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.message$m.file" + } + ], + "start": "t27-54_2_0_2", + "state": [ + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.create" + }, + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.member" + } + ] + } tags: - Room participation diff --git a/api/client-server/joining.yaml b/api/client-server/joining.yaml index 1dcf769f..af38d6f9 100644 --- a/api/client-server/joining.yaml +++ b/api/client-server/joining.yaml @@ -58,38 +58,9 @@ paths: name: third_party_signed schema: type: object - example: { - "third_party_signed": { - "sender": "@cat:the.hat", - "mxid": "@green:eggs.ham", - "token": "random8nonce", - "signatures": { - "horton.hears": { - "ed25519:0": "some9signature" - } - } - } - } properties: third_party_signed: - type: object - title: ThirdPartySigned - description: A signature of an ``m.third_party_invite`` token to prove that this user owns a third party identity which has been invited to the room. - properties: - sender: - type: string - description: The Matrix ID of the user who issued the invite. - mxid: - type: string - description: The Matrix ID of the invitee. - token: - type: string - description: The state key of the m.third_party_invite event. - signatures: - type: object - description: A signatures object containing a signature of the entire signed object. - title: Signatures - required: ["sender", "mxid", "token", "signatures"] + $ref: "definitions/third_party_signed.yaml" responses: 200: description: |- @@ -163,45 +134,9 @@ paths: name: third_party_signed schema: type: object - example: { - "third_party_signed": { - "signed": { - "sender": "@cat:the.hat", - "mxid": "@green:eggs.ham", - "token": "random8nonce", - "signatures": { - "horton.hears": { - "ed25519:0": "some9signature" - } - } - } - } - } properties: third_party_signed: - type: object - title: ThirdPartySigned - description: A signature of an ``m.third_party_invite`` token to prove that this user owns a third party identity which has been invited to the room. - properties: - signed: - type: object - title: Signed - properties: - sender: - type: string - description: The Matrix ID of the user who issued the invite. - mxid: - type: string - description: The Matrix ID of the invitee. - token: - type: string - description: The state key of the m.third_party_invite event. - signatures: - type: object - description: A signatures object containing a signature of the entire signed object. - title: Signatures - required: ["sender", "mxid", "token", "signatures"] - required: ["signed"] + $ref: "definitions/third_party_signed.yaml" responses: 200: description: |- diff --git a/api/client-server/keys.yaml b/api/client-server/keys.yaml index 55f8a5a5..69e39def 100644 --- a/api/client-server/keys.yaml +++ b/api/client-server/keys.yaml @@ -56,25 +56,48 @@ paths: One-time public keys for "pre-key" messages. The names of the properties should be in the format ``:``. The format of the key is determined - by the key algorithm. + by the `key algorithm <#key-algorithms>`_. May be absent if no new one-time keys are required. additionalProperties: type: - string - object - example: - "curve25519:AAAAAQ": "/qyvZvwjiTxGdGU0RCguDCLeR+nmsb3FfNG3/Ve4vU8" - signed_curve25519:AAAAHg: - key: "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs" - signatures: - "@alice:example.com": - ed25519:JLAFKJWSCS: "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw" - signed_curve25519:AAAAHQ: - key: "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw" - signatures: - "@alice:example.com": - ed25519:JLAFKJWSCS: "IQeCEPb9HFk217cU9kw9EOiusC6kMIkoIRnbnfOh5Oc63S1ghgyjShBGpu34blQomoalCyXWyhaaT3MrLZYQAA" + # XXX: We can't define an actual object here, so we have to hope + # that people will look at the swagger source or can figure it out + # from the other endpoints/example. + # - type: object + # title: KeyObject + # properties: + # key: + # type: string + # description: The key, encoded using unpadded base64. + # signatures: + # type: object + # description: |- + # Signature for the device. Mapped from user ID to signature object. + # additionalProperties: + # type: string + # required: ['key', 'signatures'] + example: { + "curve25519:AAAAAQ": "/qyvZvwjiTxGdGU0RCguDCLeR+nmsb3FfNG3/Ve4vU8", + "signed_curve25519:AAAAHg": { + "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs", + "signatures": { + "@alice:example.com": { + "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw" + } + } + }, + "signed_curve25519:AAAAHQ": { + "key": "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw", + "signatures": { + "@alice:example.com": { + "ed25519:JLAFKJWSCS": "IQeCEPb9HFk217cU9kw9EOiusC6kMIkoIRnbnfOh5Oc63S1ghgyjShBGpu34blQomoalCyXWyhaaT3MrLZYQAA" + } + } + } + } responses: 200: description: @@ -194,8 +217,8 @@ paths: "user_id": "@alice:example.com", "device_id": "JLAFKJWSCS", "algorithms": [ - "m.olm.v1.curve25519-aes-sha256", - "m.megolm.v1.aes-sha" + "m.olm.v1.curve25519-aes-sha2", + "m.megolm.v1.aes-sha2" ], "keys": { "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI", @@ -205,12 +228,12 @@ paths: "@alice:example.com": { "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA" } - }, + }, "unsigned": { "device_display_name": "Alice's mobile phone" } } - + tags: - End-to-end encryption "/keys/claim": @@ -246,14 +269,15 @@ paths: type: string description: algorithm example: "signed_curve25519" - example: + example: { "@alice:example.com": { "JLAFKJWSCS": "signed_curve25519" } + } required: - one_time_keys responses: 200: description: - The claimed keys + The claimed keys. schema: type: object properties: @@ -263,7 +287,7 @@ paths: If any remote homeservers could not be reached, they are recorded here. The names of the properties are the names of the unreachable servers. - + If the homeserver could be reached, but the user or device was unknown, no failure is recorded. Instead, the corresponding user or device is missing from the ``one_time_keys`` result. @@ -275,20 +299,46 @@ paths: description: |- One-time keys for the queried devices. A map from user ID, to a map from devices to a map from ``:`` to the key object. + + See the `key algorithms <#key-algorithms>`_ section for information + on the Key Object format. additionalProperties: type: object additionalProperties: type: - string - object - example: - "@alice:example.com": - JLAFKJWSCS: - signed_curve25519:AAAAHg: - key: "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs" - signatures: - "@alice:example.com": - ed25519:JLAFKJWSCS: "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw" + # XXX: We can't define an actual object here, so we have to hope + # that people will look at the swagger source or can figure it out + # from the other endpoints/example. + # - type: object + # title: KeyObject + # properties: + # key: + # type: string + # description: The key, encoded using unpadded base64. + # signatures: + # type: object + # description: |- + # Signature for the device. Mapped from user ID to signature object. + # additionalProperties: + # type: string + # required: ['key', 'signatures'] + example: { + "@alice:example.com": { + "JLAFKJWSCS": { + "signed_curve25519:AAAAHg": { + "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs", + "signatures": { + "@alice:example.com": { + "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw" + } + } + } + } + } + } + required: ['one_time_keys'] tags: - End-to-end encryption "/keys/changes": diff --git a/api/client-server/login.yaml b/api/client-server/login.yaml index 98914a06..f6c92638 100644 --- a/api/client-server/login.yaml +++ b/api/client-server/login.yaml @@ -194,10 +194,13 @@ paths: "$ref": "definitions/errors/error.yaml" 403: description: |- - The login attempt failed. For example, the password may have been incorrect. + The login attempt failed. This can include one of the following error codes: + * ``M_FORBIDDEN``: The provided authentication data was incorrect. + * ``M_USER_DEACTIVATED``: The user has been deactivated. examples: application/json: { - "errcode": "M_FORBIDDEN"} + "errcode": "M_FORBIDDEN" + } schema: "$ref": "definitions/errors/error.yaml" 429: diff --git a/api/client-server/logout.yaml b/api/client-server/logout.yaml index 2dfd6d97..75a3ec87 100644 --- a/api/client-server/logout.yaml +++ b/api/client-server/logout.yaml @@ -32,7 +32,8 @@ paths: summary: Invalidates a user access token description: |- Invalidates an existing access token, so that it can no longer be used for - authorization. + authorization. The device associated with the access token is also deleted. + `Device keys <#device-keys>`_ for the device are deleted alongside the device. operationId: logout security: - accessToken: [] @@ -49,7 +50,9 @@ paths: summary: Invalidates all access tokens for a user description: |- Invalidates all access tokens for a user, so that they can no longer be used for - authorization. This includes the access token that made this request. + authorization. This includes the access token that made this request. All devices + for the user are also deleted. `Device keys <#device-keys>`_ for the device are + deleted alongside the device. This endpoint does not require UI authorization because UI authorization is designed to protect against attacks where the someone gets hold of a single access diff --git a/api/client-server/message_pagination.yaml b/api/client-server/message_pagination.yaml index 941e61fb..22828219 100644 --- a/api/client-server/message_pagination.yaml +++ b/api/client-server/message_pagination.yaml @@ -33,6 +33,9 @@ paths: description: |- This API returns a list of message and state events for a room. It uses pagination query parameters to paginate history in the room. + + *Note*: This endpoint supports lazy-loading of room member events. See + `Lazy-loading room members <#lazy-loading-room-members>`_ for more information. operationId: getRoomEvents security: - accessToken: [] @@ -103,54 +106,45 @@ paths: chunk: type: array description: |- - A list of room events. + A list of room events. The order depends on the ``dir`` parameter. + For ``dir=b`` events will be in reverse-chronological order, + for ``dir=f`` in chronological order, so that events start + at the ``from`` point. items: type: object title: RoomEvent "$ref": "definitions/event-schemas/schema/core-event-schema/room_event.yaml" + state: + type: array + description: |- + A list of state events relevant to showing the ``chunk``. For example, if + ``lazy_load_members`` is enabled in the filter then this may contain + the membership events for the senders of events in the ``chunk``. + + Unless ``include_redundant_members`` is ``true``, the server + may remove membership events which would have already been + sent to the client in prior calls to this endpoint, assuming + the membership of those members has not changed. + items: + type: object + title: RoomStateEvent + $ref: "definitions/event-schemas/schema/core-event-schema/state_event.yaml" examples: application/json: { "start": "t47429-4392820_219380_26003_2265", "end": "t47409-4357353_219380_26003_2265", "chunk": [ { - "origin_server_ts": 1444812213737, - "sender": "@alice:example.com", - "event_id": "$1444812213350496Caaaa:example.com", - "content": { - "body": "hello world", - "msgtype":"m.text" - }, - "room_id":"!Xq3620DUiqCaoxq:example.com", - "type":"m.room.message", - "age": 1042 + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" }, { - "origin_server_ts": 1444812194656 , - "sender": "@bob:example.com", - "event_id": "$1444812213350496Cbbbb:example.com", - "content": { - "body": "the world is big", - "msgtype":"m.text" - }, - "room_id":"!Xq3620DUiqCaoxq:example.com", - "type":"m.room.message", - "age": 20123 + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.name" }, { - "origin_server_ts": 1444812163990, - "sender": "@bob:example.com", - "event_id": "$1444812213350496Ccccc:example.com", - "content": { - "name": "New room name" - }, - "prev_content": { - "name": "Old room name" - }, - "state_key": "", - "room_id":"!Xq3620DUiqCaoxq:example.com", - "type":"m.room.name", - "age": 50789 + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.message$m.video" } ] } diff --git a/api/client-server/notifications.yaml b/api/client-server/notifications.yaml index b450885b..87341d41 100644 --- a/api/client-server/notifications.yaml +++ b/api/client-server/notifications.yaml @@ -75,16 +75,7 @@ paths: "room_id": "!abcdefg:example.com", "ts": 1475508881945, "event": { - "sender": "@alice:example.com", - "type": "m.room.message", - "age": 124524, - "txn_id": "1234", - "content": { - "body": "I am a fish", - "msgtype": "m.text" - }, - "origin_server_ts": 1417731086797, - "event_id": "$74686972643033:example.com" + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" } } ] diff --git a/api/client-server/old_sync.yaml b/api/client-server/old_sync.yaml index c502c239..a79c3b32 100644 --- a/api/client-server/old_sync.yaml +++ b/api/client-server/old_sync.yaml @@ -64,18 +64,7 @@ paths: "start": "s3456_9_0", "end": "s3457_9_0", "chunk": [ - { - "age": 32, - "content": { - "body": "incoming message", - "msgtype": "m.text" - }, - "event_id": "$14328055551tzaee:localhost", - "origin_server_ts": 1432804485886, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "type": "m.room.message", - "sender": "@bob:localhost" - } + {"$ref": "definitions/event-schemas/examples/m.room.message$m.text"} ] } schema: @@ -142,16 +131,7 @@ paths: application/json: { "end": "s3456_9_0", "presence": [ - { - "content": { - "avatar_url": "mxc://localhost/GCmhgzMPRjqgpODLsNQzVuHZ#auto", - "displayname": "Bob", - "last_active_ago": 31053, - "presence": "online", - "user_id": "@bob:localhost" - }, - "type": "m.presence" - } + {"$ref": "definitions/event-schemas/examples/m.presence"} ], "account_data": [ { @@ -167,28 +147,12 @@ paths: "messages": { "chunk": [ { - "age": 343513403, - "content": { - "body": "foo", - "msgtype": "m.text" - }, - "event_id": "$14328044851tzTJS:localhost", - "origin_server_ts": 1432804485886, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "type": "m.room.message", - "sender": "@alice:localhost" + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" }, { - "age": 343511809, - "content": { - "body": "bar", - "msgtype": "m.text" - }, - "event_id": "$14328044872spjFg:localhost", - "origin_server_ts": 1432804487480, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "type": "m.room.message", - "sender": "@bob:localhost" + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "$ref": "definitions/event-schemas/examples/m.room.message$m.video" } ], "end": "s3456_9_0", @@ -197,81 +161,20 @@ paths: "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", "state": [ { - "age": 7148266897, - "content": { - "join_rule": "public" - }, - "event_id": "$14259997323TLwtb:localhost", - "origin_server_ts": 1425999732392, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "state_key": "", - "type": "m.room.join_rules", - "sender": "@alice:localhost" + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "$ref": "definitions/event-schemas/examples/m.room.join_rules" }, { - "age": 6547561012, - "content": { - "avatar_url": "mxc://localhost/fzysBrHpPEeTGANCVLXWXNMI#auto", - "membership": "join" - }, - "event_id": "$1426600438280zExKY:localhost", - "membership": "join", - "origin_server_ts": 1426600438277, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "state_key": "@alice:localhost", - "type": "m.room.member", - "sender": "@alice:localhost" + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "$ref": "definitions/event-schemas/examples/m.room.member" }, { - "age": 7148267200, - "content": { - "creator": "@alice:localhost" - }, - "event_id": "$14259997320KhbwJ:localhost", - "origin_server_ts": 1425999732089, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "state_key": "", - "type": "m.room.create", - "sender": "@alice:localhost" + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "$ref": "definitions/event-schemas/examples/m.room.create" }, { - "age": 1622568720, - "content": { - "avatar_url": "mxc://localhost/GCmhgzMPRjqgpODLsNQzVuHZ#auto", - "displayname": "Bob", - "membership": "join" - }, - "event_id": "$1431525430134MxlLX:localhost", - "origin_server_ts": 1431525430569, - "replaces_state": "$142652023736BSXcM:localhost", - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "state_key": "@bob:localhost", - "type": "m.room.member", - "sender": "@bob:localhost" - }, - { - "age": 7148267004, - "content": { - "ban": 50, - "events": { - "m.room.name": 100, - "m.room.power_levels": 100 - }, - "events_default": 0, - "kick": 50, - "redact": 50, - "state_default": 50, - "users": { - "@alice:localhost": 100 - }, - "users_default": 0 - }, - "event_id": "$14259997322mqfaq:localhost", - "origin_server_ts": 1425999732285, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "state_key": "", - "type": "m.room.power_levels", - "sender": "@alice:localhost" + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "$ref": "definitions/event-schemas/examples/m.room.power_levels" } ], "visibility": "private", @@ -423,16 +326,7 @@ paths: 200: description: The full event. examples: - application/json: { - "content": { - "body": "Hello world!", - "msgtype": "m.text" - }, - "room_id": "!wfgy43Sg4a:matrix.org", - "sender": "@bob:matrix.org", - "event_id": "$asfDuShaf7Gafaw:matrix.org", - "type": "m.room.message" - } + application/json: {"$ref": "definitions/event-schemas/examples/m.room.message$m.text"} schema: allOf: - "$ref": "definitions/event-schemas/schema/core-event-schema/event.yaml" diff --git a/api/client-server/peeking_events.yaml b/api/client-server/peeking_events.yaml index 2f66bae7..feac36f4 100644 --- a/api/client-server/peeking_events.yaml +++ b/api/client-server/peeking_events.yaml @@ -75,16 +75,8 @@ paths: "end": "s3457_9_0", "chunk": [ { - "age": 32, - "content": { - "body": "incoming message", - "msgtype": "m.text" - }, - "event_id": "$14328055551tzaee:localhost", - "origin_server_ts": 1432804485886, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "type": "m.room.message", - "sender": "@bob:localhost" + "room_id": "!somewhere:over.the.rainbow", + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" } ] } diff --git a/api/client-server/pushrules.yaml b/api/client-server/pushrules.yaml index e23c9189..a1cbc354 100644 --- a/api/client-server/pushrules.yaml +++ b/api/client-server/pushrules.yaml @@ -159,8 +159,13 @@ paths: ], "conditions": [ { - "is": "2", - "kind": "room_member_count" + "kind": "room_member_count", + "is": "2" + }, + { + "kind": "event_match", + "key": "type", + "pattern": "m.room.message" } ], "default": true, diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index e2d35d2c..71177d0c 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -29,7 +29,8 @@ paths: post: summary: Register for an account on this homeserver. description: |- - This API endpoint uses the `User-Interactive Authentication API`_. + This API endpoint uses the `User-Interactive Authentication API`_, except in + the cases where a guest account is being registered. Register for an account on this homeserver. @@ -59,6 +60,14 @@ paths: supplied by the client or generated by the server. The server may invalidate any access token previously associated with that device. See `Relationship between access tokens and devices`_. + + When registering a guest account, all parameters in the request body + with the exception of ``initial_device_display_name`` MUST BE ignored + by the server. The server MUST pick a ``device_id`` for the account + regardless of input. + + Any user ID returned by this API must conform to the grammar given in the + `Matrix specification <../appendices.html#user-identifiers>`_. operationId: register parameters: - in: query @@ -72,7 +81,7 @@ paths: enum: - guest - user - description: The kind of account to register. Defaults to `user`. + description: The kind of account to register. Defaults to ``user``. - in: body name: body schema: @@ -80,13 +89,11 @@ paths: properties: auth: description: |- - Additional authentication information for the - user-interactive authentication API. Note that this - information is *not* used to define how the registered user - should be authenticated, but is instead used to - authenticate the ``register`` call itself. It should be - left empty, or omitted, unless an earlier call returned an - response with status code 401. + Additional authentication information for the + user-interactive authentication API. Note that this + information is *not* used to define how the registered user + should be authenticated, but is instead used to + authenticate the ``register`` call itself. "$ref": "definitions/auth_data.yaml" bind_email: type: boolean @@ -94,6 +101,12 @@ paths: If true, the server binds the email used for authentication to the Matrix ID with the identity server. example: false + bind_msisdn: + type: boolean + description: |- + If true, the server binds the phone number used for authentication + to the Matrix ID with the identity server. + example: false username: type: string description: |- @@ -142,7 +155,7 @@ paths: The fully-qualified Matrix user ID (MXID) that has been registered. Any user ID returned by this API must conform to the grammar given in the - `Matrix specification `_. + `Matrix specification <../appendices.html#user-identifiers>`_. access_token: type: string description: |- @@ -194,6 +207,18 @@ paths: The homeserver requires additional authentication information. schema: "$ref": "definitions/auth_response.yaml" + 403: + description: |- + The homeserver does not permit registering the account. This response + can be used to identify that a particular ``kind`` of account is not + allowed, or that registration is generally not supported by the homeserver. + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "Registration is disabled" + } + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: @@ -204,35 +229,28 @@ paths: post: summary: Begins the validation process for an email to be used during registration. description: |- - Proxies the Identity Service API ``validate/email/requestToken``, but - first checks that the given email address is not already associated - with an account on this homeserver. See the Identity Service API for - further information. + The homeserver must check that the given email address is **not** + already associated with an account on this homeserver. The homeserver + has the choice of validating the email address itself, or proxying the + request to the ``/validate/email/requestToken`` Identity Service API. The + request should be proxied to the domain that is sent by the client in + the ``id_server``. It is imperative that the homeserver keep a list of + trusted Identity Servers and only proxies to those it trusts. operationId: requestTokenToRegisterEmail parameters: - in: body name: body required: true schema: - allOf: - - $ref: "../identity/definitions/request_email_validation.yaml" - - type: object - properties: - id_server: - type: string - description: |- - The hostname of the identity server to communicate with. May - optionally include a port. - example: "id.example.com" - required: ['id_server'] + $ref: "./definitions/request_email_validation.yaml" responses: 200: description: |- - An email has been sent to the specified address. - Note that this may be an email containing the validation token or it may be informing - the user of an error. + An email has been sent to the specified address. Note that this + may be an email containing the validation token or it may be + informing the user of an error. schema: - $ref: "../identity/definitions/sid.yaml" + $ref: "definitions/request_token_response.yaml" 403: description: The homeserver does not permit the address to be bound. schema: @@ -264,35 +282,28 @@ paths: post: summary: Requests a validation token be sent to the given phone number for the purpose of registering an account description: |- - Proxies the Identity Service API ``validate/msisdn/requestToken``, but - first checks that the given phone number is not already associated - with an account on this homeserver. See the Identity Service API for - further information. + The homeserver must check that the given phone number is **not** + already associated with an account on this homeserver. The homeserver + has the choice of validating the phone number itself, or proxying the + request to the ``/validate/msisdn/requestToken`` Identity Service API. The + request should be proxied to the domain that is sent by the client in + the ``id_server``. It is imperative that the homeserver keep a list of + trusted Identity Servers and only proxies to those it trusts. operationId: requestTokenToRegisterMSISDN parameters: - in: body name: body required: true schema: - allOf: - - $ref: "../identity/definitions/request_msisdn_validation.yaml" - - type: object - properties: - id_server: - type: string - description: |- - The hostname of the identity server to communicate with. May - optionally include a port. - example: "id.example.com" - required: ['id_server'] + $ref: "./definitions/request_msisdn_validation.yaml" responses: 200: description: |- - An SMS message has been sent to the specified phone number. - Note that this may be an SMS message containing the validation token or it may be informing - the user of an error. + An SMS message has been sent to the specified phone number. Note + that this may be an SMS message containing the validation token or + it may be informing the user of an error. schema: - $ref: "../identity/definitions/sid.yaml" + $ref: "definitions/request_token_response.yaml" 403: description: The homeserver does not permit the address to be bound. schema: @@ -326,13 +337,17 @@ paths: description: |- Changes the password for an account on this homeserver. - This API endpoint uses the `User-Interactive Authentication API`_. + This API endpoint uses the `User-Interactive Authentication API`_ to + ensure the user changing the password is actually the owner of the + account. An access token should be submitted to this endpoint if the client has an active session. The homeserver may change the flows available depending on whether a - valid access token is provided. + valid access token is provided. The homeserver SHOULD NOT revoke the + access token provided in the request, however all other access tokens + for the user should be revoked if the request succeeds. security: - accessToken: [] operationId: changePassword @@ -373,16 +388,25 @@ paths: post: summary: Requests a validation token be sent to the given email address for the purpose of resetting a user's password description: |- - Proxies the Identity Service API ``validate/email/requestToken``, but - first checks that the given email address **is** associated with an account - on this homeserver. This API should be used to request - validation tokens when authenticating for the - `account/password` endpoint. This API's parameters and response are - identical to that of the HS API |/register/email/requestToken|_ except that - `M_THREEPID_NOT_FOUND` may be returned if no account matching the + The homeserver must check that the given email address **is + associated** with an account on this homeserver. This API should be + used to request validation tokens when authenticating for the + ``/account/password`` endpoint. + + This API's parameters and response are identical to that of the + |/register/email/requestToken|_ endpoint, except that + ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the given email address could be found. The server may instead send an email to the given address prompting the user to create an account. - `M_THREEPID_IN_USE` may not be returned. + ``M_THREEPID_IN_USE`` may not be returned. + + The homeserver has the choice of validating the email address itself, + or proxying the request to the ``/validate/email/requestToken`` + Identity Service API. The request should be proxied to the domain that + is sent by the client in the ``id_server``. It is imperative that the + homeserver keep a list of trusted Identity Servers and only proxies to + those that it trusts. + .. |/register/email/requestToken| replace:: ``/register/email/requestToken`` @@ -393,22 +417,12 @@ paths: name: body required: true schema: - allOf: - - $ref: "../identity/definitions/request_email_validation.yaml" - - type: object - properties: - id_server: - type: string - description: |- - The hostname of the identity server to communicate with. May - optionally include a port. - example: "id.example.com" - required: ['id_server'] + $ref: "./definitions/request_email_validation.yaml" responses: 200: description: An email was sent to the given address. schema: - $ref: "../identity/definitions/sid.yaml" + $ref: "definitions/request_token_response.yaml" 403: description: |- The homeserver does not allow the third party identifier as a @@ -435,16 +449,24 @@ paths: post: summary: Requests a validation token be sent to the given phone number for the purpose of resetting a user's password. description: |- - Proxies the Identity Service API ``validate/msisdn/requestToken``, but - first checks that the given phone number **is** associated with an account - on this homeserver. This API should be used to request - validation tokens when authenticating for the - `account/password` endpoint. This API's parameters and response are - identical to that of the HS API |/register/msisdn/requestToken|_ except that - `M_THREEPID_NOT_FOUND` may be returned if no account matching the - given phone number could be found. The server may instead send an - SMS message to the given address prompting the user to create an account. - `M_THREEPID_IN_USE` may not be returned. + The homeserver must check that the given phone number **is + associated** with an account on this homeserver. This API should be + used to request validation tokens when authenticating for the + ``/account/password`` endpoint. + + This API's parameters and response are identical to that of the + |/register/msisdn/requestToken|_ endpoint, except that + ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the + given phone number could be found. The server may instead send the SMS + to the given phone number prompting the user to create an account. + ``M_THREEPID_IN_USE`` may not be returned. + + The homeserver has the choice of validating the phone number itself, or + proxying the request to the ``/validate/msisdn/requestToken`` Identity + Service API. The request should be proxied to the domain that is sent + by the client in the ``id_server``. It is imperative that the + homeserver keep a list of trusted Identity Servers and only proxies to + those that it trusts. .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken`` @@ -455,22 +477,12 @@ paths: name: body required: true schema: - allOf: - - $ref: "../identity/definitions/request_msisdn_validation.yaml" - - type: object - properties: - id_server: - type: string - description: |- - The hostname of the identity server to communicate with. May - optionally include a port. - example: "id.example.com" - required: ['id_server'] + $ref: "../identity/definitions/request_msisdn_validation.yaml" responses: 200: description: An SMS message was sent to the given phone number. schema: - $ref: "../identity/definitions/sid.yaml" + $ref: "definitions/request_token_response.yaml" 403: description: |- The homeserver does not allow the third party identifier as a @@ -520,13 +532,39 @@ paths: description: |- Additional authentication information for the user-interactive authentication API. "$ref": "definitions/auth_data.yaml" + id_server: + type: string + description: |- + The identity server to unbind all of the user's 3PIDs from. + If not provided, the homeserver MUST use the ``id_server`` + that was originally use to bind each identifier. If the + homeserver does not know which ``id_server`` that was, + it must return an ``id_server_unbind_result`` of + ``no-support``. + example: "example.org" responses: 200: description: The account has been deactivated. - examples: - application/json: {} schema: type: object + properties: + id_server_unbind_result: + type: string + enum: + - "success" + - "no-support" + description: |- + An indicator as to whether or not the homeserver was able to unbind + the user's 3PIDs from the identity server(s). ``success`` indicates + that all identifiers have been unbound from the identity server while + ``no-support`` indicates that one or more identifiers failed to unbind + due to the identity server refusing the request or the homeserver + being unable to determine an identity server to unbind from. This + must be ``success`` if the homeserver has no identifiers to unbind + for the user. + example: "success" + required: + - id_server_unbind_result 401: description: |- The homeserver requires additional authentication information. diff --git a/api/client-server/room_initial_sync.yaml b/api/client-server/room_initial_sync.yaml index c27f0f24..72e56ba9 100644 --- a/api/client-server/room_initial_sync.yaml +++ b/api/client-server/room_initial_sync.yaml @@ -43,28 +43,12 @@ paths: "messages": { "chunk": [ { - "age": 343513403, - "content": { - "body": "foo", - "msgtype": "m.text" - }, - "event_id": "$14328044851tzTJS:example.com", - "origin_server_ts": 1432804485886, "room_id": "!636q39766251:example.com", - "type": "m.room.message", - "sender": "@alice:example.com" + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" }, { - "age": 343511809, - "content": { - "body": "bar", - "msgtype": "m.text" - }, - "event_id": "$14328044872spjFg:example.com", - "origin_server_ts": 1432804487480, "room_id": "!636q39766251:example.com", - "type": "m.room.message", - "sender": "@bob:example.com" + "$ref": "definitions/event-schemas/examples/m.room.message$m.file" } ], "end": "s3456_9_0", @@ -73,81 +57,20 @@ paths: "room_id": "!636q39766251:example.com", "state": [ { - "age": 7148266897, - "content": { - "join_rule": "public" - }, - "event_id": "$14259997323TLwtb:example.com", - "origin_server_ts": 1425999732392, "room_id": "!636q39766251:example.com", - "state_key": "", - "type": "m.room.join_rules", - "sender": "@alice:example.com" + "$ref": "definitions/event-schemas/examples/m.room.join_rules" }, { - "age": 6547561012, - "content": { - "avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto", - "membership": "join" - }, - "event_id": "$1426600438280zExKY:example.com", - "membership": "join", - "origin_server_ts": 1426600438277, "room_id": "!636q39766251:example.com", - "state_key": "@alice:example.com", - "type": "m.room.member", - "sender": "@alice:example.com" + "$ref": "definitions/event-schemas/examples/m.room.member" }, { - "age": 7148267200, - "content": { - "creator": "@alice:example.com" - }, - "event_id": "$14259997320KhbwJ:example.com", - "origin_server_ts": 1425999732089, "room_id": "!636q39766251:example.com", - "state_key": "", - "type": "m.room.create", - "sender": "@alice:example.com" + "$ref": "definitions/event-schemas/examples/m.room.create" }, { - "age": 1622568720, - "content": { - "avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto", - "displayname": "Bob", - "membership": "join" - }, - "event_id": "$1431525430134MxlLX:example.com", - "origin_server_ts": 1431525430569, - "replaces_state": "$142652023736BSXcM:example.com", "room_id": "!636q39766251:example.com", - "state_key": "@bob:example.com", - "type": "m.room.member", - "sender": "@bob:example.com" - }, - { - "age": 7148267004, - "content": { - "ban": 50, - "events": { - "m.room.name": 100, - "m.room.power_levels": 100 - }, - "events_default": 0, - "kick": 50, - "redact": 50, - "state_default": 50, - "users": { - "@alice:example.com": 100 - }, - "users_default": 0 - }, - "event_id": "$14259997322mqfaq:example.com", - "origin_server_ts": 1425999732285, - "room_id": "!636q39766251:example.com", - "state_key": "", - "type": "m.room.power_levels", - "sender": "@alice:example.com" + "$ref": "definitions/event-schemas/examples/m.room.power_levels" } ], "visibility": "private", diff --git a/api/client-server/room_state.yaml b/api/client-server/room_state.yaml index bda66eb8..62168f26 100644 --- a/api/client-server/room_state.yaml +++ b/api/client-server/room_state.yaml @@ -31,6 +31,9 @@ paths: put: summary: Send a state event to the given room. description: | + .. For backwards compatibility with older links... + .. _`put-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state-eventtype`: + State events can be sent using this endpoint. These events will be overwritten if ````, ```` and ```` all match. @@ -61,7 +64,9 @@ paths: - in: path type: string name: stateKey - description: The state_key for the state to send. Defaults to the empty string. + description: |- + The state_key for the state to send. Defaults to the empty string. When + an empty string, the trailing slash on this endpoint is optional. required: true x-example: "@alice:example.com" - in: body @@ -70,7 +75,7 @@ paths: type: object example: { "membership": "join", - "avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto", + "avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF", "displayname": "Alice Margatroid" } responses: @@ -99,68 +104,3 @@ paths: } tags: - Room participation - "/rooms/{roomId}/state/{eventType}": - put: - summary: Send a state event to the given room. - description: | - State events can be sent using this endpoint. This endpoint is - equivalent to calling `/rooms/{roomId}/state/{eventType}/{stateKey}` - with an empty `stateKey`. Previous state events with matching - `` and ``, and empty ``, will be overwritten. - - Requests to this endpoint **cannot use transaction IDs** - like other ``PUT`` paths because they cannot be differentiated from the - ``state_key``. Furthermore, ``POST`` is unsupported on state paths. - - The body of the request should be the content object of the event; the - fields in this object will vary depending on the type of event. See - `Room Events`_ for the ``m.`` event specification. - operationId: setRoomState - security: - - accessToken: [] - parameters: - - in: path - type: string - name: roomId - description: The room to set the state in - required: true - x-example: "!636q39766251:example.com" - - in: path - type: string - name: eventType - description: The type of event to send. - required: true - x-example: "m.room.name" - - in: body - name: body - schema: - type: object - example: { - "name": "New name for the room" - } - responses: - 200: - description: "An ID for the sent event." - examples: - application/json: { - "event_id": "$YUwRidLecu:example.com" - } - schema: - type: object - properties: - event_id: - type: string - description: |- - A unique identifier for the event. - 403: - description: |- - The sender doesn't have permission to send the event into the room. - schema: - $ref: "definitions/errors/error.yaml" - examples: - application/json: { - "errcode": "M_FORBIDDEN", - "error": "You do not have permission to send the event." - } - tags: - - Room participation diff --git a/api/client-server/rooms.yaml b/api/client-server/rooms.yaml index cc1f2bf7..566e695a 100644 --- a/api/client-server/rooms.yaml +++ b/api/client-server/rooms.yaml @@ -42,7 +42,7 @@ paths: name: roomId description: The ID of the room the event is in. required: true - x-example: "!asfDuShaf7Gafaw:matrix.org" + x-example: "!636q39766251:matrix.org" - in: path type: string name: eventId @@ -54,26 +54,30 @@ paths: description: The full event. examples: application/json: { - "content": { - "body": "Hello world!", - "msgtype": "m.text" - }, - "room_id": "!wfgy43Sg4a:matrix.org", - "sender": "@bob:matrix.org", - "event_id": "$asfDuShaf7Gafaw:matrix.org", - "type": "m.room.message" - } + "room_id": "!636q39766251:matrix.org", + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" + } schema: allOf: - "$ref": "definitions/event-schemas/schema/core-event-schema/event.yaml" 404: description: The event was not found or you do not have permission to read this event. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Event not found." + } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room participation "/rooms/{roomId}/state/{eventType}/{stateKey}": get: summary: Get the state identified by the type and key. description: |- + .. For backwards compatibility with older links... + .. _`get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state-eventtype`: + Looks up the contents of a state event in a room. If the user is joined to the room then the state is taken from the current state of the room. If the user has left the room then the state is @@ -97,7 +101,9 @@ paths: - in: path type: string name: stateKey - description: The key of the state to look up. + description: |- + The key of the state to look up. Defaults to an empty string. When + an empty string, the trailing slash on this endpoint is optional. required: true x-example: "" responses: @@ -116,48 +122,6 @@ paths: member of the room. tags: - Room participation - "/rooms/{roomId}/state/{eventType}": - get: - summary: Get the state identified by the type, with the empty state key. - description: |- - Looks up the contents of a state event in a room. If the user is - joined to the room then the state is taken from the current - state of the room. If the user has left the room then the state is - taken from the state of the room when they left. - - This looks up the state event with the empty state key. - operationId: getRoomStateByType - security: - - accessToken: [] - parameters: - - in: path - type: string - name: roomId - description: The room to look up the state in. - required: true - x-example: "!636q39766251:example.com" - - in: path - type: string - name: eventType - description: The type of state to look up. - required: true - x-example: "m.room.name" - responses: - 200: - description: The content of the state event. - examples: - application/json: { - "name": "Example room name"} - schema: - type: object - 404: - description: The room has no state with the given type or key. - 403: - description: > - You aren't a member of the room and weren't previously a - member of the room. - tags: - - Room participation "/rooms/{roomId}/state": get: summary: Get all state events in the current state of a room. @@ -178,84 +142,23 @@ paths: description: The current state of the room examples: application/json: [ - { - "age": 7148266897, - "content": { - "join_rule": "public" - }, - "event_id": "$14259997323TLwtb:example.com", - "origin_server_ts": 1425999732392, - "room_id": "!636q39766251:example.com", - "state_key": "", - "type": "m.room.join_rules", - "sender": "@alice:example.com" - }, - { - "age": 6547561012, - "content": { - "avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto", - "membership": "join" - }, - "event_id": "$1426600438280zExKY:example.com", - "membership": "join", - "origin_server_ts": 1426600438277, - "room_id": "!636q39766251:example.com", - "state_key": "@alice:example.com", - "type": "m.room.member", - "sender": "@alice:example.com" - }, - { - "age": 7148267200, - "content": { - "creator": "@alice:example.com" - }, - "event_id": "$14259997320KhbwJ:example.com", - "origin_server_ts": 1425999732089, - "room_id": "!636q39766251:example.com", - "state_key": "", - "type": "m.room.create", - "sender": "@alice:example.com" - }, - { - "age": 1622568720, - "content": { - "avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto", - "displayname": "Bob", - "membership": "join" - }, - "event_id": "$1431525430134MxlLX:example.com", - "origin_server_ts": 1431525430569, - "replaces_state": "$142652023736BSXcM:example.com", - "room_id": "!636q39766251:example.com", - "state_key": "@bob:example.com", - "type": "m.room.member", - "sender": "@bob:example.com" - }, - { - "age": 7148267004, - "content": { - "ban": 50, - "events": { - "m.room.name": 100, - "m.room.power_levels": 100 - }, - "events_default": 0, - "kick": 50, - "redact": 50, - "state_default": 50, - "users": { - "@alice:example.com": 100 - }, - "users_default": 0 - }, - "event_id": "$14259997322mqfaq:example.com", - "origin_server_ts": 1425999732285, - "room_id": "!636q39766251:example.com", - "state_key": "", - "type": "m.room.power_levels", - "sender": "@alice:example.com" - } - ] + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.join_rules" + }, + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.member" + }, + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.create" + }, + { + "room_id": "!636q39766251:example.com", + "$ref": "definitions/event-schemas/examples/m.room.power_levels" + } + ] schema: type: array title: RoomState @@ -288,6 +191,44 @@ paths: description: The room to get the member events for. required: true x-example: "!636q39766251:example.com" + - in: query + name: at + type: string + description: |- + The point in time (pagination token) to return members for in the room. + This token can be obtained from a ``prev_batch`` token returned for + each room by the sync API. Defaults to the current state of the room, + as determined by the server. + x-example: "YWxsCgpOb25lLDM1ODcwOA" + # XXX: As mentioned in MSC1227, replacing `[not_]membership` with a JSON + # filter might be a better alternative. + # See https://github.com/matrix-org/matrix-doc/issues/1337 + - in: query + name: membership + type: string + enum: + - join + - invite + - leave + - ban + description: |- + The kind of membership to filter for. Defaults to no filtering if + unspecified. When specified alongside ``not_membership``, the two + parameters create an 'or' condition: either the membership *is* + the same as ``membership`` **or** *is not* the same as ``not_membership``. + x-example: "join" + - in: query + name: not_membership + type: string + enum: + - join + - invite + - leave + - ban + description: |- + The kind of membership to exclude from the results. Defaults to no + filtering if unspecified. + x-example: leave security: - accessToken: [] responses: @@ -300,33 +241,8 @@ paths: application/json: { "chunk": [ { - "age": 6547561012, - "content": { - "avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto", - "membership": "join" - }, - "event_id": "$1426600438280zExKY:example.com", - "membership": "join", - "origin_server_ts": 1426600438277, "room_id": "!636q39766251:example.com", - "state_key": "@alice:example.com", - "type": "m.room.member", - "sender": "@alice:example.com" - }, - { - "age": 1622568720, - "content": { - "avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto", - "displayname": "Bob", - "membership": "join" - }, - "event_id": "$1431525430134MxlLX:example.com", - "origin_server_ts": 1431525430569, - "replaces_state": "$142652023736BSXcM:example.com", - "room_id": "!636q39766251:example.com", - "state_key": "@bob:example.com", - "type": "m.room.member", - "sender": "@bob:example.com" + "$ref": "definitions/event-schemas/examples/m.room.member" } ] } diff --git a/api/client-server/search.yaml b/api/client-server/search.yaml index 4a5f4515..4fe72d5b 100644 --- a/api/client-server/search.yaml +++ b/api/client-server/search.yaml @@ -280,7 +280,7 @@ paths: Any groups that were requested. The outer ``string`` key is the group key requested (eg: ``room_id`` - or ``sender``). The inner ``string`` key is the grouped value (eg: + or ``sender``). The inner ``string`` key is the grouped value (eg: a room's ID or a user's ID). additionalProperties: type: object @@ -347,16 +347,9 @@ paths: { "rank": 0.00424866, "result": { - "age": 526228296, - "content": { - "body": "Test content martians and men", - "msgtype": "m.text" - }, - "event_id": "$144429830826TWwbB:localhost", - "origin_server_ts": 1444298308034, "room_id": "!qPewotXpIctQySfjSy:localhost", - "type": "m.room.message", - "sender": "@test:localhost" + "event_id": "$144429830826TWwbB:localhost", + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" } } ] diff --git a/api/client-server/sync.yaml b/api/client-server/sync.yaml index f204152a..00a332d9 100644 --- a/api/client-server/sync.yaml +++ b/api/client-server/sync.yaml @@ -34,6 +34,20 @@ paths: Clients use this API when they first log in to get an initial snapshot of the state on the server, and then continue to call this API to get incremental deltas to the state, and to receive new messages. + + *Note*: This endpoint supports lazy-loading. See `Filtering <#filtering>`_ + for more information. Lazy-loading members is only supported on a ``StateFilter`` + for this endpoint. When lazy-loading is enabled, servers MUST include the + syncing user's own membership event when they join a room, or when the + full state of rooms is requested, to aid discovering the user's avatar & + displayname. + + Like other members, the user's own membership event is eligible + for being considered redundant by the server. When a sync is ``limited``, + the server MUST return membership events for events in the gap + (between ``since`` and the start of the returned timeline), regardless + as to whether or not they are redundant. This ensures that joins/leaves + and profile changes which occur during the gap are not lost. operationId: sync security: - accessToken: [] @@ -49,6 +63,8 @@ paths: requests. Creating a filter using the filter API is recommended for clients that reuse the same filter multiple times, for example in long poll requests. + + See `Filtering <#filtering>`_ for more information. x-example: "66696p746572" - in: query name: since @@ -125,6 +141,50 @@ paths: title: Joined Room type: object properties: + summary: + title: RoomSummary + type: object + description: |- + Information about the room which clients may need to + correctly render it to users. + properties: + "m.heroes": + type: array + description: |- + The users which can be used to generate a room name + if the room does not have one. Required if the room's + ``m.room.name`` or ``m.room.canonical_alias`` state events + are unset or empty. + + This should be the first 5 members of the room, ordered + by stream ordering, which are joined or invited. The + list must never include the client's own user ID. When + no joined or invited members are available, this should + consist of the banned and left users. More than 5 members + may be provided, however less than 5 should only be provided + when there are less than 5 members to represent. + + When lazy-loading room members is enabled, the membership + events for the heroes MUST be included in the ``state``, + unless they are redundant. When the list of users changes, + the server notifies the client by sending a fresh list of + heroes. If there are no changes since the last sync, this + field may be omitted. + items: + type: string + "m.joined_member_count": + type: integer + description: |- + The number of users with ``membership`` of ``join``, + including the client's own user ID. If this field has + not changed since the last sync, it may be omitted. + Required otherwise. + "m.invited_member_count": + type: integer + description: |- + The number of users with ``membership`` of ``invite``. + If this field has not changed since the last sync, it + may be omitted. Required otherwise. state: title: State type: object @@ -167,11 +227,13 @@ paths: this room. allOf: - $ref: "definitions/event_batch.yaml" - "unread_notifications": + unread_notifications: title: Unread Notification Counts type: object description: |- - Counts of unread notifications for this room + Counts of unread notifications for this room. See the + `Receiving notifications section <#receiving-notifications>`_ + for more information on how these are calculated. properties: highlight_count: title: Highlighted notification count @@ -212,30 +274,7 @@ paths: events: description: The StrippedState events that form the invite state. items: - description: |- - A stripped down state event, with only the ``type``, ``state_key``, - ``sender``, and ``content`` keys. - properties: - content: - description: The ``content`` for the event. - title: EventContent - type: object - state_key: - description: The ``state_key`` for the event. - type: string - type: - description: The ``type`` for the event. - type: string - sender: - description: The ``sender`` for the event. - type: string - required: - - type - - state_key - - content - - sender - title: StrippedState - type: object + $ref: "definitions/event-schemas/schema/stripped_state.yaml" type: array leave: title: Left rooms @@ -310,11 +349,7 @@ paths: "next_batch": "s72595_4483_1934", "presence": { "events": [ - { - "sender": "@alice:example.com", - "type": "m.presence", - "content": {"presence": "online"} - } + {"$ref": "definitions/event-schemas/examples/m.presence"} ] }, "account_data": { @@ -330,39 +365,31 @@ paths: "rooms": { "join": { "!726s6s6q:example.com": { + "summary": { + "m.heroes": [ + "@alice:example.com", + "@bob:example.com" + ], + "m.joined_member_count": 2, + "m.invited_member_count": 0 + }, "state": { "events": [ { - "sender": "@alice:example.com", - "type": "m.room.member", - "state_key": "@alice:example.com", - "content": {"membership": "join"}, - "origin_server_ts": 1417731086795, - "event_id": "$66697273743031:example.com" + "room_id": "!726s6s6q:example.com", + "$ref": "definitions/event-schemas/examples/m.room.member" } ] }, "timeline": { "events": [ { - "sender": "@bob:example.com", - "type": "m.room.member", - "state_key": "@bob:example.com", - "content": {"membership": "join"}, - "prev_content": {"membership": "invite"}, - "origin_server_ts": 1417731086795, - "event_id": "$7365636s6r6432:example.com" + "room_id": "!726s6s6q:example.com", + "$ref": "definitions/event-schemas/examples/m.room.member" }, { - "sender": "@alice:example.com", - "type": "m.room.message", - "txn_id": "1234", - "content": { - "body": "I am a fish", - "msgtype": "m.text" - }, - "origin_server_ts": 1417731086797, - "event_id": "$74686972643033:example.com" + "room_id": "!726s6s6q:example.com", + "$ref": "definitions/event-schemas/examples/m.room.message$m.text" } ], "limited": true, @@ -370,18 +397,12 @@ paths: }, "ephemeral": { "events": [ - { - "type": "m.typing", - "content": {"user_ids": ["@alice:example.com"]} - } + {"$ref": "definitions/event-schemas/examples/m.typing"} ] }, "account_data": { "events": [ - { - "type": "m.tag", - "content": {"tags": {"work": {"order": 1}}} - }, + {"$ref": "definitions/event-schemas/examples/m.tag"}, { "type": "org.example.custom.room.config", "content": { diff --git a/api/identity/associations.yaml b/api/identity/associations.yaml index 152a0a9b..82c70fb8 100644 --- a/api/identity/associations.yaml +++ b/api/identity/associations.yaml @@ -30,6 +30,7 @@ paths: description: |- Determines if a given 3pid has been validated by a user. operationId: getValidated3pid + deprecated: true parameters: - in: query type: string @@ -104,6 +105,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: bind + deprecated: true parameters: - in: body name: body @@ -201,3 +203,100 @@ paths: } schema: $ref: "../client-server/definitions/errors/error.yaml" + "/3pid/unbind": + post: + summary: Remove an association between a session and a Matrix user ID. + description: |- + Remove an association between a session and a Matrix user ID. + + Future calls to ``/lookup`` for any of the session's 3pids will not + return the removed association. + + 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`` parameters, + as per ``/3pid/bind``, which proves ownership of the 3PID. + + If this endpoint returns a JSON Matrix error, that error should be passed + through to the client requesting an unbind through a homeserver, if the + homeserver is acting on behalf of a client. + operationId: unbind + deprecated: true + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "mxid": "@ears:example.org", + "threepid": { + "medium": "email", + "address": "monkeys_have_ears@example.org" + } + } + properties: + sid: + type: string + description: The Session ID generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret passed to the ``requestToken`` call. + mxid: + type: string + description: The Matrix user ID to remove from the 3pids. + threepid: + type: object + title: 3PID + description: |- + The 3PID to remove. Must match the 3PID used to generate the session + if using ``sid`` and ``client_secret`` to authenticate this request. + properties: + medium: + type: string + description: |- + A medium from the `3PID Types`_ Appendix, matching the medium + of the identifier to unbind. + address: + type: string + description: The 3PID address to remove. + required: ['medium', 'address'] + required: ["threepid", "mxid"] + responses: + 200: + description: The association was successfully removed. + examples: + application/json: {} + schema: + type: object + 400: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. + 404: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. + 403: + description: |- + The credentials supplied to authenticate the request were invalid. + This may also be returned if the identity server does not support + the chosen authentication method (such as blocking homeservers from + unbinding identifiers). + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "Invalid homeserver signature" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + 501: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. diff --git a/api/identity/definitions/request_email_validation.yaml b/api/identity/definitions/request_email_validation.yaml index 1194a18e..1a7502c7 100644 --- a/api/identity/definitions/request_email_validation.yaml +++ b/api/identity/definitions/request_email_validation.yaml @@ -39,12 +39,14 @@ properties: avoid repeatedly sending the same email in the case of request retries between the POSTing user and the identity server. The client should increment this value if they desire a new - email (e.g. a reminder) to be sent. + email (e.g. a reminder) to be sent. If they do not, the server + should respond with success but not resend the email. example: 1 next_link: type: string description: |- - Optional. When the validation is completed, the identity - server will redirect the user to this URL. + Optional. When the validation is completed, the identity server will + redirect the user to this URL. This option is ignored when submitting + 3PID validation information through a POST request. example: "https://example.org/congratulations.html" required: ["client_secret", "email", "send_attempt"] diff --git a/api/identity/definitions/request_msisdn_validation.yaml b/api/identity/definitions/request_msisdn_validation.yaml index 1a8c0cf8..018bd733 100644 --- a/api/identity/definitions/request_msisdn_validation.yaml +++ b/api/identity/definitions/request_msisdn_validation.yaml @@ -51,7 +51,8 @@ properties: next_link: type: string description: |- - Optional. When the validation is completed, the identity - server will redirect the user to this URL. + Optional. When the validation is completed, the identity server will + redirect the user to this URL. This option is ignored when submitting + 3PID validation information through a POST request. example: "https://example.org/congratulations.html" required: ["client_secret", "country", "phone_number", "send_attempt"] diff --git a/api/identity/definitions/security.yaml b/api/identity/definitions/security.yaml new file mode 100644 index 00000000..ef49ff5c --- /dev/null +++ b/api/identity/definitions/security.yaml @@ -0,0 +1,18 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +accessToken: + type: apiKey + description: The access_token returned by a call to ``/register``. + name: access_token + in: query diff --git a/api/identity/email_associations.yaml b/api/identity/email_associations.yaml index 38186432..6ac28cd0 100644 --- a/api/identity/email_associations.yaml +++ b/api/identity/email_associations.yaml @@ -46,6 +46,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: emailRequestToken + deprecated: true parameters: - in: body name: body @@ -92,6 +93,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: emailSubmitTokenPost + deprecated: true parameters: - in: body name: body @@ -142,6 +144,7 @@ paths: Note that, in contrast with the POST version, this endpoint will be used by end-users, and so the response should be human-readable. operationId: emailSubmitTokenGet + deprecated: true parameters: - in: query type: string diff --git a/api/identity/invitation_signing.yaml b/api/identity/invitation_signing.yaml index 4e10e2b5..e2ac28b0 100644 --- a/api/identity/invitation_signing.yaml +++ b/api/identity/invitation_signing.yaml @@ -33,6 +33,7 @@ paths: The identity server will look up ``token`` which was stored in a call to ``store-invite``, and fetch the sender of the invite. operationId: blindlySignStuff + deprecated: true parameters: - in: body name: body diff --git a/api/identity/lookup.yaml b/api/identity/lookup.yaml index fd50f94b..166b0962 100644 --- a/api/identity/lookup.yaml +++ b/api/identity/lookup.yaml @@ -32,6 +32,7 @@ paths: summary: Look up the Matrix user ID for a 3pid. description: Look up the Matrix user ID for a 3pid. operationId: lookupUser + deprecated: true parameters: - in: query type: string @@ -101,6 +102,7 @@ paths: summary: Lookup Matrix user IDs for a list of 3pids. description: Lookup Matrix user IDs for a list of 3pids. operationId: lookupUsers + deprecated: true parameters: - in: body name: body diff --git a/api/identity/phone_associations.yaml b/api/identity/phone_associations.yaml index b9933f1a..28312a4d 100644 --- a/api/identity/phone_associations.yaml +++ b/api/identity/phone_associations.yaml @@ -46,6 +46,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: msisdnRequestToken + deprecated: true parameters: - in: body name: body @@ -94,6 +95,7 @@ paths: ``application/x-form-www-urlencoded`` data. However, this usage is deprecated. operationId: msisdnSubmitTokenPost + deprecated: true parameters: - in: body name: body @@ -144,6 +146,7 @@ paths: Note that, in contrast with the POST version, this endpoint will be used by end-users, and so the response should be human-readable. operationId: msisdnSubmitTokenGet + deprecated: true parameters: - in: query type: string diff --git a/api/identity/ping.yaml b/api/identity/ping.yaml index 05e12a87..d7249a77 100644 --- a/api/identity/ping.yaml +++ b/api/identity/ping.yaml @@ -36,6 +36,7 @@ paths: This is primarly used for auto-discovery and health check purposes by entities acting as a client for the identity server. operationId: ping + deprecated: true responses: 200: description: An identity server is ready to serve requests. diff --git a/api/identity/pubkey.yaml b/api/identity/pubkey.yaml index e657c61c..a585e7ec 100644 --- a/api/identity/pubkey.yaml +++ b/api/identity/pubkey.yaml @@ -30,6 +30,7 @@ paths: description: |- Get the public key for the passed key ID. operationId: getPubKey + deprecated: true parameters: - in: path type: string @@ -72,6 +73,7 @@ paths: Check whether a long-term public key is valid. The response should always be the same, provided the key exists. operationId: isPubKeyValid + deprecated: true parameters: - in: query type: string @@ -101,6 +103,7 @@ paths: description: |- Check whether a short-term public key is valid. operationId: isEphemeralPubKeyValid + deprecated: true parameters: - in: query type: string diff --git a/api/identity/store_invite.yaml b/api/identity/store_invite.yaml index 69103294..4a5d525d 100644 --- a/api/identity/store_invite.yaml +++ b/api/identity/store_invite.yaml @@ -50,31 +50,67 @@ paths: requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``. Currently, invites may only be issued for 3pids of the ``email`` medium. + + Optional fields in the request should be populated to the best of the + server's ability. Identity servers may use these variables when notifying + the ``address`` of the pending invite for display purposes. operationId: storeInvite + deprecated: true parameters: - in: body name: body schema: type: object - example: { - "medium": "email", - "address": "foo@bar.baz", - "room_id": "!something:example.tld", - "sender": "@bob:example.com" - } properties: medium: type: string description: The literal string ``email``. + example: "email" address: type: string description: The email address of the invited user. + example: "foo@example.com" room_id: type: string description: The Matrix room ID to which the user is invited + example: "!something:example.org" sender: type: string description: The Matrix user ID of the inviting user + example: "@bob:example.com" + room_alias: + type: string + description: |- + The Matrix room alias for the room to which the user is + invited. This should be retrieved from the ``m.room.canonical_alias`` + state event. + example: "#somewhere:exmaple.org" + room_avatar_url: + type: string + description: |- + The Content URI for the room to which the user is invited. This should + be retrieved from the ``m.room.avatar`` state event. + example: "mxc://example.org/s0meM3dia" + room_join_rules: + type: string + description: |- + The ``join_rule`` for the room to which the user is invited. This should + be retrieved from the ``m.room.join_rules`` state event. + example: "public" + room_name: + type: string + description: |- + The name of the room to which the user is invited. This should be retrieved + from the ``m.room.name`` state event. + example: "Bob's Emporium of Messages" + sender_display_name: + type: string + description: The display name of the user ID initiating the invite. + example: "Bob Smith" + sender_avatar_url: + type: string + description: The Content URI for the avatar of the user ID initiating the invite. + example: "mxc://example.org/an0th3rM3dia" required: ["medium", "address", "room_id", "sender"] responses: 200: diff --git a/api/identity/v2_associations.yaml b/api/identity/v2_associations.yaml new file mode 100644 index 00000000..d1b29a8f --- /dev/null +++ b/api/identity/v2_associations.yaml @@ -0,0 +1,308 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Establishing Associations API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/3pid/getValidated3pid": + get: + summary: Check whether ownership of a 3pid was validated. + description: |- + Determines if a given 3pid has been validated by a user. + operationId: getValidated3pidV2 + security: + - accessToken: [] + parameters: + - in: query + type: string + name: sid + description: The Session ID generated by the ``requestToken`` call. + required: true + x-example: 1234 + - in: query + type: string + name: client_secret + description: The client secret passed to the ``requestToken`` call. + required: true + x-example: monkeys_are_GREAT + responses: + 200: + description: Validation information for the session. + examples: + application/json: { + "medium": "email", + "validated_at": 1457622739026, + "address": "louise@bobs.burgers" + } + schema: + type: object + properties: + medium: + type: string + description: The medium type of the 3pid. + address: + type: string + description: The address of the 3pid being looked up. + validated_at: + type: integer + description: |- + Timestamp, in milliseconds, indicating the time that the 3pid + was validated. + required: ['medium', 'address', 'validated_at'] + 400: + description: |- + The session has not been validated. + + If the session has not been validated, then ``errcode`` will be + ``M_SESSION_NOT_VALIDATED``. If the session has timed out, then + ``errcode`` will be ``M_SESSION_EXPIRED``. + examples: + application/json: { + "errcode": "M_SESSION_NOT_VALIDATED", + "error": "This validation session has not yet been completed" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + 404: + description: The Session ID or client secret were not found. + examples: + application/json: { + "errcode": "M_NO_VALID_SESSION", + "error": "No valid session was found matching that sid and client secret" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/3pid/bind": + post: + summary: Publish an association between a session and a Matrix user ID. + description: |- + Publish an association between a session and a Matrix user ID. + + Future calls to ``/lookup`` for any of the session\'s 3pids will return + this association. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: bindV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "mxid": "@ears:matrix.org" + } + properties: + sid: + type: string + description: The Session ID generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret passed to the ``requestToken`` call. + mxid: + type: string + description: The Matrix user ID to associate with the 3pids. + required: ["sid", "client_secret", "mxid"] + responses: + 200: + description: The association was published. + examples: + application/json: { + "address": "louise@bobs.burgers", + "medium": "email", + "mxid": "@ears:matrix.org", + "not_before": 1428825849161, + "not_after": 4582425849161, + "ts": 1428825849161, + "signatures": { + "matrix.org": { + "ed25519:0": "ENiU2YORYUJgE6WBMitU0mppbQjidDLanAusj8XS2nVRHPu+0t42OKA/r6zV6i2MzUbNQ3c3MiLScJuSsOiVDQ" + } + } + } + schema: + type: object + properties: + address: + type: string + description: The 3pid address of the user being looked up. + medium: + type: string + description: The medium type of the 3pid. + mxid: + type: string + description: The Matrix user ID associated with the 3pid. + not_before: + type: integer + description: A unix timestamp before which the association is not known to be valid. + not_after: + type: integer + description: A unix timestamp after which the association is not known to be valid. + ts: + type: integer + description: The unix timestamp at which the association was verified. + signatures: + type: object + description: |- + The signatures of the verifying identity servers which show that the + association should be trusted, if you trust the verifying identity + services. + $ref: "../../schemas/server-signatures.yaml" + required: + - address + - medium + - mxid + - not_before + - not_after + - ts + - signatures + 400: + description: |- + The association was not published. + + If the session has not been validated, then ``errcode`` will be + ``M_SESSION_NOT_VALIDATED``. If the session has timed out, then + ``errcode`` will be ``M_SESSION_EXPIRED``. + examples: + application/json: { + "errcode": "M_SESSION_NOT_VALIDATED", + "error": "This validation session has not yet been completed" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + 404: + description: The Session ID or client secret were not found + examples: + application/json: { + "errcode": "M_NO_VALID_SESSION", + "error": "No valid session was found matching that sid and client secret" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/3pid/unbind": + post: + summary: Remove an association between a session and a Matrix user ID. + description: |- + Remove an association between a session and a Matrix user ID. + + Future calls to ``/lookup`` for any of the session's 3pids will not + return the removed association. + + 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`` parameters, + as per ``/3pid/bind``, which proves ownership of the 3PID. + + If this endpoint returns a JSON Matrix error, that error should be passed + through to the client requesting an unbind through a homeserver, if the + homeserver is acting on behalf of a client. + operationId: unbindV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "mxid": "@ears:example.org", + "threepid": { + "medium": "email", + "address": "monkeys_have_ears@example.org" + } + } + properties: + sid: + type: string + description: The Session ID generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret passed to the ``requestToken`` call. + mxid: + type: string + description: The Matrix user ID to remove from the 3pids. + threepid: + type: object + title: 3PID + description: |- + The 3PID to remove. Must match the 3PID used to generate the session + if using ``sid`` and ``client_secret`` to authenticate this request. + properties: + medium: + type: string + description: |- + A medium from the `3PID Types`_ Appendix, matching the medium + of the identifier to unbind. + address: + type: string + description: The 3PID address to remove. + required: ['medium', 'address'] + required: ["threepid", "mxid"] + responses: + 200: + description: The association was successfully removed. + examples: + application/json: {} + schema: + type: object + 400: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. + 404: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. + 403: + description: |- + The credentials supplied to authenticate the request were invalid. + This may also be returned if the identity server does not support + the chosen authentication method (such as blocking homeservers from + unbinding identifiers). + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "Invalid homeserver signature" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + 501: + description: |- + If the response body is not a JSON Matrix error, the identity server + does not support unbinds. If a JSON Matrix error is in the response + body, the requesting party should respect the error. diff --git a/api/identity/v2_email_associations.yaml b/api/identity/v2_email_associations.yaml new file mode 100644 index 00000000..eff18eaf --- /dev/null +++ b/api/identity/v2_email_associations.yaml @@ -0,0 +1,183 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Email Associations API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/validate/email/requestToken": + post: + summary: Request a token for validating an email address. + description: |- + Create a session for validating an email address. + + The identity server will send an email containing a token. If that + token is presented to the identity server in the future, it indicates + that that user was able to read the email for that email address, and + so we validate ownership of the email address. + + Note that homeservers offer APIs that proxy this API, adding + additional behaviour on top, for example, + ``/register/email/requestToken`` is designed specifically for use when + registering an account and therefore will inform the user if the email + address given is already registered on the server. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: emailRequestTokenV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + $ref: "definitions/request_email_validation.yaml" + responses: + 200: + description: Session created. + schema: + $ref: "definitions/sid.yaml" + 400: + description: | + An error ocurred. Some possible errors are: + + - ``M_INVALID_EMAIL``: The email address provided was invalid. + - ``M_EMAIL_SEND_ERROR``: The validation email could not be sent. + examples: + application/json: { + "errcode": "M_INVALID_EMAIL", + "error": "The email address is not valid" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/validate/email/submitToken": + post: + summary: Validate ownership of an email address. + description: |- + Validate ownership of an email address. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the email address is considered to + have been validated. This does not publish any information publicly, or + associate the email address with any Matrix user ID. Specifically, + calls to ``/lookup`` will not show a binding. + + The identity server is free to match the token case-insensitively, or + carry out other mapping operations such as unicode + normalisation. Whether to do so is an implementation detail for the + identity server. Clients must always pass on the token without + modification. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: emailSubmitTokenPostV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "token": "atoken" + } + properties: + sid: + type: string + description: The session ID, generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret that was supplied to the ``requestToken`` call. + token: + type: string + description: The token generated by the ``requestToken`` call and emailed to the user. + required: ["sid", "client_secret", "token"] + responses: + 200: + description: + The success of the validation. + examples: + application/json: { + "success": true + } + schema: + type: object + properties: + success: + type: boolean + description: Whether the validation was successful or not. + required: ['success'] + get: + summary: Validate ownership of an email address. + description: |- + Validate ownership of an email address. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the email address is considered to + have been validated. This does not publish any information publicly, or + associate the email address with any Matrix user ID. Specifically, + calls to ``/lookup`` will not show a binding. + + Note that, in contrast with the POST version, this endpoint will be + used by end-users, and so the response should be human-readable. + operationId: emailSubmitTokenGetV2 + security: + - accessToken: [] + parameters: + - in: query + type: string + name: sid + required: true + description: The session ID, generated by the ``requestToken`` call. + x-example: 1234 + - in: query + type: string + name: client_secret + required: true + description: The client secret that was supplied to the ``requestToken`` call. + x-example: monkeys_are_GREAT + - in: query + type: string + name: token + required: true + description: The token generated by the ``requestToken`` call and emailed to the user. + x-example: atoken + responses: + "200": + description: Email address is validated. + "3xx": + description: |- + Email address is validated, and the ``next_link`` parameter was + provided to the ``requestToken`` call. The user must be redirected + to the URL provided by the ``next_link`` parameter. + "4xx": + description: + Validation failed. diff --git a/api/identity/v2_invitation_signing.yaml b/api/identity/v2_invitation_signing.yaml new file mode 100644 index 00000000..c1267bdc --- /dev/null +++ b/api/identity/v2_invitation_signing.yaml @@ -0,0 +1,101 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Ephemeral Invitation Signing API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/sign-ed25519": + post: + summary: Sign invitation details + description: |- + Sign invitation details. + + The identity server will look up ``token`` which was stored in a call + to ``store-invite``, and fetch the sender of the invite. + operationId: blindlySignStuffV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + example: { + "mxid": "@foo:bar.com", + "token": "sometoken", + "private_key": "base64encodedkey" + } + properties: + mxid: + type: string + description: The Matrix user ID of the user accepting the invitation. + token: + type: string + description: The token from the call to ``store-invite``. + private_key: + type: string + description: The private key, encoded as `Unpadded base64`_. + required: ["mxid", "token", "private_key"] + responses: + 200: + description: The signed JSON of the mxid, sender, and token. + schema: + type: object + properties: + mxid: + type: string + description: The Matrix user ID of the user accepting the invitation. + sender: + type: string + description: The Matrix user ID of the user who sent the invitation. + signatures: + type: object + description: The signature of the mxid, sender, and token. + $ref: "../../schemas/server-signatures.yaml" + token: + type: string + description: The token for the invitation. + required: ['mxid', 'sender', 'signatures', 'token'] + examples: + application/json: { + "mxid": "@foo:bar.com", + "sender": "@baz:bar.com", + "signatures": { + "my.id.server": { + "ed25519:0": "def987" + } + }, + "token": "abc123" + } + 404: + description: The token was not found. + examples: + application/json: { + "errcode": "M_UNRECOGNIZED", + "error": "Didn't recognize token" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/v2_phone_associations.yaml b/api/identity/v2_phone_associations.yaml new file mode 100644 index 00000000..cfaea410 --- /dev/null +++ b/api/identity/v2_phone_associations.yaml @@ -0,0 +1,185 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Phone Number Associations API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/validate/msisdn/requestToken": + post: + summary: Request a token for validating a phone number. + description: |- + Create a session for validating a phone number. + + The identity server will send an SMS message containing a token. If + that token is presented to the identity server in the future, it + indicates that that user was able to read the SMS for that phone + number, and so we validate ownership of the phone number. + + Note that homeservers offer APIs that proxy this API, adding + additional behaviour on top, for example, + ``/register/msisdn/requestToken`` is designed specifically for use when + registering an account and therefore will inform the user if the phone + number given is already registered on the server. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: msisdnRequestTokenV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + $ref: "definitions/request_msisdn_validation.yaml" + responses: + 200: + description: Session created. + schema: + $ref: "definitions/sid.yaml" + 400: + description: | + An error ocurred. Some possible errors are: + + - ``M_INVALID_ADDRESS``: The phone number provided was invalid. + - ``M_SEND_ERROR``: The validation SMS could not be sent. + - ``M_DESTINATION_REJECTED``: The identity server cannot deliver an + SMS to the provided country or region. + examples: + application/json: { + "errcode": "M_INVALID_ADDRESS", + "error": "The phone number is not valid" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/validate/msisdn/submitToken": + post: + summary: Validate ownership of a phone number. + description: |- + Validate ownership of a phone number. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the phone number is considered to + have been validated. This does not publish any information publicly, or + associate the phone number address with any Matrix user + ID. Specifically, calls to ``/lookup`` will not show a binding. + + The identity server is free to match the token case-insensitively, or + carry out other mapping operations such as unicode + normalisation. Whether to do so is an implementation detail for the + identity server. Clients must always pass on the token without + modification. + + Note: for backwards compatibility with previous drafts of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: msisdnSubmitTokenPostV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + example: { + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "token": "atoken" + } + properties: + sid: + type: string + description: The session ID, generated by the ``requestToken`` call. + client_secret: + type: string + description: The client secret that was supplied to the ``requestToken`` call. + token: + type: string + description: The token generated by the ``requestToken`` call and sent to the user. + required: ["sid", "client_secret", "token"] + responses: + 200: + description: + The success of the validation. + examples: + application/json: { + "success": true + } + schema: + type: object + properties: + success: + type: boolean + description: Whether the validation was successful or not. + required: ['success'] + get: + summary: Validate ownership of a phone number. + description: |- + Validate ownership of a phone number. + + If the three parameters are consistent with a set generated by a + ``requestToken`` call, ownership of the phone number address is + considered to have been validated. This does not publish any + information publicly, or associate the phone number with any Matrix + user ID. Specifically, calls to ``/lookup`` will not show a binding. + + Note that, in contrast with the POST version, this endpoint will be + used by end-users, and so the response should be human-readable. + operationId: msisdnSubmitTokenGetV2 + security: + - accessToken: [] + parameters: + - in: query + type: string + name: sid + required: true + description: The session ID, generated by the ``requestToken`` call. + x-example: 1234 + - in: query + type: string + name: client_secret + required: true + description: The client secret that was supplied to the ``requestToken`` call. + x-example: monkeys_are_GREAT + - in: query + type: string + name: token + required: true + description: The token generated by the ``requestToken`` call and sent to the user. + x-example: atoken + responses: + "200": + description: Phone number is validated. + "3xx": + description: |- + Phone number address is validated, and the ``next_link`` parameter + was provided to the ``requestToken`` call. The user must be + redirected to the URL provided by the ``next_link`` parameter. + "4xx": + description: + Validation failed. diff --git a/api/identity/v2_ping.yaml b/api/identity/v2_ping.yaml new file mode 100644 index 00000000..61f5d35b --- /dev/null +++ b/api/identity/v2_ping.yaml @@ -0,0 +1,46 @@ +# Copyright 2018 Kamax Sàrl +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: "2.0" +info: + title: "Matrix Identity Service Ping API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity +produces: + - application/json +paths: + "/v2": + get: + summary: Checks that an identity server is available at this API endpoint. + description: |- + Checks that an identity server is available at this API endpoint. + + To discover that an identity server is available at a specific URL, + this endpoint can be queried and will return an empty object. + + This is primarly used for auto-discovery and health check purposes + by entities acting as a client for the identity server. + operationId: pingV2 + responses: + 200: + description: An identity server is ready to serve requests. + examples: + application/json: {} + schema: + type: object diff --git a/api/identity/v2_pubkey.yaml b/api/identity/v2_pubkey.yaml new file mode 100644 index 00000000..68facd68 --- /dev/null +++ b/api/identity/v2_pubkey.yaml @@ -0,0 +1,127 @@ +# Copyright 2016 OpenMarket Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Public Key API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +paths: + "/pubkey/{keyId}": + get: + summary: Get a public key. + description: |- + Get the public key for the passed key ID. + operationId: getPubKeyV2 + parameters: + - in: path + type: string + name: keyId + required: true + description: |- + The ID of the key. This should take the form algorithm:identifier + where algorithm identifies the signing algorithm, and the identifier + is an opaque string. + x-example: "ed25519:0" + responses: + 200: + description: + The public key exists. + examples: + application/json: { + "public_key": "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" + } + schema: + type: object + properties: + public_key: + type: string + description: Unpadded Base64 encoded public key. + required: ['public_key'] + 404: + description: + The public key was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "The public key was not found" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" + "/pubkey/isvalid": + get: + summary: Check whether a long-term public key is valid. + description: |- + Check whether a long-term public key is valid. The response should always + be the same, provided the key exists. + operationId: isPubKeyValidV2 + parameters: + - in: query + type: string + name: public_key + required: true + description: |- + The unpadded base64-encoded public key to check. + x-example: "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" + responses: + 200: + description: + The validity of the public key. + examples: + application/json: { + "valid": true + } + schema: + type: object + properties: + valid: + type: boolean + description: Whether the public key is recognised and is currently valid. + required: ['valid'] + "/pubkey/ephemeral/isvalid": + get: + summary: Check whether a short-term public key is valid. + description: |- + Check whether a short-term public key is valid. + operationId: isEphemeralPubKeyValidV2 + parameters: + - in: query + type: string + name: public_key + required: true + description: |- + The unpadded base64-encoded public key to check. + x-example: "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" + responses: + 200: + description: + The validity of the public key. + examples: + application/json: { + "valid": true + } + schema: + type: object + properties: + valid: + type: boolean + description: Whether the public key is recognised and is currently valid. + required: ['valid'] diff --git a/api/identity/v2_store_invite.yaml b/api/identity/v2_store_invite.yaml new file mode 100644 index 00000000..afc41a1c --- /dev/null +++ b/api/identity/v2_store_invite.yaml @@ -0,0 +1,165 @@ +# Copyright 2018 New Vector Ltd +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Store Invitations API" + version: "2.0.0" +host: localhost:8090 +schemes: + - https +basePath: /_matrix/identity/v2 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/store-invite": + post: + summary: Store pending invitations to a user's 3pid. + description: |- + Store pending invitations to a user's 3pid. + + In addition to the request parameters specified below, an arbitrary + number of other parameters may also be specified. These may be used in + the invite message generation described below. + + The service will generate a random token and an ephemeral key used for + accepting the invite. + + The service also generates a ``display_name`` for the inviter, which is + a redacted version of ``address`` which does not leak the full contents + of the ``address``. + + The service records persistently all of the above information. + + It also generates an email containing all of this data, sent to the + ``address`` parameter, notifying them of the invitation. + + Also, the generated ephemeral public key will be listed as valid on + requests to ``/_matrix/identity/v2/pubkey/ephemeral/isvalid``. + + Currently, invites may only be issued for 3pids of the ``email`` medium. + + Optional fields in the request should be populated to the best of the + server's ability. Identity servers may use these variables when notifying + the ``address`` of the pending invite for display purposes. + operationId: storeInviteV2 + security: + - accessToken: [] + parameters: + - in: body + name: body + schema: + type: object + properties: + medium: + type: string + description: The literal string ``email``. + example: "email" + address: + type: string + description: The email address of the invited user. + example: "foo@example.com" + room_id: + type: string + description: The Matrix room ID to which the user is invited + example: "!something:example.org" + sender: + type: string + description: The Matrix user ID of the inviting user + example: "@bob:example.com" + room_alias: + type: string + description: |- + The Matrix room alias for the room to which the user is + invited. This should be retrieved from the ``m.room.canonical_alias`` + state event. + example: "#somewhere:exmaple.org" + room_avatar_url: + type: string + description: |- + The Content URI for the room to which the user is invited. This should + be retrieved from the ``m.room.avatar`` state event. + example: "mxc://example.org/s0meM3dia" + room_join_rules: + type: string + description: |- + The ``join_rule`` for the room to which the user is invited. This should + be retrieved from the ``m.room.join_rules`` state event. + example: "public" + room_name: + type: string + description: |- + The name of the room to which the user is invited. This should be retrieved + from the ``m.room.name`` state event. + example: "Bob's Emporium of Messages" + sender_display_name: + type: string + description: The display name of the user ID initiating the invite. + example: "Bob Smith" + sender_avatar_url: + type: string + description: The Content URI for the avatar of the user ID initiating the invite. + example: "mxc://example.org/an0th3rM3dia" + required: ["medium", "address", "room_id", "sender"] + responses: + 200: + description: The invitation was stored. + schema: + type: object + properties: + token: + type: string + description: | + The generated token. Must be a string consisting of the + characters ``[0-9a-zA-Z.=_-]``. Its length must not exceed + 255 characters and it must not be empty. + public_keys: + type: array + description: | + A list of [server's long-term public key, generated ephemeral + public key]. + items: + type: string + display_name: + type: string + description: The generated (redacted) display_name. + required: ['token', 'public_keys', 'display_name'] + example: + application/json: { + "token": "sometoken", + "public_keys": [ + "serverpublickey", + "ephemeralpublickey" + ], + "display_name": "f...@b..." + } + 400: + description: | + An error has occured. + + If the 3pid is already bound to a Matrix user ID, the error code + will be ``M_THREEPID_IN_USE``. If the medium is unsupported, the + error code will be ``M_UNRECOGNIZED``. + examples: + application/json: { + "errcode": "M_THREEPID_IN_USE", + "error": "Binding already known", + "mxid": "@alice:example.com" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/push-gateway/push_notifier.yaml b/api/push-gateway/push_notifier.yaml index 4a6cb8f7..754c325a 100644 --- a/api/push-gateway/push_notifier.yaml +++ b/api/push-gateway/push_notifier.yaml @@ -57,7 +57,7 @@ paths: type: object example: { "notification": { - "id": "$3957tyerfgewrf384", + "event_id": "$3957tyerfgewrf384", "room_id": "!slw48wfj34rtnrf:example.com", "type": "m.room.message", "sender": "@exampleuser:matrix.org", diff --git a/api/server-server/backfill.yaml b/api/server-server/backfill.yaml index 0da0e234..2ed6298c 100644 --- a/api/server-server/backfill.yaml +++ b/api/server-server/backfill.yaml @@ -64,7 +64,7 @@ paths: A transaction containing the PDUs that preceded the given event(s), including the given event(s), up to the given limit. schema: - $ref: "definitions/transaction.yaml" + $ref: "definitions/unlimited_pdu_transaction.yaml" "/get_missing_events/{roomId}": post: summary: Retrieves events that the sender is missing diff --git a/api/server-server/definitions/keys.yaml b/api/server-server/definitions/keys.yaml index 06619641..8bc6c563 100644 --- a/api/server-server/definitions/keys.yaml +++ b/api/server-server/definitions/keys.yaml @@ -94,6 +94,12 @@ properties: type: integer format: int64 description: |- - POSIX timestamp when the list of valid keys should be refreshed. Keys used beyond this - timestamp are no longer valid. + POSIX timestamp when the list of valid keys should be refreshed. This field MUST + be ignored in room versions 1, 2, 3, and 4. Keys used beyond this timestamp MUST + be considered invalid, depending on the `room version specification`_. + + Servers MUST use the lesser of this field and 7 days into the future when + determining if a key is valid. This is to avoid a situation where an attacker + publishes a key which is valid for a significant amount of time without a way + for the homeserver owner to revoke it. example: 1052262000000 diff --git a/api/server-server/definitions/pdu_v3.yaml b/api/server-server/definitions/pdu_v3.yaml index 8d41fbda..38105098 100644 --- a/api/server-server/definitions/pdu_v3.yaml +++ b/api/server-server/definitions/pdu_v3.yaml @@ -20,6 +20,10 @@ allOf: - $ref: "unsigned_pdu_base.yaml" - type: object properties: + redacts: + type: string + description: For redaction events, the ID of the event being redacted. + example: "$def/456+oldevent" auth_events: type: array items: diff --git a/api/server-server/definitions/pdu_v4.yaml b/api/server-server/definitions/pdu_v4.yaml new file mode 100644 index 00000000..a045e657 --- /dev/null +++ b/api/server-server/definitions/pdu_v4.yaml @@ -0,0 +1,47 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Persistent Data Unit +description: A persistent data unit (event) for room version 4 and beyond. +example: + $ref: "../examples/pdu_v4.json" +allOf: + - $ref: "pdu_v3.yaml" + - type: object + properties: + redacts: + type: string + description: For redaction events, the ID of the event being redacted. + example: "$def_456-oldevent" + 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: ["$URLsafe-base64EncodedHash", "$Another_Event"] + 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: ["$URLsafe-base64EncodedHash", "$Another_Event"] + required: + - auth_events + - prev_events diff --git a/api/server-server/definitions/single_pdu_transaction.yaml b/api/server-server/definitions/single_pdu_transaction.yaml new file mode 100644 index 00000000..ff682a44 --- /dev/null +++ b/api/server-server/definitions/single_pdu_transaction.yaml @@ -0,0 +1,32 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +allOf: + - $ref: "transaction.yaml" +properties: + pdus: + type: array + description: |- + A single PDU. 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>`_ 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/unlimited_pdu_transaction.yaml b/api/server-server/definitions/unlimited_pdu_transaction.yaml new file mode 100644 index 00000000..0fc31ee4 --- /dev/null +++ b/api/server-server/definitions/unlimited_pdu_transaction.yaml @@ -0,0 +1,33 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +allOf: + - $ref: "transaction.yaml" +properties: + pdus: + type: array + description: |- + List of persistent updates to rooms. 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>`_ 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/events.yaml b/api/server-server/events.yaml index 1f1a802d..1f8ee537 100644 --- a/api/server-server/events.yaml +++ b/api/server-server/events.yaml @@ -156,4 +156,4 @@ paths: 200: description: A transaction containing a single PDU which is the event requested. schema: - $ref: "definitions/transaction.yaml" + $ref: "definitions/single_pdu_transaction.yaml" diff --git a/api/server-server/examples/pdu_v3.json b/api/server-server/examples/pdu_v3.json index 6a454b4e..acffdf26 100644 --- a/api/server-server/examples/pdu_v3.json +++ b/api/server-server/examples/pdu_v3.json @@ -15,5 +15,6 @@ "prev_events": [ "$base64encodedeventid", "$adifferenteventid" - ] + ], + "redacts": "$some/old+event" } diff --git a/api/server-server/examples/pdu_v4.json b/api/server-server/examples/pdu_v4.json new file mode 100644 index 00000000..3c2f0e22 --- /dev/null +++ b/api/server-server/examples/pdu_v4.json @@ -0,0 +1,12 @@ +{ + "$ref": "pdu_v3.json", + "auth_events": [ + "$urlsafe_base64_encoded_eventid", + "$a-different-event-id" + ], + "prev_events": [ + "$urlsafe_base64_encoded_eventid", + "$a-different-event-id" + ], + "redacts": "$some-old_event" +} diff --git a/api/server-server/invites-v1.yaml b/api/server-server/invites-v1.yaml index 2ad0f220..8e1c861d 100644 --- a/api/server-server/invites-v1.yaml +++ b/api/server-server/invites-v1.yaml @@ -82,35 +82,9 @@ paths: 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" - } - } - ] + $ref: "../../event-schemas/schema/stripped_state.yaml" + example: + $ref: "../../event-schemas/examples/invite_room_state.json" example: { "$ref": "examples/minimal_pdu.json", "type": "m.room.member", @@ -118,26 +92,6 @@ paths: "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" }, @@ -180,24 +134,9 @@ paths: "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" - } - } - ] + "invite_room_state": { + "$ref": "../../../event-schemas/examples/invite_room_state.json" + } }, "content": { "membership": "invite" diff --git a/api/server-server/invites-v2.yaml b/api/server-server/invites-v2.yaml index c459a848..cae14bb4 100644 --- a/api/server-server/invites-v2.yaml +++ b/api/server-server/invites-v2.yaml @@ -83,35 +83,9 @@ paths: 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" - } - } - ] + $ref: "../../event-schemas/schema/stripped_state.yaml" + example: + $ref: "../../event-schemas/examples/invite_room_state.json" required: ['room_version', 'event'] example: { "room_version": "2", @@ -130,25 +104,7 @@ paths: "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: @@ -174,24 +130,9 @@ paths: "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" - } - } - ] + "invite_room_state": { + "$ref": "../../../event-schemas/examples/invite_room_state.json" + } }, "content": { "membership": "invite" diff --git a/api/server-server/keys_query.yaml b/api/server-server/keys_query.yaml index e616915b..4989f7fa 100644 --- a/api/server-server/keys_query.yaml +++ b/api/server-server/keys_query.yaml @@ -44,8 +44,10 @@ 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. + + When excluded, the trailing slash on this endpoint is optional. required: false x-example: "ed25519:abc123" - in: query @@ -53,7 +55,7 @@ paths: type: integer format: int64 description: |- - A millisecond POSIX timestamp in milliseconds indicating when the returned + A millisecond POSIX timestamp in milliseconds indicating when the returned certificates will need to be valid until to be useful to the requesting server. If not supplied, the current time as determined by the notary server is used. @@ -114,7 +116,7 @@ paths: format: int64 description: |- A millisecond POSIX timestamp in milliseconds indicating when - the returned certificates will need to be valid until to be + the returned certificates will need to be valid until to be useful to the requesting server. If not supplied, the current time as determined by the notary diff --git a/api/server-server/keys_server.yaml b/api/server-server/keys_server.yaml index 69985ab7..465bb294 100644 --- a/api/server-server/keys_server.yaml +++ b/api/server-server/keys_server.yaml @@ -51,6 +51,8 @@ paths: **Deprecated**. Servers should not use this parameter and instead opt to return all keys, not just the requested one. The key ID to look up. + + When excluded, the trailing slash on this endpoint is optional. required: false x-example: "ed25519:abc123" deprecated: true diff --git a/api/server-server/leaving.yaml b/api/server-server/leaving.yaml index c088cb5d..e0882fe8 100644 --- a/api/server-server/leaving.yaml +++ b/api/server-server/leaving.yaml @@ -57,7 +57,6 @@ paths: `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: diff --git a/api/server-server/query.yaml b/api/server-server/query.yaml index dc14724c..29826b32 100644 --- a/api/server-server/query.yaml +++ b/api/server-server/query.yaml @@ -81,7 +81,7 @@ paths: servers: type: array description: |- - An array of server names that are likely to hold the given room. This + An array of server names that are likely to hold the given room. This list may or may not include the server answering the query. items: type: string @@ -128,7 +128,7 @@ paths: x-example: "@someone:example.org" - in: query name: field - type: enum + type: string enum: ['displayname', 'avatar_url'] description: |- The field to query. If specified, the server will only return the given field @@ -139,7 +139,7 @@ paths: description: |- The profile for the user. If a ``field`` is specified in the request, only the matching field should be included in the response. If no ``field`` was specified, - the response should include the fields of the user's profile that can be made + the response should include the fields of the user's profile that can be made public, such as the display name and avatar. If the user does not have a particular field set on their profile, the server diff --git a/api/server-server/user_devices.yaml b/api/server-server/user_devices.yaml index 4805deb4..362f9baa 100644 --- a/api/server-server/user_devices.yaml +++ b/api/server-server/user_devices.yaml @@ -42,7 +42,6 @@ paths: description: |- The user ID to retrieve devices for. Must be a user local to the receiving homeserver. - required: true x-example: "@alice:example.org" responses: 200: @@ -82,4 +81,4 @@ paths: description: Optional display name for the device. example: "Alice's Mobile Phone" required: ['device_id', 'keys'] - required: ['user_id', 'stream_id', 'devices'] \ No newline at end of file + required: ['user_id', 'stream_id', 'devices'] diff --git a/api/server-server/user_keys.yaml b/api/server-server/user_keys.yaml index 63c74d20..93237d80 100644 --- a/api/server-server/user_keys.yaml +++ b/api/server-server/user_keys.yaml @@ -63,7 +63,7 @@ paths: - one_time_keys responses: 200: - description: The claimed keys + description: The claimed keys. schema: type: object properties: @@ -72,30 +72,42 @@ paths: description: |- One-time keys for the queried devices. A map from user ID, to a map from devices to a map from ``:`` to the key object. + + See the Client-Server Key Algorithms section for more information on + the Key Object format. additionalProperties: type: object additionalProperties: type: - string - - object - required: ['one_time_keys'] - examples: - application/json: { - "one_time_keys": { - "@alice:example.com": { - "JLAFKJWSCS": { - "signed_curve25518:AAAAHg": { - "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs", - "signatures": { - "@alice:example.com": { - "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw" + - type: object + title: KeyObject + properties: + key: + type: string + description: The key, encoded using unpadded base64. + signatures: + type: object + description: |- + Signature for the device. Mapped from user ID to signature object. + additionalProperties: + type: string + required: ['key', 'signatures'] + example: { + "@alice:example.com": { + "JLAFKJWSCS": { + "signed_curve25519:AAAAHg": { + "key": "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs", + "signatures": { + "@alice:example.com": { + "ed25519:JLAFKJWSCS": "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw" + } } } } } } - } - } + required: ['one_time_keys'] "/user/keys/query": post: summary: Download device identity keys. @@ -168,8 +180,8 @@ paths: "user_id": "@alice:example.com", "device_id": "JLAFKJWSCS", "algorithms": [ - "m.olm.v1.curve25519-aes-sha256", - "m.megolm.v1.aes-sha" + "m.olm.v1.curve25519-aes-sha2", + "m.megolm.v1.aes-sha2" ], "keys": { "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI", diff --git a/api/server-server/version.yaml b/api/server-server/version.yaml index 19975529..929f7b91 100644 --- a/api/server-server/version.yaml +++ b/api/server-server/version.yaml @@ -27,6 +27,7 @@ paths: get: summary: Get the implementation name and version of this homeserver. description: Get the implementation name and version of this homeserver. + operationId: getVersion responses: 200: description: diff --git a/api/server-server/wellknown.yaml b/api/server-server/wellknown.yaml index 75676646..bc390bd5 100644 --- a/api/server-server/wellknown.yaml +++ b/api/server-server/wellknown.yaml @@ -29,6 +29,7 @@ paths: 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. + operationId: getWellKnown responses: 200: description: diff --git a/changelogs/application_service.rst b/changelogs/application_service.rst index 9d098837..51a97637 100644 --- a/changelogs/application_service.rst +++ b/changelogs/application_service.rst @@ -1,3 +1,22 @@ +r0.1.2 +====== + +Spec Clarifications +------------------- + +- Clearer wording for the legacy routes section. (`#2160 `_) + + +r0.1.1 +====== + +Spec Clarifications +------------------- + +- Change examples to use example.org instead of a real domain. (`#1650 `_) +- Add missing definition for how appservices verify requests came from a homeserver. (`#2037 `_) + + r0.1.0 ====== diff --git a/changelogs/client_server.rst b/changelogs/client_server.rst index eb92c29d..c56a1073 100644 --- a/changelogs/client_server.rst +++ b/changelogs/client_server.rst @@ -1,3 +1,85 @@ +r0.5.0 +====== + +Breaking Changes +---------------- + +- Add a new ``submit_url`` field to the responses of ``/requestToken`` which older clients will not be able to handle correctly. (`#2101 `_) + + +Deprecations +------------ + +- Remove references to presence lists. (`#1817 `_) + + +New Endpoints +------------- + +- ``GET /account_data`` routes. (`#1873 `_) + + +Backwards Compatible Changes +---------------------------- + +- Add megolm session export format. (`#1701 `_) +- Add support for advertising experimental features to clients. (`#1786 `_) +- Add a generic SSO login API. (`#1789 `_) +- Add a mechanism for servers to redirect clients to an alternative homeserver after logging in. (`#1790 `_) +- Add room version upgrades. (`#1791 `_, `#1875 `_) +- Support optional features by having clients query for capabilities. (`#1829 `_, `#1879 `_) +- Add ``M_RESOURCE_LIMIT_EXCEEDED`` as an error code for when homeservers exceed limits imposed on them. (`#1874 `_) +- Emit ``M_UNSUPPORTED_ROOM_VERSION`` error codes where applicable on ``/createRoom`` and ``/invite`` APIs. (`#1908 `_) +- Add a ``.m.rule.tombstone`` default push rule for room ugprade notifications. (`#2020 `_) +- Add support for sending server notices to clients. (`#2026 `_) +- Add MSISDN (phone number) support to User-Interactive Authentication. (`#2030 `_) +- Add the option to lazy-load room members for increased client performance. (`#2035 `_) +- Add ``id_server`` to ``/deactivate`` and ``/3pid/delete`` endpoints to unbind from a specific identity server. (`#2046 `_) +- Add support for Olm sessions becoming un-stuck. (`#2059 `_) +- Add interactive device verification, including a common framework for device verification. (`#2072 `_) + + +Spec Clarifications +------------------- + +- Change examples to use example.org instead of a real domain. (`#1650 `_) +- Clarify that ``state_default`` in ``m.room.power_levels`` always defaults to 50. (`#1656 `_) +- Add missing ``status_msg`` to ``m.presence`` schema. (`#1744 `_) +- Fix various spelling mistakes throughout the specification. (`#1838 `_, `#1853 `_, `#1860 `_, `#1933 `_, `#1969 `_, `#1988 `_, `#1989 `_, `#1991 `_, `#1992 `_) +- Add the missing ``m.push_rules`` event schema. (`#1889 `_) +- Clarify how modern day local echo is meant to be solved by clients. (`#1891 `_) +- Clarify that ``width`` and ``height`` are required parameters on ``/_matrix/media/r0/thumbnail/{serverName}/{mediaId}``. (`#1975 `_) +- Clarify how ``m.login.dummy`` can be used to disambiguate login flows. (`#1999 `_) +- Remove ``prev_content`` from the redaction algorithm's essential keys list. (`#2016 `_) +- Fix the ``third_party_signed`` definitions for the join APIs. (`#2025 `_) +- Clarify why User Interactive Auth is used on password changes and how access tokens are handled. (`#2027 `_) +- Clarify that devices are deleted upon logout. (`#2028 `_) +- Add ``M_NOT_FOUND`` error definition for deleting room aliases. (`#2029 `_) +- Add missing ``reason`` to ``m.call.hangup``. (`#2031 `_) +- Clarify how redactions affect room state. (`#2032 `_) +- Clarify that ``FAIL_ERROR`` in autodiscovery is not limited to just homeservers. (`#2036 `_) +- Fix example ``Content-Type`` for ``/media/upload`` request. (`#2041 `_) +- Clarify that login flows are meant to be completed in order. (`#2042 `_) +- Clarify that clients should not send read receipts for their own messages. (`#2043 `_) +- Use consistent examples of events throughout the specification. (`#2051 `_) +- Clarify which push rule condition kinds exist. (`#2052 `_) +- Clarify the required fields on ``m.file`` (and similar) messages. (`#2053 `_) +- Clarify that User-Interactive Authentication stages cannot be attempted more than once. (`#2054 `_) +- Clarify which parameters apply in what scenarios on ``/register``. (`#2055 `_) +- Clarify how to interpret changes of ``membership`` over time. (`#2056 `_) +- Clarify exactly what invite_room_state consists of. (`#2067 `_) +- Clarify how the content repository works, and what it is used for. (`#2068 `_) +- Clarify the order events in chunk are returned in for ``/messages``. (`#2069 `_) +- Clarify the key object definition for the key management API. (`#2083 `_) +- Reorganize information about events into a common section. (`#2087 `_) +- De-duplicate ``/state/`` endpoints, clarifying that the ```` is optional. (`#2088 `_) +- Clarify when and where CORS headers should be returned. (`#2089 `_) +- Clarify when authorization and rate-limiting are not applicable. (`#2090 `_) +- Clarify that ``/register`` must produce valid Matrix User IDs. (`#2091 `_) +- Clarify how ``unread_notifications`` is calculated. (`#2097 `_) +- Clarify what a "module" is and update feature profiles for clients. (`#2098 `_) + + r0.4.0 ====== diff --git a/changelogs/client_server/newsfragments/1701.feature b/changelogs/client_server/newsfragments/1701.feature deleted file mode 100644 index 39c22dd7..00000000 --- a/changelogs/client_server/newsfragments/1701.feature +++ /dev/null @@ -1 +0,0 @@ -Documented megolm session export format. \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1744.clarification b/changelogs/client_server/newsfragments/1744.clarification deleted file mode 100644 index dc103920..00000000 --- a/changelogs/client_server/newsfragments/1744.clarification +++ /dev/null @@ -1 +0,0 @@ -Add missing status_msg to m.presence schema. diff --git a/changelogs/client_server/newsfragments/1786.feature b/changelogs/client_server/newsfragments/1786.feature deleted file mode 100644 index 6f21778c..00000000 --- a/changelogs/client_server/newsfragments/1786.feature +++ /dev/null @@ -1 +0,0 @@ -Add support for advertising experimental features to clients. diff --git a/changelogs/client_server/newsfragments/1789.feature b/changelogs/client_server/newsfragments/1789.feature deleted file mode 100644 index 97c1e5ca..00000000 --- a/changelogs/client_server/newsfragments/1789.feature +++ /dev/null @@ -1 +0,0 @@ -Add a generic SSO login API. diff --git a/changelogs/client_server/newsfragments/1790.feature b/changelogs/client_server/newsfragments/1790.feature deleted file mode 100644 index 26dccd05..00000000 --- a/changelogs/client_server/newsfragments/1790.feature +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 0a854c8f..00000000 --- a/changelogs/client_server/newsfragments/1791.feature +++ /dev/null @@ -1 +0,0 @@ -Add room version upgrades. diff --git a/changelogs/client_server/newsfragments/1817.deprecation b/changelogs/client_server/newsfragments/1817.deprecation deleted file mode 100644 index 2c52d198..00000000 --- a/changelogs/client_server/newsfragments/1817.deprecation +++ /dev/null @@ -1 +0,0 @@ -Remove references to presence lists. diff --git a/changelogs/client_server/newsfragments/1829.feature b/changelogs/client_server/newsfragments/1829.feature deleted file mode 100644 index 107291f3..00000000 --- a/changelogs/client_server/newsfragments/1829.feature +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index b0f05203..00000000 --- a/changelogs/client_server/newsfragments/1838.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various spelling mistakes throughout the specification. diff --git a/changelogs/client_server/newsfragments/1853.clarification b/changelogs/client_server/newsfragments/1853.clarification deleted file mode 100644 index b0f05203..00000000 --- a/changelogs/client_server/newsfragments/1853.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various spelling mistakes throughout the specification. diff --git a/changelogs/client_server/newsfragments/1860.clarification b/changelogs/client_server/newsfragments/1860.clarification deleted file mode 100644 index b0f05203..00000000 --- a/changelogs/client_server/newsfragments/1860.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various spelling mistakes throughout the specification. diff --git a/changelogs/client_server/newsfragments/1873.new b/changelogs/client_server/newsfragments/1873.new deleted file mode 100644 index 724a4308..00000000 --- a/changelogs/client_server/newsfragments/1873.new +++ /dev/null @@ -1 +0,0 @@ -``GET /account_data`` routes. diff --git a/changelogs/client_server/newsfragments/1874.feature b/changelogs/client_server/newsfragments/1874.feature deleted file mode 100644 index bdab5464..00000000 --- a/changelogs/client_server/newsfragments/1874.feature +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 0a854c8f..00000000 --- a/changelogs/client_server/newsfragments/1875.feature +++ /dev/null @@ -1 +0,0 @@ -Add room version upgrades. diff --git a/changelogs/client_server/newsfragments/1879.feature b/changelogs/client_server/newsfragments/1879.feature deleted file mode 100644 index 107291f3..00000000 --- a/changelogs/client_server/newsfragments/1879.feature +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 5026dab3..00000000 --- a/changelogs/client_server/newsfragments/1889.clarification +++ /dev/null @@ -1 +0,0 @@ -Add the missing `m.push_rules` event schema. diff --git a/changelogs/client_server/newsfragments/1903.feature b/changelogs/client_server/newsfragments/1903.feature deleted file mode 100644 index 1c64d826..00000000 --- a/changelogs/client_server/newsfragments/1903.feature +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index b0f05203..00000000 --- a/changelogs/client_server/newsfragments/1933.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various spelling mistakes throughout the specification. diff --git a/changelogs/client_server/newsfragments/2125.clarification b/changelogs/client_server/newsfragments/2125.clarification new file mode 100644 index 00000000..c71cdfff --- /dev/null +++ b/changelogs/client_server/newsfragments/2125.clarification @@ -0,0 +1 @@ +Add missing format fields to ``m.room.message$m.notice`` schema. diff --git a/changelogs/client_server/newsfragments/2129.clarification b/changelogs/client_server/newsfragments/2129.clarification new file mode 100644 index 00000000..9d67deac --- /dev/null +++ b/changelogs/client_server/newsfragments/2129.clarification @@ -0,0 +1 @@ +Remove "required" designation from the ``url`` field of certain ``m.room.message`` msgtypes. diff --git a/changelogs/client_server/newsfragments/2131.clarification b/changelogs/client_server/newsfragments/2131.clarification new file mode 100644 index 00000000..3c41fb60 --- /dev/null +++ b/changelogs/client_server/newsfragments/2131.clarification @@ -0,0 +1 @@ +Fix typo in key verification framework section. diff --git a/changelogs/client_server/newsfragments/2132.clarification b/changelogs/client_server/newsfragments/2132.clarification new file mode 100644 index 00000000..b8a4cc8a --- /dev/null +++ b/changelogs/client_server/newsfragments/2132.clarification @@ -0,0 +1 @@ +Clarify the distinction between ``m.key.verification.start`` and its ``m.sas.v1`` variant. diff --git a/changelogs/client_server/newsfragments/2133.clarification b/changelogs/client_server/newsfragments/2133.clarification new file mode 100644 index 00000000..3a003179 --- /dev/null +++ b/changelogs/client_server/newsfragments/2133.clarification @@ -0,0 +1 @@ +Fix link to Olm signing specification. diff --git a/changelogs/client_server/newsfragments/2136.clarification b/changelogs/client_server/newsfragments/2136.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/2136.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2148.misc b/changelogs/client_server/newsfragments/2148.misc new file mode 100644 index 00000000..d5514131 --- /dev/null +++ b/changelogs/client_server/newsfragments/2148.misc @@ -0,0 +1 @@ +Fix a small duplicated "as". diff --git a/changelogs/client_server/newsfragments/2152.clarification b/changelogs/client_server/newsfragments/2152.clarification new file mode 100644 index 00000000..03fde9ff --- /dev/null +++ b/changelogs/client_server/newsfragments/2152.clarification @@ -0,0 +1 @@ +Clarify the conditions for the ``.m.rule.room_one_to_one`` push rule. diff --git a/changelogs/client_server/newsfragments/2157.clarification b/changelogs/client_server/newsfragments/2157.clarification new file mode 100644 index 00000000..5c1d549b --- /dev/null +++ b/changelogs/client_server/newsfragments/2157.clarification @@ -0,0 +1 @@ +Clarify the encryption algorithms supported by the device of the device keys example. diff --git a/changelogs/client_server/newsfragments/2204.clarification b/changelogs/client_server/newsfragments/2204.clarification new file mode 100644 index 00000000..7a66b08b --- /dev/null +++ b/changelogs/client_server/newsfragments/2204.clarification @@ -0,0 +1 @@ +Clarify that ``/rooms/:roomId/event/:eventId`` returns a Matrix error. diff --git a/changelogs/client_server/newsfragments/2215.clarification b/changelogs/client_server/newsfragments/2215.clarification new file mode 100644 index 00000000..3ccb2333 --- /dev/null +++ b/changelogs/client_server/newsfragments/2215.clarification @@ -0,0 +1 @@ +Fix various typos throughout the specification. diff --git a/changelogs/client_server/newsfragments/2223.clarification b/changelogs/client_server/newsfragments/2223.clarification new file mode 100644 index 00000000..165b7e13 --- /dev/null +++ b/changelogs/client_server/newsfragments/2223.clarification @@ -0,0 +1 @@ +Add a missing ``state_key`` check on ``.m.rule.tombstone``. diff --git a/changelogs/client_server/newsfragments/2234.feature b/changelogs/client_server/newsfragments/2234.feature new file mode 100644 index 00000000..bb1883b3 --- /dev/null +++ b/changelogs/client_server/newsfragments/2234.feature @@ -0,0 +1 @@ +Add ``M_USER_DEACTIVATED`` error code. diff --git a/changelogs/client_server/newsfragments/2247.clarification b/changelogs/client_server/newsfragments/2247.clarification new file mode 100644 index 00000000..43553399 --- /dev/null +++ b/changelogs/client_server/newsfragments/2247.clarification @@ -0,0 +1 @@ +Fix the ``m.room_key_request`` ``action`` value, setting it from ``cancel_request`` to ``request_cancellation``. diff --git a/changelogs/identity_service.rst b/changelogs/identity_service.rst index 0405d515..cb06709d 100644 --- a/changelogs/identity_service.rst +++ b/changelogs/identity_service.rst @@ -1,3 +1,32 @@ +r0.2.1 +====== + +Spec Clarifications +------------------- + +- Remove incorrect ``id_server`` parameter from ``/requestToken`` endpoints. (`#2124 `_) +- Clarify that identity servers can return 403 for unbind requests. (`#2126 `_) + + +r0.2.0 +====== + +New Endpoints +------------- + +- Add ``/3pid/unbind`` for removing 3PIDs. (`#2046 `_) + + +Spec Clarifications +------------------- + +- Fix various spelling mistakes throughout the specification. (`#1853 `_) +- Fix route for ``/3pid/bind``. (`#1967 `_) +- Add missing aesthetic parameters to ``/store-invite``. (`#2049 `_) +- Clarify what the client should receive upon sending an identical email validation request multiple times. (`#2057 `_) +- Clarify that the default transport is JSON over HTTP. (`#2086 `_) + + r0.1.0 ====== diff --git a/changelogs/identity_service/newsfragments/1853.clarification b/changelogs/identity_service/newsfragments/1853.clarification deleted file mode 100644 index b0f05203..00000000 --- a/changelogs/identity_service/newsfragments/1853.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix various spelling mistakes throughout the specification. diff --git a/changelogs/push_gateway.rst b/changelogs/push_gateway.rst index 33a7683c..2afb2a9e 100644 --- a/changelogs/push_gateway.rst +++ b/changelogs/push_gateway.rst @@ -1,3 +1,12 @@ +r0.1.1 +====== + +Spec Clarifications +------------------- + +- Fix ``event_id`` field in push request body. (`#2151 `_) + + r0.1.0 ====== diff --git a/changelogs/server_server.rst b/changelogs/server_server.rst index a21da177..22da543c 100644 --- a/changelogs/server_server.rst +++ b/changelogs/server_server.rst @@ -1,3 +1,30 @@ +r0.1.3 +====== + +Spec Clarifications +------------------- + +- Clarify the encryption algorithms supported by the device of the user keys query example. (`#2157 `_) +- Clarify the purpose of reference hashes. (`#2159 `_) + + +r0.1.2 +====== + +Spec Clarifications +------------------- + +- Change examples to use example.org instead of a real domain. (`#1650 `_) +- Fix the ``access_token`` parameter in the open_id endpoint. (`#1906 `_) +- Fix various spelling mistakes throughout the specification. (`#1991 `_) +- Clarify exactly what invite_room_state consists of. (`#2067 `_) +- Clarify how ``valid_until_ts`` behaves with respect to room version. (`#2080 `_) +- Clarify which servers are supposed to sign events. (`#2081 `_) +- Clarify the key object definition for the key management API. (`#2083 `_) +- Clarify how many PDUs are contained in transaction objects for various endpoints. (`#2095 `_) +- Clarify that the trailing slash is optional on ``/keys/*`` endpoints when no key ID is requested. (`#2097 `_) + + r0.1.1 ====== diff --git a/changelogs/server_server/newsfragments/1904.clarification b/changelogs/server_server/newsfragments/1904.clarification deleted file mode 100644 index 94174ebd..00000000 --- a/changelogs/server_server/newsfragments/1904.clarification +++ /dev/null @@ -1 +0,0 @@ -Fix the `access_token` parameter in the open_id endpoint. diff --git a/data-definitions/sas-emoji.json b/data-definitions/sas-emoji.json new file mode 100644 index 00000000..060fbd49 --- /dev/null +++ b/data-definitions/sas-emoji.json @@ -0,0 +1,66 @@ +[ + {"number": 0, "emoji": "🐶", "description": "Dog", "unicode": "U+1F436"}, + {"number": 1, "emoji": "🐱", "description": "Cat", "unicode": "U+1F431"}, + {"number": 2, "emoji": "🦁", "description": "Lion", "unicode": "U+1F981"}, + {"number": 3, "emoji": "🐎", "description": "Horse", "unicode": "U+1F40E"}, + {"number": 4, "emoji": "🦄", "description": "Unicorn", "unicode": "U+1F984"}, + {"number": 5, "emoji": "🐷", "description": "Pig", "unicode": "U+1F437"}, + {"number": 6, "emoji": "🐘", "description": "Elephant", "unicode": "U+1F418"}, + {"number": 7, "emoji": "🐰", "description": "Rabbit", "unicode": "U+1F430"}, + {"number": 8, "emoji": "🐼", "description": "Panda", "unicode": "U+1F43C"}, + {"number": 9, "emoji": "🐓", "description": "Rooster", "unicode": "U+1F413"}, + {"number": 10, "emoji": "🐧", "description": "Penguin", "unicode": "U+1F427"}, + {"number": 11, "emoji": "🐢", "description": "Turtle", "unicode": "U+1F422"}, + {"number": 12, "emoji": "🐟", "description": "Fish", "unicode": "U+1F41F"}, + {"number": 13, "emoji": "🐙", "description": "Octopus", "unicode": "U+1F419"}, + {"number": 14, "emoji": "🦋", "description": "Butterfly", "unicode": "U+1F98B"}, + {"number": 15, "emoji": "🌷", "description": "Flower", "unicode": "U+1F337"}, + {"number": 16, "emoji": "🌳", "description": "Tree", "unicode": "U+1F333"}, + {"number": 17, "emoji": "🌵", "description": "Cactus", "unicode": "U+1F335"}, + {"number": 18, "emoji": "🍄", "description": "Mushroom", "unicode": "U+1F344"}, + {"number": 19, "emoji": "🌏", "description": "Globe", "unicode": "U+1F30F"}, + {"number": 20, "emoji": "🌙", "description": "Moon", "unicode": "U+1F319"}, + {"number": 21, "emoji": "☁️", "description": "Cloud", "unicode": "U+2601U+FE0F"}, + {"number": 22, "emoji": "🔥", "description": "Fire", "unicode": "U+1F525"}, + {"number": 23, "emoji": "🍌", "description": "Banana", "unicode": "U+1F34C"}, + {"number": 24, "emoji": "🍎", "description": "Apple", "unicode": "U+1F34E"}, + {"number": 25, "emoji": "🍓", "description": "Strawberry", "unicode": "U+1F353"}, + {"number": 26, "emoji": "🌽", "description": "Corn", "unicode": "U+1F33D"}, + {"number": 27, "emoji": "🍕", "description": "Pizza", "unicode": "U+1F355"}, + {"number": 28, "emoji": "🎂", "description": "Cake", "unicode": "U+1F382"}, + {"number": 29, "emoji": "❤️", "description": "Heart", "unicode": "U+2764U+FE0F"}, + {"number": 30, "emoji": "😀", "description": "Smiley", "unicode": "U+1F600"}, + {"number": 31, "emoji": "🤖", "description": "Robot", "unicode": "U+1F916"}, + {"number": 32, "emoji": "🎩", "description": "Hat", "unicode": "U+1F3A9"}, + {"number": 33, "emoji": "👓", "description": "Glasses", "unicode": "U+1F453"}, + {"number": 34, "emoji": "🔧", "description": "Spanner", "unicode": "U+1F527"}, + {"number": 35, "emoji": "🎅", "description": "Santa", "unicode": "U+1F385"}, + {"number": 36, "emoji": "👍", "description": "Thumbs Up", "unicode": "U+1F44D"}, + {"number": 37, "emoji": "☂️", "description": "Umbrella", "unicode": "U+2602U+FE0F"}, + {"number": 38, "emoji": "⌛", "description": "Hourglass", "unicode": "U+231B"}, + {"number": 39, "emoji": "⏰", "description": "Clock", "unicode": "U+23F0"}, + {"number": 40, "emoji": "🎁", "description": "Gift", "unicode": "U+1F381"}, + {"number": 41, "emoji": "💡", "description": "Light Bulb", "unicode": "U+1F4A1"}, + {"number": 42, "emoji": "📕", "description": "Book", "unicode": "U+1F4D5"}, + {"number": 43, "emoji": "✏️", "description": "Pencil", "unicode": "U+270FU+FE0F"}, + {"number": 44, "emoji": "📎", "description": "Paperclip", "unicode": "U+1F4CE"}, + {"number": 45, "emoji": "✂️", "description": "Scissors", "unicode": "U+2702U+FE0F"}, + {"number": 46, "emoji": "🔒", "description": "Lock", "unicode": "U+1F512"}, + {"number": 47, "emoji": "🔑", "description": "Key", "unicode": "U+1F511"}, + {"number": 48, "emoji": "🔨", "description": "Hammer", "unicode": "U+1F528"}, + {"number": 49, "emoji": "☎️", "description": "Telephone", "unicode": "U+260EU+FE0F"}, + {"number": 50, "emoji": "🏁", "description": "Flag", "unicode": "U+1F3C1"}, + {"number": 51, "emoji": "🚂", "description": "Train", "unicode": "U+1F682"}, + {"number": 52, "emoji": "🚲", "description": "Bicycle", "unicode": "U+1F6B2"}, + {"number": 53, "emoji": "✈️", "description": "Aeroplane", "unicode": "U+2708U+FE0F"}, + {"number": 54, "emoji": "🚀", "description": "Rocket", "unicode": "U+1F680"}, + {"number": 55, "emoji": "🏆", "description": "Trophy", "unicode": "U+1F3C6"}, + {"number": 56, "emoji": "⚽", "description": "Ball", "unicode": "U+26BD"}, + {"number": 57, "emoji": "🎸", "description": "Guitar", "unicode": "U+1F3B8"}, + {"number": 58, "emoji": "🎺", "description": "Trumpet", "unicode": "U+1F3BA"}, + {"number": 59, "emoji": "🔔", "description": "Bell", "unicode": "U+1F514"}, + {"number": 60, "emoji": "⚓", "description": "Anchor", "unicode": "U+2693"}, + {"number": 61, "emoji": "🎧", "description": "Headphones", "unicode": "U+1F3A7"}, + {"number": 62, "emoji": "📁", "description": "Folder", "unicode": "U+1F4C1"}, + {"number": 63, "emoji": "📌", "description": "Pin", "unicode": "U+1F4CC"} +] diff --git a/event-schemas/check_examples.py b/event-schemas/check_examples.py index 3e536ec3..31daa478 100755 --- a/event-schemas/check_examples.py +++ b/event-schemas/check_examples.py @@ -106,14 +106,17 @@ def check_example_dir(exampledir, schemadir): if filename.startswith("."): # Skip over any vim .swp files. continue + if filename.endswith(".json"): + # Skip over any explicit examples (partial event definitions) + continue cwd = os.path.basename(os.path.dirname(os.path.join(root, filename))) if cwd == "core": # Skip checking the underlying definitions continue examplepath = os.path.join(root, filename) schemapath = examplepath.replace(exampledir, schemadir) - if schemapath.find("#") >= 0: - schemapath = schemapath[:schemapath.find("#")] + if schemapath.find("$") >= 0: + schemapath = schemapath[:schemapath.find("$")] try: check_example_file(examplepath, schemapath) except Exception as e: diff --git a/event-schemas/examples/invite_room_state.json b/event-schemas/examples/invite_room_state.json new file mode 100644 index 00000000..9d8c1b2b --- /dev/null +++ b/event-schemas/examples/invite_room_state.json @@ -0,0 +1,18 @@ +[ + { + "type": "m.room.name", + "sender": "@bob:example.org", + "state_key": "", + "content": { + "name": "Example Room" + } + }, + { + "type": "m.room.join_rules", + "sender": "@bob:example.org", + "state_key": "", + "content": { + "join_rule": "invite" + } + } +] diff --git a/event-schemas/examples/m.dummy b/event-schemas/examples/m.dummy new file mode 100644 index 00000000..0cd39166 --- /dev/null +++ b/event-schemas/examples/m.dummy @@ -0,0 +1,4 @@ +{ + "content": {}, + "type": "m.dummy" +} diff --git a/event-schemas/examples/m.key.verification.accept b/event-schemas/examples/m.key.verification.accept new file mode 100644 index 00000000..98e89c06 --- /dev/null +++ b/event-schemas/examples/m.key.verification.accept @@ -0,0 +1,12 @@ +{ + "type": "m.key.verification.accept", + "content": { + "transaction_id": "S0meUniqueAndOpaqueString", + "method": "m.sas.v1", + "key_agreement_protocol": "curve25519", + "hash": "sha256", + "message_authentication_code": "hkdf-hmac-sha256", + "short_authentication_string": ["decimal", "emoji"], + "commitment": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } +} diff --git a/event-schemas/examples/m.key.verification.cancel b/event-schemas/examples/m.key.verification.cancel new file mode 100644 index 00000000..9d78f67c --- /dev/null +++ b/event-schemas/examples/m.key.verification.cancel @@ -0,0 +1,8 @@ +{ + "type": "m.key.verification.cancel", + "content": { + "transaction_id": "S0meUniqueAndOpaqueString", + "code": "m.user", + "reason": "User rejected the key verification request" + } +} diff --git a/event-schemas/examples/m.key.verification.key b/event-schemas/examples/m.key.verification.key new file mode 100644 index 00000000..608a2ebd --- /dev/null +++ b/event-schemas/examples/m.key.verification.key @@ -0,0 +1,7 @@ +{ + "type": "m.key.verification.key", + "content": { + "transaction_id": "S0meUniqueAndOpaqueString", + "key": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } +} diff --git a/event-schemas/examples/m.key.verification.mac b/event-schemas/examples/m.key.verification.mac new file mode 100644 index 00000000..c77c3a8d --- /dev/null +++ b/event-schemas/examples/m.key.verification.mac @@ -0,0 +1,10 @@ +{ + "type": "m.key.verification.mac", + "content": { + "transaction_id": "S0meUniqueAndOpaqueString", + "keys": "2Wptgo4CwmLo/Y8B8qinxApKaCkBG2fjTWB7AbP5Uy+aIbygsSdLOFzvdDjww8zUVKCmI02eP9xtyJxc/cLiBA", + "mac": { + "ed25519:ABCDEF": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } + } +} diff --git a/event-schemas/examples/m.key.verification.request b/event-schemas/examples/m.key.verification.request new file mode 100644 index 00000000..258471d2 --- /dev/null +++ b/event-schemas/examples/m.key.verification.request @@ -0,0 +1,11 @@ +{ + "type": "m.key.verification.request", + "content": { + "from_device": "AliceDevice2", + "transaction_id": "S0meUniqueAndOpaqueString", + "methods": [ + "m.sas.v1" + ], + "timestamp": 1559598944869 + } +} diff --git a/event-schemas/examples/m.key.verification.start b/event-schemas/examples/m.key.verification.start new file mode 100644 index 00000000..52f16150 --- /dev/null +++ b/event-schemas/examples/m.key.verification.start @@ -0,0 +1,8 @@ +{ + "type": "m.key.verification.start", + "content": { + "from_device": "BobDevice1", + "transaction_id": "S0meUniqueAndOpaqueString", + "method": "m.sas.v1" + } +} diff --git a/event-schemas/examples/m.key.verification.start$m.sas.v1 b/event-schemas/examples/m.key.verification.start$m.sas.v1 new file mode 100644 index 00000000..dae1d405 --- /dev/null +++ b/event-schemas/examples/m.key.verification.start$m.sas.v1 @@ -0,0 +1,12 @@ +{ + "type": "m.key.verification.start", + "content": { + "from_device": "BobDevice1", + "transaction_id": "S0meUniqueAndOpaqueString", + "method": "m.sas.v1", + "key_agreement_protocols": ["curve25519"], + "hashes": ["sha256"], + "message_authentication_codes": ["hkdf-hmac-sha256"], + "short_authentication_string": ["decimal", "emoji"] + } +} diff --git a/event-schemas/examples/m.push_rules b/event-schemas/examples/m.push_rules index e4f0a959..34bc2fe6 100644 --- a/event-schemas/examples/m.push_rules +++ b/event-schemas/examples/m.push_rules @@ -107,8 +107,13 @@ ], "conditions": [ { - "is": "2", - "kind": "room_member_count" + "kind": "room_member_count", + "is": "2" + }, + { + "kind": "event_match", + "key": "type", + "pattern": "m.room.message" } ], "default": true, diff --git a/event-schemas/examples/m.room.encrypted#megolm b/event-schemas/examples/m.room.encrypted$megolm similarity index 100% rename from event-schemas/examples/m.room.encrypted#megolm rename to event-schemas/examples/m.room.encrypted$megolm diff --git a/event-schemas/examples/m.room.encrypted#olm b/event-schemas/examples/m.room.encrypted$olm similarity index 100% rename from event-schemas/examples/m.room.encrypted#olm rename to event-schemas/examples/m.room.encrypted$olm diff --git a/event-schemas/examples/m.room.member b/event-schemas/examples/m.room.member index b0aa59dd..18bc457b 100644 --- a/event-schemas/examples/m.room.member +++ b/event-schemas/examples/m.room.member @@ -4,7 +4,7 @@ "type": "m.room.member", "content": { "membership": "join", - "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF#auto", + "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", "displayname": "Alice Margatroid" } } diff --git a/event-schemas/examples/m.room.member#invite_room_state b/event-schemas/examples/m.room.member#invite_room_state deleted file mode 100644 index f8f05484..00000000 --- a/event-schemas/examples/m.room.member#invite_room_state +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$ref": "m.room.member", - "content": { - "membership": "invite", - "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF#auto", - "displayname": "Alice Margatroid" - }, - "unsigned": { - "age": 1234, - "invite_room_state": [ - { - "type": "m.room.name", - "state_key": "", - "content": { - "name": "Forest of Magic" - } - }, - { - "type": "m.room.join_rules", - "state_key": "", - "content": { - "join_rule": "invite" - } - } - ] - } -} diff --git a/event-schemas/examples/m.room.member$invite_room_state b/event-schemas/examples/m.room.member$invite_room_state new file mode 100644 index 00000000..54fcba60 --- /dev/null +++ b/event-schemas/examples/m.room.member$invite_room_state @@ -0,0 +1,14 @@ +{ + "$ref": "m.room.member", + "content": { + "membership": "invite", + "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", + "displayname": "Alice Margatroid" + }, + "unsigned": { + "age": 1234, + "invite_room_state": { + "$ref": "invite_room_state.json" + } + } +} diff --git a/event-schemas/examples/m.room.member#third_party_invite b/event-schemas/examples/m.room.member$third_party_invite similarity index 88% rename from event-schemas/examples/m.room.member#third_party_invite rename to event-schemas/examples/m.room.member$third_party_invite index c688a283..a40d44f9 100644 --- a/event-schemas/examples/m.room.member#third_party_invite +++ b/event-schemas/examples/m.room.member$third_party_invite @@ -2,7 +2,7 @@ "$ref": "m.room.member", "content": { "membership": "invite", - "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF#auto", + "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", "displayname": "Alice Margatroid", "third_party_invite": { "display_name": "alice", diff --git a/event-schemas/examples/m.room.message#m.audio b/event-schemas/examples/m.room.message$m.audio similarity index 99% rename from event-schemas/examples/m.room.message#m.audio rename to event-schemas/examples/m.room.message$m.audio index 2f743d49..58e874e0 100644 --- a/event-schemas/examples/m.room.message#m.audio +++ b/event-schemas/examples/m.room.message$m.audio @@ -11,4 +11,4 @@ }, "msgtype": "m.audio" } -} +} diff --git a/event-schemas/examples/m.room.message#m.emote b/event-schemas/examples/m.room.message$m.emote similarity index 100% rename from event-schemas/examples/m.room.message#m.emote rename to event-schemas/examples/m.room.message$m.emote diff --git a/event-schemas/examples/m.room.message#m.file b/event-schemas/examples/m.room.message$m.file similarity index 100% rename from event-schemas/examples/m.room.message#m.file rename to event-schemas/examples/m.room.message$m.file diff --git a/event-schemas/examples/m.room.message#m.image b/event-schemas/examples/m.room.message$m.image similarity index 100% rename from event-schemas/examples/m.room.message#m.image rename to event-schemas/examples/m.room.message$m.image diff --git a/event-schemas/examples/m.room.message#m.location b/event-schemas/examples/m.room.message$m.location similarity index 100% rename from event-schemas/examples/m.room.message#m.location rename to event-schemas/examples/m.room.message$m.location diff --git a/event-schemas/examples/m.room.message#m.notice b/event-schemas/examples/m.room.message$m.notice similarity index 100% rename from event-schemas/examples/m.room.message#m.notice rename to event-schemas/examples/m.room.message$m.notice diff --git a/event-schemas/examples/m.room.message$m.server_notice b/event-schemas/examples/m.room.message$m.server_notice new file mode 100644 index 00000000..0eb44ea7 --- /dev/null +++ b/event-schemas/examples/m.room.message$m.server_notice @@ -0,0 +1,11 @@ +{ + "$ref": "core/room_event.json", + "type": "m.room.message", + "content": { + "body": "Human-readable message to explain the notice", + "msgtype": "m.server_notice", + "server_notice_type": "m.server_notice.usage_limit_reached", + "admin_contact": "mailto:server.admin@example.org", + "limit_type": "monthly_active_user" + } +} diff --git a/event-schemas/examples/m.room.message#m.text b/event-schemas/examples/m.room.message$m.text similarity index 100% rename from event-schemas/examples/m.room.message#m.text rename to event-schemas/examples/m.room.message$m.text diff --git a/event-schemas/examples/m.room.message#m.video b/event-schemas/examples/m.room.message$m.video similarity index 100% rename from event-schemas/examples/m.room.message#m.video rename to event-schemas/examples/m.room.message$m.video diff --git a/event-schemas/examples/m.room_key_request#cancel_request b/event-schemas/examples/m.room_key_request$cancel_request similarity index 77% rename from event-schemas/examples/m.room_key_request#cancel_request rename to event-schemas/examples/m.room_key_request$cancel_request index c6eb25de..afc1c350 100644 --- a/event-schemas/examples/m.room_key_request#cancel_request +++ b/event-schemas/examples/m.room_key_request$cancel_request @@ -1,6 +1,6 @@ { "content": { - "action": "cancel_request", + "action": "request_cancellation", "requesting_device_id": "RJYKSTBOIE", "request_id": "1495474790150.19" }, diff --git a/event-schemas/examples/m.room_key_request#request b/event-schemas/examples/m.room_key_request$request similarity index 100% rename from event-schemas/examples/m.room_key_request#request rename to event-schemas/examples/m.room_key_request$request diff --git a/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml b/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml index 8ff27b1e..ff40efcb 100644 --- a/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml +++ b/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml @@ -19,8 +19,8 @@ properties: type: integer thumbnail_url: description: |- - The URL to a thumbnail of the image. Only present if the - thumbnail is unencrypted. + The URL (typically `MXC URI`_) to a thumbnail of the image. + Only present if the thumbnail is unencrypted. type: string thumbnail_file: description: |- diff --git a/event-schemas/schema/core-event-schema/room_event.yaml b/event-schemas/schema/core-event-schema/room_event.yaml index 007372a5..231d5c65 100644 --- a/event-schemas/schema/core-event-schema/room_event.yaml +++ b/event-schemas/schema/core-event-schema/room_event.yaml @@ -1,7 +1,6 @@ allOf: - $ref: sync_room_event.yaml -description: In addition to the Event fields, Room Events have the following additional - fields. +description: Room Events have the following fields. properties: room_id: description: |- diff --git a/event-schemas/schema/core-event-schema/state_event.yaml b/event-schemas/schema/core-event-schema/state_event.yaml index 37d4426f..816f925f 100644 --- a/event-schemas/schema/core-event-schema/state_event.yaml +++ b/event-schemas/schema/core-event-schema/state_event.yaml @@ -1,7 +1,6 @@ allOf: - $ref: room_event.yaml - $ref: sync_state_event.yaml -description: In addition to the Room Event fields, State Events have the following - additional fields. +description: State Events have the following fields. title: State Event type: object diff --git a/event-schemas/schema/m.call.hangup b/event-schemas/schema/m.call.hangup index 9d45d179..c0478f5a 100644 --- a/event-schemas/schema/m.call.hangup +++ b/event-schemas/schema/m.call.hangup @@ -15,6 +15,14 @@ "version": { "type": "integer", "description": "The version of the VoIP specification this message adheres to. This specification is version 0." + }, + "reason": { + "type": "string", + "description": "Optional error reason for the hangup. This should not be provided when the user naturally ends or rejects the call. When there was an error in the call negotiation, this should be ``ice_failed`` for when ICE negotiation fails or ``invite_timeout`` for when the other party did not answer in time.", + "enum": [ + "ice_failed", + "invite_timeout" + ] } }, "required": ["call_id", "version"] diff --git a/event-schemas/schema/m.dummy b/event-schemas/schema/m.dummy new file mode 100644 index 00000000..8e4b6f94 --- /dev/null +++ b/event-schemas/schema/m.dummy @@ -0,0 +1,23 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + This event type is used to indicate new Olm sessions for end-to-end encryption. + Typically it is encrypted as an ``m.room.encrypted`` event, then sent as a `to-device`_ + event. + + The event does not have any content associated with it. The sending client is expected + to send a key share request shortly after this message, causing the receiving client to + process this ``m.dummy`` event as the most recent event and using the keyshare request + to set up the session. The keyshare request and ``m.dummy`` combination should result + in the original sending client receiving keys over the newly established session. +properties: + content: + properties: {} + type: object + type: + enum: + - m.dummy + type: string +type: object diff --git a/event-schemas/schema/m.key.verification.accept b/event-schemas/schema/m.key.verification.accept new file mode 100644 index 00000000..ad54488e --- /dev/null +++ b/event-schemas/schema/m.key.verification.accept @@ -0,0 +1,64 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + Accepts a previously sent ``m.key.verification.start`` message. Typically sent as a + `to-device`_ event. +properties: + content: + properties: + transaction_id: + type: string + description: |- + An opaque identifier for the verification process. Must be the same as + the one used for the ``m.key.verification.start`` message. + method: + type: string + enum: ["m.sas.v1"] + description: |- + The verification method to use. + key_agreement_protocol: + type: string + description: |- + The key agreement protocol the device is choosing to use, out of the + options in the ``m.key.verification.start`` message. + hash: + type: string + description: |- + The hash method the device is choosing to use, out of the options in + the ``m.key.verification.start`` message. + message_authentication_code: + type: string + description: |- + The message authentication code the device is choosing to use, out of + the options in the ``m.key.verification.start`` message. + short_authentication_string: + type: array + description: |- + The SAS methods both devices involved in the verification process + understand. Must be a subset of the options in the ``m.key.verification.start`` + message. + items: + type: string + enum: ["decimal", "emoji"] + commitment: + type: string + description: |- + The hash (encoded as unpadded base64) of the concatenation of the device's + ephemeral public key (encoded as unpadded base64) and the canonical JSON + representation of the ``m.key.verification.start`` message. + required: + - transaction_id + - method + - key_agreement_protocol + - hash + - message_authentication_code + - short_authentication_string + - commitment + type: object + type: + enum: + - m.key.verification.accept + type: string +type: object diff --git a/event-schemas/schema/m.key.verification.cancel b/event-schemas/schema/m.key.verification.cancel new file mode 100644 index 00000000..36ffc9ea --- /dev/null +++ b/event-schemas/schema/m.key.verification.cancel @@ -0,0 +1,70 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + Cancels a key verification process/request. Typically sent as a `to-device`_ event. +properties: + content: + properties: + transaction_id: + type: string + description: |- + The opaque identifier for the verification process/request. + reason: + type: string + description: |- + A human readable description of the ``code``. The client should only rely on this + string if it does not understand the ``code``. + code: + type: string + # Note: this is not an enum because we go into detail about the different + # error codes. If we made this an enum, we'd be repeating information. + # Also, we can't put a real bulleted list in here because the HTML2RST parser + # cuts the text at weird points, breaking the list completely. + description: |- + The error code for why the process/request was cancelled by the user. Error + codes should use the Java package naming convention if not in the following + list: + + ``m.user``: The user cancelled the verification. + + ``m.timeout``: The verification process timed out. Verification processes + can define their own timeout parameters. + + ``m.unknown_transaction``: The device does not know about the given transaction + ID. + + ``m.unknown_method``: The device does not know how to handle the requested + method. This should be sent for ``m.key.verification.start`` messages and + messages defined by individual verification processes. + + ``m.unexpected_message``: The device received an unexpected message. Typically + raised when one of the parties is handling the verification out of order. + + ``m.key_mismatch``: The key was not verified. + + ``m.user_mismatch``: The expected user did not match the user verified. + + ``m.invalid_message``: The message received was invalid. + + ``m.accepted``: A ``m.key.verification.request`` was accepted by a different + device. The device receiving this error can ignore the verification request. + + Clients should be careful to avoid error loops. For example, if a device sends + an incorrect message and the client returns ``m.invalid_message`` to which it + gets an unexpected response with ``m.unexpected_message``, the client should not + respond again with ``m.unexpected_message`` to avoid the other device potentially + sending another error response. + + .. The above blank line is important for RST. + required: + - transaction_id + - code + - reason + type: object + type: + enum: + - m.key.verification.cancel + type: string +type: object diff --git a/event-schemas/schema/m.key.verification.key b/event-schemas/schema/m.key.verification.key new file mode 100644 index 00000000..6dc4954b --- /dev/null +++ b/event-schemas/schema/m.key.verification.key @@ -0,0 +1,28 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + Sends the ephemeral public key for a device to the partner device. Typically sent as a + `to-device`_ event. +properties: + content: + properties: + transaction_id: + type: string + description: |- + An opaque identifier for the verification process. Must be the same as + the one used for the ``m.key.verification.start`` message. + key: + type: string + description: |- + The device's ephemeral public key, encoded as unpadded base64. + required: + - transaction_id + - key + type: object + type: + enum: + - m.key.verification.key + type: string +type: object diff --git a/event-schemas/schema/m.key.verification.mac b/event-schemas/schema/m.key.verification.mac new file mode 100644 index 00000000..769ebe15 --- /dev/null +++ b/event-schemas/schema/m.key.verification.mac @@ -0,0 +1,38 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + Sends the MAC of a device's key to the partner device. Typically sent as a + `to-device`_ event. +properties: + content: + properties: + transaction_id: + type: string + description: |- + An opaque identifier for the verification process. Must be the same as + the one used for the ``m.key.verification.start`` message. + mac: + type: object + description: |- + A map of the key ID to the MAC of the key, using the algorithm in the + verification process. The MAC is encoded as unpadded base64. + additionalProperties: + type: string + description: The key's MAC, encoded as unpadded base64. + keys: + type: string + description: |- + The MAC of the comma-separated, sorted, list of key IDs given in the ``mac`` + property, encoded as unpadded base64. + required: + - transaction_id + - mac + - keys + type: object + type: + enum: + - m.key.verification.mac + type: string +type: object diff --git a/event-schemas/schema/m.key.verification.request b/event-schemas/schema/m.key.verification.request new file mode 100644 index 00000000..c9efa14e --- /dev/null +++ b/event-schemas/schema/m.key.verification.request @@ -0,0 +1,43 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + Requests a key verification with another user's devices. Typically sent as a + `to-device`_ event. +properties: + content: + properties: + from_device: + type: string + description: |- + The device ID which is initiating the request. + transaction_id: + type: string + description: |- + An opaque identifier for the verification request. Must be unique + with respect to the devices involved. + methods: + type: array + description: |- + The verification methods supported by the sender. + items: + type: string + timestamp: + type: integer + format: int64 + description: |- + The POSIX timestamp in milliseconds for when the request was made. If + the request is in the future by more than 5 minutes or more than 10 + minutes in the past, the message should be ignored by the receiver. + required: + - from_device + - transaction_id + - methods + - timestamp + type: object + type: + enum: + - m.key.verification.request + type: string +type: object diff --git a/event-schemas/schema/m.key.verification.start b/event-schemas/schema/m.key.verification.start new file mode 100644 index 00000000..faa7a96a --- /dev/null +++ b/event-schemas/schema/m.key.verification.start @@ -0,0 +1,42 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + Begins a key verification process. Typically sent as a `to-device`_ event. The ``method`` + field determines the type of verification. The fields in the event will differ depending + on the ``method``. This definition includes fields that are in common among all variants. +properties: + content: + properties: + from_device: + type: string + description: |- + The device ID which is initiating the process. + transaction_id: + type: string + description: |- + An opaque identifier for the verification process. Must be unique + with respect to the devices involved. Must be the same as the + ``transaction_id`` given in the ``m.key.verification.request`` + if this process is originating from a request. + method: + type: string + description: |- + The verification method to use. + next_method: + type: string + description: |- + Optional method to use to verify the other user's key with. Applicable + when the ``method`` chosen only verifies one user's key. This field will + never be present if the ``method`` verifies keys both ways. + required: + - from_device + - transaction_id + - method + type: object + type: + enum: + - m.key.verification.start + type: string +type: object diff --git a/event-schemas/schema/m.key.verification.start$m.sas.v1 b/event-schemas/schema/m.key.verification.start$m.sas.v1 new file mode 100644 index 00000000..daf6fa39 --- /dev/null +++ b/event-schemas/schema/m.key.verification.start$m.sas.v1 @@ -0,0 +1,69 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + Begins a SAS key verification process using the ``m.sas.v1`` method. Typically sent as a `to-device`_ event. +properties: + content: + properties: + from_device: + type: string + description: |- + The device ID which is initiating the process. + transaction_id: + type: string + description: |- + An opaque identifier for the verification process. Must be unique + with respect to the devices involved. Must be the same as the + ``transaction_id`` given in the ``m.key.verification.request`` + if this process is originating from a request. + method: + type: string + enum: ["m.sas.v1"] + description: |- + The verification method to use. + key_agreement_protocols: + type: array + description: |- + The key agreement protocols the sending device understands. Must + include at least ``curve25519``. + items: + type: string + hashes: + type: array + description: |- + The hash methods the sending device understands. Must include at least + ``sha256``. + items: + type: string + message_authentication_codes: + type: array + description: |- + The message authentication codes that the sending device understands. + Must include at least ``hkdf-hmac-sha256``. + items: + type: string + short_authentication_string: + type: array + description: |- + The SAS methods the sending device (and the sending device's user) + understands. Must include at least ``decimal``. Optionally can include + ``emoji``. + items: + type: string + enum: ["decimal", "emoji"] + required: + - from_device + - transaction_id + - method + - key_agreement_protocols + - hashes + - message_authentication_codes + - short_authentication_string + type: object + type: + enum: + - m.key.verification.start + type: string +type: object diff --git a/event-schemas/schema/m.room.member b/event-schemas/schema/m.room.member index de14644d..1033c098 100644 --- a/event-schemas/schema/m.room.member +++ b/event-schemas/schema/m.room.member @@ -21,6 +21,29 @@ description: |- This event may also include an ``invite_room_state`` key inside the event's ``unsigned`` data. If present, this contains an array of ``StrippedState`` Events. These events provide information on a subset of state events such as the room name. + + The user for which a membership applies is represented by the ``state_key``. Under some conditions, + the ``sender`` and ``state_key`` may not match - this may be interpreted as the ``sender`` affecting + the membership state of the ``state_key`` user. + + The ``membership`` for a given user can change over time. The table below represents the various changes + over time and how clients and servers must interpret those changes. Previous membership can be retrieved + from the ``prev_content`` object on an event. If not present, the user's previous membership must be assumed + as ``leave``. + + .. TODO: Improve how this table is written? We use a csv-table to get around vertical header restrictions. + + .. csv-table:: + :header-rows: 1 + :stub-columns: 1 + + "","to ``invite``","to ``join``","to ``leave``","to ``ban``","to ``knock``" + "from ``invite``","No change.","User joined the room.","If the ``state_key`` is the same as the ``sender``, the user rejected the invite. Otherwise, the ``state_key`` user had their invite revoked.","User was banned.","Not implemented." + "from ``join``","Must never happen.","``displayname`` or ``avatar_url`` changed.","If the ``state_key`` is the same as the ``sender``, the user left. Otherwise, the ``state_key`` user was kicked.","User was kicked and banned.","Not implemented." + "from ``leave``","New invitation sent.","User joined.","No change.","User was banned.","Not implemented." + "from ``ban``","Must never happen.","Must never happen.","User was unbanned.","No change.","Not implemented." + "from ``knock``","Not implemented.","Not implemented.","Not implemented.","Not implemented.","Not implemented." + properties: content: properties: @@ -81,24 +104,7 @@ properties: invite_room_state: description: 'A subset of the state of the room at the time of the invite, if ``membership`` is ``invite``. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for ``m.room.avatar``, ``m.room.canonical_alias``, ``m.room.join_rules``, and ``m.room.name`` SHOULD be included.' items: - description: 'A stripped down state event, with only the ``type``, ``state_key`` and ``content`` keys.' - properties: - content: - description: The ``content`` for the event. - title: EventContent - type: object - state_key: - description: The ``state_key`` for the event. - type: string - type: - description: The ``type`` for the event. - type: string - required: - - type - - state_key - - content - title: StrippedState - type: object + $ref: "stripped_state.yaml" type: array required: - membership diff --git a/event-schemas/schema/m.room.message#m.audio b/event-schemas/schema/m.room.message$m.audio similarity index 90% rename from event-schemas/schema/m.room.message#m.audio rename to event-schemas/schema/m.room.message$m.audio index c258b85f..fb049fc9 100644 --- a/event-schemas/schema/m.room.message#m.audio +++ b/event-schemas/schema/m.room.message$m.audio @@ -27,7 +27,9 @@ properties: - m.audio type: string url: - description: Required if the file is not encrypted. The URL to the audio clip. + description: |- + Required if the file is unencrypted. The URL (typically `MXC URI`_) + to the audio clip. type: string file: description: |- diff --git a/event-schemas/schema/m.room.message#m.emote b/event-schemas/schema/m.room.message$m.emote similarity index 100% rename from event-schemas/schema/m.room.message#m.emote rename to event-schemas/schema/m.room.message$m.emote diff --git a/event-schemas/schema/m.room.message#m.file b/event-schemas/schema/m.room.message$m.file similarity index 93% rename from event-schemas/schema/m.room.message#m.file rename to event-schemas/schema/m.room.message$m.file index 2fb4fe50..54a999ec 100644 --- a/event-schemas/schema/m.room.message#m.file +++ b/event-schemas/schema/m.room.message$m.file @@ -42,7 +42,9 @@ properties: - m.file type: string url: - description: Required if the file is unencrypted. The URL to the file. + description: |- + Required if the file is unencrypted. The URL (typically `MXC URI`_) + to the file. type: string file: description: |- @@ -53,7 +55,6 @@ properties: required: - msgtype - body - - filename type: object type: enum: diff --git a/event-schemas/schema/m.room.message#m.image b/event-schemas/schema/m.room.message$m.image similarity index 89% rename from event-schemas/schema/m.room.message#m.image rename to event-schemas/schema/m.room.message$m.image index 349f78f4..8944ce96 100644 --- a/event-schemas/schema/m.room.message#m.image +++ b/event-schemas/schema/m.room.message$m.image @@ -17,7 +17,9 @@ properties: - m.image type: string url: - description: Required if the file is unencrypted. The URL to the image. + description: |- + Required if the file is unencrypted. The URL (typically `MXC URI`_) + to the image. type: string file: description: |- diff --git a/event-schemas/schema/m.room.message#m.location b/event-schemas/schema/m.room.message$m.location similarity index 100% rename from event-schemas/schema/m.room.message#m.location rename to event-schemas/schema/m.room.message$m.location diff --git a/event-schemas/schema/m.room.message#m.notice b/event-schemas/schema/m.room.message$m.notice similarity index 71% rename from event-schemas/schema/m.room.message#m.notice rename to event-schemas/schema/m.room.message$m.notice index ef97e28a..19c4f985 100644 --- a/event-schemas/schema/m.room.message#m.notice +++ b/event-schemas/schema/m.room.message$m.notice @@ -12,6 +12,16 @@ properties: enum: - m.notice type: string + format: + description: |- + The format used in the ``formatted_body``. Currently only + ``org.matrix.custom.html`` is supported. + type: string + formatted_body: + description: |- + The formatted version of the ``body``. This is required if ``format`` + is specified. + type: string required: - msgtype - body diff --git a/event-schemas/schema/m.room.message$m.server_notice b/event-schemas/schema/m.room.message$m.server_notice new file mode 100644 index 00000000..f1848821 --- /dev/null +++ b/event-schemas/schema/m.room.message$m.server_notice @@ -0,0 +1,39 @@ +--- +allOf: + - $ref: core-event-schema/room_event.yaml +description: Represents a server notice for a user. +properties: + content: + properties: + body: + description: A human-readable description of the notice. + type: string + msgtype: + enum: + - m.server_notice + type: string + server_notice_type: + description: |- + The type of notice being represented. + type: string + admin_contact: + description: |- + A URI giving a contact method for the server administrator. Required if the + notice type is ``m.server_notice.usage_limit_reached``. + type: string + limit_type: + description: |- + The kind of usage limit the server has exceeded. Required if the notice type is + ``m.server_notice.usage_limit_reached``. + type: string + required: + - msgtype + - body + - server_notice_type + type: object + type: + enum: + - m.room.message + type: string +title: ServerNoticeMessage +type: object diff --git a/event-schemas/schema/m.room.message#m.text b/event-schemas/schema/m.room.message$m.text similarity index 100% rename from event-schemas/schema/m.room.message#m.text rename to event-schemas/schema/m.room.message$m.text diff --git a/event-schemas/schema/m.room.message#m.video b/event-schemas/schema/m.room.message$m.video similarity index 87% rename from event-schemas/schema/m.room.message#m.video rename to event-schemas/schema/m.room.message$m.video index 8a66fdeb..1a3c3e40 100644 --- a/event-schemas/schema/m.room.message#m.video +++ b/event-schemas/schema/m.room.message$m.video @@ -28,8 +28,8 @@ properties: type: integer thumbnail_url: description: |- - The URL to an image thumbnail of the video clip. Only present if the - thumbnail is unencrypted. + The URL (typically `MXC URI`_) to an image thumbnail of + the video clip. Only present if the thumbnail is unencrypted. type: string thumbnail_file: description: |- @@ -48,7 +48,9 @@ properties: - m.video type: string url: - description: Required if the file is unencrypted. The URL to the video clip. + description: |- + Required if the file is unencrypted. The URL (typically `MXC URI`_) + to the video clip. type: string file: description: |- diff --git a/event-schemas/schema/m.room_key_request b/event-schemas/schema/m.room_key_request index 007d0086..c08ac0e3 100644 --- a/event-schemas/schema/m.room_key_request +++ b/event-schemas/schema/m.room_key_request @@ -38,7 +38,7 @@ properties: action: enum: - request - - cancel_request + - request_cancellation type: string requesting_device_id: description: ID of the device requesting the key. diff --git a/event-schemas/schema/stripped_state.yaml b/event-schemas/schema/stripped_state.yaml new file mode 100644 index 00000000..ec591bf1 --- /dev/null +++ b/event-schemas/schema/stripped_state.yaml @@ -0,0 +1,44 @@ +# Copyright 2019 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Note: this, and the example, are in the `event-schemas` directory because +# the CS API uses a symlink. In order for the `m.room.member` event to +# reference this, we'd need to use relative pathing. The symlink makes this +# difficult because the schema would be at two different locations, with +# different relative pathing. + +title: StrippedState +type: object +description: |- + A stripped down state event, with only the ``type``, ``state_key``, + ``sender``, and ``content`` keys. +properties: + content: + description: The ``content`` for the event. + title: EventContent + type: object + state_key: + description: The ``state_key`` for the event. + type: string + type: + description: The ``type`` for the event. + type: string + sender: + description: The ``sender`` for the event. + type: string +required: + - type + - state_key + - content + - sender diff --git a/meta/github-labels.rst b/meta/github-labels.rst new file mode 100644 index 00000000..f674b81b --- /dev/null +++ b/meta/github-labels.rst @@ -0,0 +1,91 @@ +The following labels are used to help categorize issues: + +`spec-omission `_ +-------------------------------------------------------------------------------- + +Things which have been implemented but not currently specified. These may range +from entire API endpoints, to particular options or return parameters. + +Issues with this label will have been implemented in `Synapse +`_. Normally there will be a design +document in Google Docs or similar which describes the feature. + +Examples: + +* `Spec PUT /directory/list `_ +* `Unspec'd server_name request param for /join/{roomIdOrAlias} + `_ + +`clarification `_ +-------------------------------------------------------------------------------- + +An area where the spec could do with being more explicit. + +Examples: + +* `Spec the implicit limit on /syncs + `_ + +* `Clarify the meaning of the currently_active flags in presence events + `_ + +`spec-bug `_ +---------------------------------------------------------------------- + +Something which is in the spec, but is wrong. + +Note: this is *not* for things that are badly designed or don't work well +(for which see 'improvement' or 'feature') - it is for places where the +spec doesn't match reality. + +Examples: + +* `swagger is wrong for directory PUT + `_ + +* `receipts section still refers to initialSync + `_ + +`improvement `_ +---------------------------------------------------------------------------- + +A suggestion for a relatively simple improvement to the protocol. + +Examples: + +* `We need a 'remove 3PID' API so that users can remove mappings + `_ +* `We should mandate that /publicRooms requires an access_token + `_ + +`feature `_ +-------------------------------------------------------------------- + +A suggestion for a significant extension to the matrix protocol which +needs considerable consideration before implementation. + +Examples: + +* `Peer-to-peer Matrix `_ +* `Specify a means for clients to "edit" previous messages + `_ + + +`wart `_ +-------------------------------------------------------------- + +A point where the protocol is inconsistent or inelegant, but which isn't really +causing anybody any problems right now. Might be nice to consider fixing one +day. + + +`question `_ +---------------------------------------------------------------------- + +A thought or idea about the protocol which we aren't really sure whether to +pursue or not. + +Examples: + +* `Should we prepend anti-eval code to our json responses? + `_ diff --git a/proposals/1717-key_verification.md b/proposals/1717-key_verification.md new file mode 100644 index 00000000..429e3a97 --- /dev/null +++ b/proposals/1717-key_verification.md @@ -0,0 +1,167 @@ +# Key verification mechanisms + +Key verification is an essential part of ensuring that end-to-end encrypted +messages are secure. Matrix may support multiple verification methods that +require sending events; in fact, two such methods (such as [MSC +1267](https://github.com/matrix-org/matrix-doc/issues/1267) and [MSC +1543](https://github.com/matrix-org/matrix-doc/issues/1543)) have already been +proposed. + +This proposal tries to present a common framework for verification methods to +use, and presents a way to request key verification. + +## Proposal + +Each key verification method is identified by a name. Verification method +names defined in the Matrix spec will begin with `m.`, and verification method +names that are not defined in the Matrix spec must be namespaced following the +Java package naming convention. + +If Alice wants to verify keys with Bob, Alice's device may send `to_device` +events to Bob's devices with the `type` set to `m.key.verification.request`, as +described below. The `m.key.verification.request` messages should all have the +same `transaction_id`, and are considered to be a single request. Thus, for +example, if Bob rejects the request on one device, then the entire request +should be considered as rejected across all of his devices. Similarly, if Bob +accepts the request on one device, that device is now in charge of completing +the key verification, and Bob's other devices no longer need to be involved. + +The `m.key.verification.request` event lists the verification methods that +Alice's device supports, and upon receipt of this message, Bob's client should +prompt him to verify keys with Alice using one of the applicable methods. In +order to avoid displaying stale key verification prompts, if Bob does not +interact with the prompt, it should be automatically hidden 10 minutes after +the message is sent (according to the `timestamp` field), or 2 minutes after +the client receives the message, whichever comes first. The prompt should also +be hidden if an appropriate `m.key.verification.cancel` message is received. + +If Bob chooses to reject the key verification request, Bob's client should send +a `m.key.verification.cancel` message to Alice's device. This indicates to +Alice that Bob does not wish to verify keys with her. In this case, Alice's +device should send an `m.key.verification.cancel` message to all of Bob's +devices to notify them that the request has been rejected. + +If one of Bob's clients does not understand any of the methods offered, it +should display a message to Bob saying so. However, it should not send a +`m.key.verification.cancel` message to Alice's device unless Bob chooses to +reject the verification request, as Bob may have another device that is capable +of verifying using one of the given methods. + +To initiate a key verification process, Bob's device sends a `to_device` event +to one of Alice's devices with the `type` set to `m.key.verification.start`. +This may either be done in response to an `m.key.verification.request` message, +or can be done independently. If it is done in response to an +`m.key.verification.request` messsage, it should use the same `transaction_id` +as the `m.key.verification.request` message. If Alice's device receives an +`m.key.verification.start` message in response to an +`m.key.verification.request` message, it should send an +`m.key.verification.cancel` message to Bob's other devices that it had +originally sent an `m.key.verification.request` to, in order to cancel the key +verification request. + +Verification methods can define other events required to complete the +verification. Event types for verification methods defined in the Matrix spec +should be in the `m.key.verification` namespace. Event types that are not +defined in the Matrix spec must be namespaced following the Java package naming +convention. + +Alice's or Bob's devices can cancel a key verification process or a key +verification request by sending a `to_device` event with `type` set to +`m.key.verification.cancel`. + +### Event Definitions + +#### `m.key.verification.request` + +Requests a key verification. + +Properties: + +- `from_device` (string): Required. The device ID of the device requesting + verification. +- `transaction_id` (string): Required. An identifier for the verification + request. Must be unique with respect to the pair of devices. +- `methods` ([string]): Required. The verification methods supported by the + sender. +- `timestamp` (integer): Required. The time when the request was made. If the + timestamp is in the future (by more than 5 minutes, to allow for clock skew), + or more than 10 minutes in the past, then the message must be ignored. + +#### `m.key.verification.start` + +Begins a key verification process. + +Properties: + +- `method` (string): Required. The verification method to use. +- `from_device` (string): Required. The device ID of the device starting the + verification process. +- `transaction_id` (string): Required. An identifier for the verification + process. If this message is sent in reponse to an + `m.key.verification.request` event, then it must use the same + `transaction_id` as the one given in the `m.key.verification.request`. +- `next_method` (string): Optional. If the selected verification method only + verifies one user's key, then this property can be used to indicate the + method to use to verify the other user's key, which will be started + immediately after after the current key verification is complete. + +Key verification methods can define additional properties to be included. + +#### `m.key.verification.cancel` + +Cancels a key verification process or a key verification request. Upon +receiving an `m.key.verification.cancel` message, the receiving device must +cancel the verification or the request. If it is a verification process that +is cancelled, or a verification request initiated by the recipient of the +cancellation message, the device should inform the user of the reason. + +Properties: + +- `transaction_id` (string): the identifier for the request or key verification + to cancel. +- `code` (string): machine-readable reason for cancelling. Possible reasons + are: + - `m.user`: the user cancelled the verification. + - `m.timeout`: the verification process has timed out. Different verification + methods may define their own timeouts. + - `m.unknown_transaction`: the device does not know about the given transaction + ID. + - `m.unknown_method`: the device does not know how to handle the given method. + This can be sent in response to an `m.key.verification.start` message, or + can be sent in response to other verification method-specific messages. + - `m.unexpected_message`: the device received an unexpected message. For + example, a message for a verification method may have been received when it + was not expected. + - `m.key_mismatch`: the key was not verified. + - `m.user_mismatch`: the expected user did not match the user verified. + - `m.invalid_message`: an invalid message was received. + - `m.accepted`: when an `m.key.verification.request` is accepted by one + device, an `m.key.verification.cancel` message with `code` set to + `m.accepted` is sent to the other devices +- `reason` (string): human-readable reason for cancelling. This should only be + used if the recieving client does not understand the code given in the `code` + property. + +Verification methods may define their own additional cancellation codes. +Cancellation codes defined in the Matrix spec will begin with `m.`; other +cancellation codes must be namespaced following the Java package naming +convention. + +## Tradeoffs + +Rather than broadcasting verification requests to Bob's devices, Alice could +simply send an `m.key.verification.start` request to a single device. However, +this would require Alice to choose the right device to send to, which may be +hard for Alice to do if, for example, Bob has many devices, or if his devices +have similar names. + +## Security considerations + +An attacker could try to spam a user with verification requests. Clients +should take care that such requests do not interfere with a user's use of the +client. + +## Conclusion + +This proposal presents common event definitions for use by key verification +methods and defines a way for users to request key verification. diff --git a/proposals/1719-olm_unwedging.md b/proposals/1719-olm_unwedging.md new file mode 100644 index 00000000..dffdc8b1 --- /dev/null +++ b/proposals/1719-olm_unwedging.md @@ -0,0 +1,59 @@ +# Olm unwedging + +Olm sessions sometimes get out of sync, resulting in undecryptable messages. +This can happen for several reasons. For example, if a user restores their +client state from a backup, the client will be using an old ratchet state +([riot-web#3822](https://github.com/vector-im/riot-web/issues/3822)). Or a +client might expire a one-time key that another client is trying to use +([riot-web#3309](https://github.com/vector-im/riot-web/issues/3309)). This +proposal documents a method for devices to create a new session to replace the +broken session. + +## Proposal + +When a device receives an olm-encrypted message that it cannot decrypt, it +should assume that the olm session has become corrupted and create a new olm +session to replace it. It should then send a dummy message, using that +session, to the other party in order to inform them of the new session. To +send a dummy message, clients may send an event with type `m.dummy`, and with +empty contents. + +In order to avoid creating too many extra sessions, a client should rate-limit +the number of new sessions it creates per device that it receives a message +from; the client should not create a new session with another device if it has +already created one for that given device in the past 1 hour. + +Clients may wish to take steps to mitigate the loss of the undecryptable +messages. For example, megolm sessions that were sent using the old session +would have been lost, so the client can send +[`m.room_key_request`](https://matrix.org/docs/spec/client_server/latest.html#m-room-key-request) +messages to re-request any megolm sessions that it is unable to decrypt. + +The spec currently says, "If a client has multiple sessions established with +another device, it should use the session from which it last received a +message." (the last paragraph of the [`m.olm.v1.curve25519-aes-sha2` +section](https://matrix.org/docs/spec/client_server/r0.4.0.html#m-olm-v1-curve25519-aes-sha2)). +When comparing the time of the last received message for each session, the +client should only consider messages that were successfully decrypted, +and for sessions that have never received a message, it should use the creation +time of the session. The spec will be changed to read: + +> If a client has multiple sessions established with another device, it should +> use the session from which it last received and successfully decrypted a +> message. For these purposes, a session that has not received any messages +> should use its creation time as the time that it last received a message. + +## Tradeoffs + +## Potential issues + +## Security considerations + +An attacker could use this to create a new session on a device that they are +able to read. However, this would require the attacker to have compromised the +device's keys. + +## Conclusion + +This proposal outlines how wedged olm sessions can be replaced by a new +session. diff --git a/proposals/1779-open-governance.md b/proposals/1779-open-governance.md new file mode 100644 index 00000000..bfe86eb8 --- /dev/null +++ b/proposals/1779-open-governance.md @@ -0,0 +1,457 @@ +# Proposal for Open Governance of Matrix.org + +This whole document is the proposed constitution proposal for Matrix.org, and +will form the basis of the first full Articles of Association (AoA) for [The +Matrix.org Foundation +C.I.C.](https://beta.companieshouse.gov.uk/company/11648710) - a non-profit +legal entity incorporated to act as the neutral guardian of the Matrix +decentralised communication standard on behalf of the whole Matrix community. + +See https://matrix.org/blog/2018/10/29/introducing-the-matrix-org-foundation-part-1-of-2/ +for more context. + +This obsoletes [MSC1318](https://github.com/matrix-org/matrix-doc/issues/1318). + +**This MSC is now formalised in the official Rules of the Matrix.org Foundation, +maintained at https://docs.google.com/document/d/1MhqsuIUxPc7Vf_y8D250mKZlLeQS6E39DPY6Azpc2NY** + +## Introduction + +Historically the core team of Matrix has been paid to work on it by the same +employer (currently New Vector; the startup incorporated to hire the core +team in Aug 2017). Whilst convenient in initially getting Matrix built, we +recognise that this could create a potential conflict of interest between the +core team’s responsibilities to neutrally support the wider Matrix.org ecosystem +versus the need for New Vector to be able to support the team, and it has always +been the plan to set up a completely neutral custodian for the standard once it +had reached sufficient maturity. + +This proposal seeks to establish a new open governance process for Matrix.org, +such that once the specification has finally been ‘born’ and reached an initial +‘r0’ release across all APIs, control of Matrix.org can be decoupled from New +Vector and better support contributions from the whole ecosystem. + +The concepts here are somewhat inspired by [Rust’s Governance +Model](https://github.com/rust-lang/rfcs/blob/master/text/1068-rust-governance.md); +a highly regarded solution to a similar problem: an ambitious +open-source project which has been too many years in the making, incubated at +first by a single company (Mozilla Corporation), which also enjoys a very +enthusiastic community! + +## Overview + +Governance of the project is split into two teams: the Spec Core Team and the +Guardians of the Foundation. In brief: + +The Spec Core Team are the technical experts who curate and edit the Matrix +Specification from day to day, and so steer the evolution of the protocol by +having final review over which Matrix Spec Changes (MSCs) are merged into the +core spec. + +The Guardians are the legal directors of the non-profit Foundation, and are +responsible for ensuring that the Foundation (and by extension the Spec Core +Team) keeps on mission and neutrally protects the development of Matrix. +Guardians are typically independent of the commercial Matrix ecosystem and may +even not be members of today’s Matrix community, but are deeply aligned with the +mission of the project. Guardians are selected to be respected and trusted by +the wider community to uphold the guiding principles of the Foundation and keep +the other Guardians honest. + +In other words; the Spec Core Team builds the spec, and the Guardians provide an +independent backstop to ensure the spec evolves in line with the Foundation's +mission. + +## Guiding Principles + +The guiding principles define the core philosophy of the project, and will be a +formal part of the final Articles of Association of the Matrix.org Foundation. + +### Matrix Manifesto + +We believe: + + * People should have full control over their own communication. + + * People should not be locked into centralised communication silos, but instead + be free to pick who they choose to host their communication without limiting + who they can reach. + + * The ability to converse securely and privately is a basic human right. + + * Communication should be available to everyone as a free and open, + unencumbered, standard and global network. + +### Mission + +The Matrix.org Foundation exists to act as a neutral custodian for Matrix and to +nurture it as efficiently as possible as a single unfragmented standard, for the +greater benefit of the whole ecosystem, not benefiting or privileging any single +player or subset of players. + +For clarity: the Matrix ecosystem is defined as anyone who uses the Matrix +protocol. This includes (non-exhaustively): + + * End-users of Matrix clients. + * Matrix client developers and testers. + * Spec developers. + * Server admins. + * Matrix packagers & maintainers. + * Companies building products or services on Matrix. + * Bridge developers. + * Bot developers. + * Widget developers. + * Server developers. + * Matrix room and community moderators. + * End-users who are using Matrix indirectly via bridges. + * External systems which are bridged into Matrix. + * Anyone using Matrix for data communications. + +"Greater benefit" is defined as maximising: + + * the number of Matrix-native end-users reachable on the open Matrix network. + * the number of regular users on the Matrix network (e.g. 30-day retained federated users). + * the number of online servers in the open federation. + * the number of developers building on Matrix. + * the number of independent implementations which use Matrix. + * the number of bridged end-users reachable on the open Matrix network. + * the signal-to-noise ratio of the content on the open Matrix network (i.e. minimising spam). + * the ability for users to discover content on their terms (empowering them to select what to see and what not to see). + * the quality and utility of the Matrix spec (as defined by ease and ability + with which a developer can implement spec-compliant clients, servers, bots, + bridges, and other integrations without needing to refer to any other + external material). + +N.B. that we consider success to be the growth of the open federated network +rather than closed deployments. For example, if WhatsApp adopted Matrix it +wouldn’t be a complete win unless they openly federated with the rest of the +Matrix network. + +### Values + +As Matrix evolves, it's critical that the Spec Core Team and Guardians are +aligned on the overall philosophy of the project, particularly in more +subjective areas. The values we follow are: + + * Supporting the whole long-term ecosystem rather than individual stakeholder gain. + * Openness rather than proprietary lock-in. + * Interoperability rather than fragmentation. + * Cross-platform rather than platform-specific. + * Collaboration rather than competition. + * Accessibility rather than elitism. + * Transparency rather than stealth. + * Empathy rather than contrariness. + * Pragmatism rather than perfection. + * Proof rather than conjecture. + +Patent encumbered IP is strictly prohibited from being added to the standard. + +Making the specification rely on non-standard/unspecified behaviour of other +systems or actors (such as SaaS services, even open-sourced, not governed by a +standard protocol) shall not be accepted, either. + +## The Spec Core Team + +The contents and direction of the Matrix Spec is governed by the Spec Core Team; +a set of experts from across the whole Matrix community, representing all +aspects of the Matrix ecosystem. The Spec Core Team acts as a subcommittee of +the Foundation. + +Members of the Spec Core Team pledge to act as a neutral custodian for Matrix on +behalf of the whole ecosystem and uphold the Guiding Principles of the project +as outlined above. In particular, they agree to drive the adoption of Matrix as +a single global federation, an open standard unencumbered from any proprietary +IP or software patents, minimising fragmentation (whilst encouraging +experimentation), evolving rapidly, and prioritising the long-term success and +growth of the overall network over individual commercial concerns. + +Spec Core Team members need to have significant proven domain experience/skill +and have had clear dedication and commitment to the project and community for >6 +months. (In future, once we have subteams a la Rust, folks need to have proven +themselves there first). + +Members need to demonstrate ability to work constructively with the rest of the +team; we want participation in the Spec Core Team to be an efficient, pleasant and +productive place, even in the face of inevitable disagreement. We do not want a +toxic culture of bullying or competitive infighting. Folks need to be able to +compromise; we are not building a culture of folks pushing their personal +agendas at the expense of the overall project. + +The team should be particularly vigilant against 'trojan horse' additions to the +spec - features which only benefit particular players, or are designed to +somehow cripple or fragment the open protocol and ecosystem in favour of +competitive advantage. Commercial players are of course free to build +proprietary implementations, or use custom event types, or even custom API +extensions (e.g. more efficient network transports) - but implementations must +fall back to interoperating correctly with the rest of the ecosystem. + +### Spec Core Team logistics + +The Spec Core Team itself will be made up of roughly 8 members + 1 project lead. +Roughly half the members are expected to be from the historical core team +(similar to Rust). The team must have 5 members to be able to function, with +the aim of generally having between 7 and 9 members. + +In future we may also have sub-teams (like Rust - e.g. CS/AS/Push API; SS API; +IS API; Crypto), but as a starting point we are beginning with a single core +team in the interests of not over-engineering it and scaling up elastically. + +Spec Core Team members need to be able to commit to at least 1 hour a week of +availability to work on the spec and (where relevant) reference implementations. +Members must arrange their own funding for their time. + +Responsibilities include: + + * Reviewing Matrix Spec Change proposals and Spec PRs. + + * Contributing to and reviewing reference implementations of Matrix Spec Change + proposals. + + * Shepherding Matrix Spec Changes on behalf of authors where needed. + + * Triaging Matrix Spec issues. + + * Coordinating reference implementations. + + * Ensuring the code of conduct for +matrix:matrix.org community rooms is + maintained and applied. + +If members are absent (uncontactable) for more than 8 weeks without prior +agreement, they will be assumed to have left the project. + +Spec Core Team members can resign whenever they want, but must notify the rest +of the team and the Guardians on doing so. + +New additions to the team must be approved by all current members of the team. +Membership has to be formally proposed by someone already on the Spec Core Team. + +Members can be removed from the team if 75% of the current members approves and +agrees they are no longer following the goals and guiding principles of the +project. (The 75% is measured of the whole team, including the member in +question). + +Guardians act as a safety net, and can appoint or remove Spec Core Team members +(requiring approval by 75% of the current Guardians) if the Spec Core Team is +unable to function or is failing to align with the Foundation's mission. + +It's suggested that one of the Spec Core Team members should also be a Guardian, +to facilitate information exchange between the Guardians and the Spec Core Team, +and to represent the technical angle of the project to the other Guardians. + +The project lead role acts to coordinate the team and to help steer the team to +consensus in the event of failing to get agreement on a Matrix Spec Change. +Every 12 months, a vote of confidence is held in the project lead, requiring the +approval of 75% of the current Spec Core Team members for the lead to be +renewed. There is no maximum term for the project lead. The lead may be +removed by the core team at any point (requiring 75% approval of current +members), and may resign the role at any point (notifying the team and the +Guardians). The lead automatically resigns the role if they resign from the +Spec Core Team. Resignation automatically triggers selection of a new lead, who +must be selected from the existing Spec Core Team with 75% approval from current +members within 14 days. + +It is vital that the core spec team has strong domain expertise covering all +different domains of the spec (e.g. we don't want to end up with a core spec +team where nobody has strong experience in cryptography) + +The initial Spec Core Team (and their domain areas) is: + + * Matthew Hodgson (Lead, Guardian) + * Erik Johnston (Servers) + * Richard van der Hoff (Servers, Cryptography) + * David Baker (Clients, IS API, Push API, Media) + * Hubert Chathi (Cryptography, General) + * Andrew Morgan (Servers, AS API, Spec Process) + * Travis Ralston (Bots and Bridges & AS API, Media, acting with Dimension hat on) + * Alexey Rusakov (Clients on behalf of Community) + * TBD + +MSCs require approval by 75% of the current members of the Spec Core Team to +proceed to Final Comment Period (see https://matrix.org/docs/spec/proposals for +the rest of the MSC process). + +Even though a threshold of only 75% is required for approval, the Spec Core Team +is expected to seek consensus on MSCs. + +The above governance process for the Spec Core Team is considered as part of the +spec and is updated using the Matrix Spec Change process. However, changes to +the governance process also require approval by 75% of the current Guardians +(acting as a formal decision of the Foundation's Directors), in order to ensure +changes are aligned with the Foundation's mission. For avoidance of doubt, Spec +Core Team votes and Guardians' votes are distinct and a person having both hats +has to vote independently on both forums with the respective hat on. + +Spec Core Team decisions (e.g. appointing/removing members and lead) +should be published openly and transparently for the public. + +## The Guardians + +*This section will be used as the basis for the legal responsibilities of +Directors in the Articles of Association of the Foundation.* + +The Guardians form the legal Board of Directors of The Matrix.org Foundation CIC +(Community Interest Company). They are responsible for ensuring the Foundation +is following its guiding principles, and provide a safety mechanism if the +structure of the Spec Core Team runs into trouble. + +In practice, this means that: + + * Guardians are responsible for ensuring the Spec Core Team continues to + function, and have the power to appoint/dismiss members of the spec core team + (with the agreement of 75% of the Guardians) to address issues with the Spec + Core Team. + * Guardians must keep each other honest, providing a ‘checks and balances’. + mechanism between each other to ensure that all Guardians and the Spec Core + Team act in the best interests of the protocol and ecosystem. + * Guardians may dismiss members of the Spec Core Team who are in serious + breach of the guiding principles. + * Guardians may appoint members of the Spec Core Team to break deadlocks in the + unanimous consent requirement for the Spec Core Team when appointing new + members. + * Guardians may also override deadlocks when appointing a Spec Core Team leader + (with approval of 75% of the current Guardians). + * Guardians must approve changes to the above Guiding Principles (with approval + of 75% of the current Guardians) + * Guardians are responsible for approving use of the Foundation's assets + (e.g. redistributing donations). + * In future, Guardians may also be responsible for ensuring staff are hired by + the Foundation to support administrative functions and other roles required + to facilitate the Foundation's mission. + * As well as the Spec Core Team committee, they may also oversee committees for + other areas such as marketing Matrix.org, registering custom event types, + or "Made for Matrix" certification. + * Guardians are responsible for choosing if, when and how staff are located by + the Foundation to fill administrative and other functions required to + facilitate the Foundations' mission. + * Guardians are responsible for choosing if and when additional committees are + formed, and to oversee those committees. + * Guardians are not required to be involved on a day-to-day basis, however + those not taking a hands on approach are required to monitor to ensure a + suitable balance is kept by those that do. + +Guardians are chosen typically to be independent of the commercial Matrix +ecosystem (and especially independent from New Vector), and may even not be +members of today’s Matrix community. However, they should be deeply aligned with +the mission of the project, and respected and trusted by the wider community to +uphold the guiding principles of the Foundation and keep the other Guardians +honest. + +Guardians are responsible for maintaining and updating the Guiding Principles +and Articles of Association of the Foundation if/when necessary. Changes to the +Guiding Principles require approval from 75% of the current Guardians and are +passed as a 'special resolution' of the board. + +New Guardians may be appointed with approval from 75% of the current Guardians. + +Guardians may resign at any time, with notification to the board. + +Guardians may be removed due to serious breach of the guiding principles with +approval by 75% of the other current Guardians, or if absent from 3 consecutive +board meetings, or if they are legally disqualified from acting as a Director. + +We aim to recruit roughly 5 Guardians. The initial Guardians are: + + * Matthew Hodgson (CEO/CTO, New Vector) + * Amandine Le Pape (COO, New Vector) + * TBA (agreed, needs paperwork) + * TBD + * TBD + +The intention is for Matthew & Amandine (the original founders of Matrix) to +form a minority of the Guardians, in order to ensure the neutrality of the +Foundation relative to Matthew & Amandine’s day jobs at New Vector. + +Guardians must arrange their own funding for their time. + +Guardian decisions (e.g. appointing/removing guardians; changes to the spec core +team; etc) should be published openly and transparently for the public. + +## The Code Core Team (aka The Core Team) + +The "Core Team" (or the "Code Core Team", to disambiguate from the Spec Core +Team) is a loose term that describes the set of people with access to commit +code to the public https://github.com/matrix-org repositories, who are either +working on matrix.org's reference implementations or the spec itself. Commit +access is decided by those responsible for the projects in question, much like +any other open source project. Anyone is eligible for commit access if they +have proved themselves a valuable long-term contributor, uphold the guiding +principles and mission of the project and have proved themselves able to +collaborate constructively with the existing core team. Active participation in +the core team is also signified by membership of the +matrix:matrix.org Matrix +community. + +Responsibilities include: + * Helping ensure the quality of the projects' code repositories. + * Ensuring all commits are reviewed. + * Ensuring that all projects follow the Matrix spec. + * Helping architect the implementations. + * Contributing code to the implementations. + * Fostering contributions and engaging with contributors constructively in a + way that fosters a healthy and happy community. + * Following the Guiding Principles and promoting them within the community. + +Code Core Team members must arrange their own funding for their time. + +## Functions of the Foundation + + * Independent legal entity which acts as neutral custodian of Matrix. + * Gathers donations. + * Owns the core Matrix IP in an asset lock, which shall be transferred from New Vector: + * Owns the matrix.org domain and branding. + * Owns the copyright of the reference implementations of Matrix (i.e. everything in https://github.com/matrix-org). + By assigning copyright to the Foundation, it’s protected against New Vector ever being tempted to relicense it. + * Owns the IP of the website. + * Owns the Matrix.org marketing swag (t-shirts, stickers, exhibition stands etc). + * Responsible for finding someone to run the Matrix.org homeserver (currently New Vector). + * Publishes the spec. + * Responsible for tools and documentation that support the spec. + * Responsible for ensuring reference implementations and test suite exists for the spec. + * Publishes the website (including ensuring This Week In Matrix and similar exist to promote independent projects). + * Manages any future IANA-style allocations for Matrix, such as: + * mx:// URI scheme. + * TCP port 8448. + * .well-known URIs +* Ensures that Matrix promotion is happening (e.g. ensuring that meetups & + events & community activity is supported). + +In future: + + * Contracts entities to work on Matrix if such contracts help the Foundation to + fulfil its mission and obey the Guiding Principles (e.g. redistributing + donations back to fund development of reference implementations or compliance + kits). + * Manages a "Made for Matrix" certification process? (to confirm that products + are actually compatible with Matrix). + +## Timings + +The Foundation was incorporated in October 2018 as a UK limited by guarantee +private company, using generic non-profit articles of association combined with +a high-level mission lock aligned with the above: + +> 4. The objects of the Foundation are for the benefit of the community as a whole +> to: + +> 4.1.1 empower users to control their communication data and have freedom over +> their communications infrastructure by creating, maintaining and promoting +> Matrix as an openly standardised secure decentralised communication protocol and +> network, open to all, and available to the public for no charge; + +> 4.1.2 build and develop an appropriate governance model for Matrix through the +> Foundation, in order to drive the adoption of Matrix as a single global +> federation, an open standard unencumbered from any proprietary intellectual +> property and/or software patents, minimising fragmentation (whilst encouraging +> experimentation), maximising speed of development, and prioritising the long- +> term success and growth of the overall network over the commercial concerns of +> an individual person or persons. + +The foundation was then converted into a Community Interest Company, formalising +its non-profit status under the approval of the independent [Community Interest +Companies Regulator](https://www.gov.uk/government/organisations/office-of-the-regulator-of-community-interest-companies), +which took effect Jan 2019. + +We are currently planning to release r0 of the Matrix Spec at the end of Jan 2019, and +finalise the Foundation's articles of association shortly afterwards based on the +contents of this MSC once passed FCP. + +This will coincide with the formal asset transfer of Matrix.org's assets from +New Vector Ltd, and the appointment of the remaining Guardians. diff --git a/proposals/1915-unbind-identity-server-param.md b/proposals/1915-unbind-identity-server-param.md index 6817ece3..5b7a1a50 100644 --- a/proposals/1915-unbind-identity-server-param.md +++ b/proposals/1915-unbind-identity-server-param.md @@ -27,7 +27,9 @@ 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. +server returns an error then that should be returned to the client. If the homeserver +is unable to determine an `id_server` to use, it should return `no-support` for +the `id_server_unbind_result`. Example: diff --git a/proposals/1930-tombstone-notifications.md b/proposals/1930-tombstone-notifications.md index 715921aa..253bf826 100644 --- a/proposals/1930-tombstone-notifications.md +++ b/proposals/1930-tombstone-notifications.md @@ -19,6 +19,11 @@ A new default override rule is to be added which is similar to `@room` notificat "kind": "event_match", "key": "type", "pattern": "m.room.tombstone" + }, + { + "kind": "event_match", + "key": "state_key", + "pattern": "" } ], "actions": [ diff --git a/proposals/1957-integrations-discovery.md b/proposals/1957-integrations-discovery.md new file mode 100644 index 00000000..85939726 --- /dev/null +++ b/proposals/1957-integrations-discovery.md @@ -0,0 +1,189 @@ +# MSC1957: Integration manager discovery + +**Note**: this proposal is part of a larger "Integrations API" which has not yet been defined. +See [MSC1956](https://github.com/matrix-org/matrix-doc/pull/1956) for details. + +**Note**: this proposal makes use of the existing Widget API proposed by +[MSC1236](https://github.com/matrix-org/matrix-doc/issues/1236). + +Users should have the freedom to choose which integration manager they want to use in their client, while +also accepting suggestions from their homeserver and client. Clients need to know where to find the different +integration managers and how to contact them. + + +## Proposal + +A single logged in user may be influenced by zero or more integration managers at any given time. Managers +are sourced from the client's own configuration, homeserver discovery information, and the user's personal +account data in the form of widgets. Clients should support users using more than one integration manager +at a given time, although the rules for how this can be handled are defined later in this proposal. + +#### Client-configured integration managers + +This is left as an implementation detail. In the case of Riot, this is likely to be part of the existing +`config.json` options, although likely modified to support multiple managers instead of one. + +#### Homeserver-configured integration managers + +The integration managers suggested by a homeserver are done through the existing +[.well-known](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-well-known-matrix-client) discovery +mechanism. The added optional fields, which should not affect a client's ability to log a user in, are: +```json +{ + "m.integrations": { + "managers": [ + { + "api_url": "https://integrations.example.org", + "ui_url": "https://integrations.example.org/ui" + }, + { + "api_url": "https://bots.example.org" + } + ] + } +} +``` + +As shown, the homeserver is able to suggest multiple integration managers through this method. Each manager +must have an `api_url` which must be an `http` or `https` URL. The `ui_url` is optional and if not provided +is the same as the `api_url`. Like the `api_url`, the `ui_url` must be `http` or `https` if supplied. + +The `ui_url` is ultimately treated the same as a widget, except that the `data` object from the widget is not +present and must not be templated here. Variables like `$matrix_display_name` are able to function, however. +Integration managers should never use the `$matrix_user_id` as authoritative and instead seek other ways to +determine the user ID. This is covered by other proposals. + +The `api_url` is the URL clients will use when *not* embedding the integration manager, and instead showing +its own purpose-built interface. + +Clients should query the `.well-known` information for the homeserver periodically to update the integration +manager settings for that homeserver. The client is not expected to validate or use any other information +contained in the response. Current recommendations are to query the configuration when the client starts up +and every 8 hours after that. Clients can additionally refresh the configuration whenever they feel is +necessary (such as every time the user opens the integration manager). + +#### User-configured integration managers + +Users can specify integration managers in the form of account widgets. The `type` is to be `m.integration_manager` +and the content would look something similar to: +```json +{ + "url": "https://integrations.example.org/ui?displayName=$matrix_display_name", + "data": { + "api_url": "https://integrations.example.org" + } +} +``` + +The `api_url` in the `data` object is required and has the same meaning as the homeserver-defined `api_url`. +The `url` of the widget is analogous to the `ui_url` from the homeserver configuration above, however normal +widget rules apply here. + +The user is able to have multiple integration managers through use of multiple widgets. + +The query string shown in the example is to demonstrate that integration managers are widgets and can +make use of the template options provided to widgets. + +#### Display order of integration managers + +Clients which have support for integration managers should display at least 1 manager, but should +display multiple via something like tabs. Clients must prefer to display the user's configured +integration managers over any defaults, and if only displaying one manager must pick the first +manager after sorting the `state_key`s of the applicable widgets in lexicographical order. Clients +can additionally display default managers if they so wish, and should preserve the order defined in +the various defaults. If the user has no configured integration managers, the client must prefer +to display one or more of the managers suggested by the homeserver over the managers recommended +by the client. + +The client can optionally support a way to entirely disable integration manager support, even if the +user and homeserver have managers defined. + +The rationale for having the client prefer to use the user's integration managers first is so that +the user can tailor their experience within Matrix if desired. Similarly, a homeserver may wish to +subject all of their users to the same common integration manager as would be common in some organizations. +The client's own preference is a last ditch effort to have an integration manager available to the +user so they don't get left out. + +#### Displaying integration managers + +Clients simply open the `ui_url` (or equivalent) in an `iframe` or similar. In the current ecosystem, +integration managers would receive a `scalar_token` to identify the user - this is no longer the case +and instead integration managers must seek other avenues for determining the user ID. Other proposals +cover how to do this in the context of the integrations API. + +Integration managers shown in this way must be treated like widgets, regardless of source. In practice +this means exposing the Widget API to the manager and applying necessary scoping to keep the manager +as an account widget rather than a room widget. + +#### Discovering a manager by only the domain name + +Clients may wish to ask users for a single canonical domain name so they can find the manager to add +to the user's account transparently. This differs from the .well-known discovery which allows homeservers +to recommend their own integration manager: the homeserver is not recommending a default here. The +user has instead opted to pick an integration manager (identified only by domain name) and the client +is expected to resolve that to a set of URLs it can use for the manager. + +Similar to the .well-known discovery done by servers (and clients during login), clients which have an +integrations domain (eg: "example.org") make a regular HTTPS request to +`https://example.org/.well-known/matrix/integrations` which returns an object which looks like the +following: +```json +{ + "m.integrations_widget": { + "url": "https://integrations.example.org/ui?displayName=$matrix_display_name", + "data": { + "api_url": "https://integrations.example.org" + } + } +} +``` + +The response should be parsed as JSON. If the endpoint returns an error or is missing the `m.integrations_widget` +property, the client should assume there is no integrations manager running on that domain. The +`m.integrations_widget` is an object which has the exact same format as the account widget for +an integration manager, described above. The client should wrap the object verbatim into the appropriate +account data location. + +Because the .well-known file would be accessed by web browsers, among other platforms, the server +should be using appropriate CORS headers for the request. The recommended headers are the same as those +which are already recommended for homeserver discovery in the Client-Server API. + +*Note*: this could reuse the client-server mechanic for discovery and just omit the homeserver information +however that conflates many concerns together on the one endpoint. A new endpoint is instead proposed +to keep the concerns isolated. + +The query string shown in the example is to demonstrate that integration managers are widgets and can +make use of the template options provided to widgets. + +## Tradeoffs + +We could limit the user (and by extension, the homeserver and client) to exactly 1 integration manager +and not worry about tabs or other concepts, however this restricts a user's access to integrations. +In a scenario where the user wants to use widgets from Service A and bots from Service B, they'd +end up switching accounts or clients to gain access to either service, or potentially just give up +and walk away from the problem. Instead of having the user switch between clients, we might as well +support this use case, even if it is moderately rare. + +We could also define the integration managers in a custom account data event rather than defining them +as a widget. Doing so just adds clutter to the account data and risks duplicating code in clients as +using widgets gets us URL templating for free (see the section earlier on in this proposal about account +widgets for more information: "User-configured integration managers"). + + +## Future extensions + +Some things which may be desirable in the future are: +* Avatars for the different managers +* Human-readable names for the different managers +* Supporting `ui_url`s targeting specific clients for a more consistent design + + +## Security considerations + +When displaying integration managers, clients should not trust that the input is sanitary. Per the +proposal above, an integration manager is only permitted to be served from HTTP(S) URIs. A given +integration manager can still have malicious intent however, and clients should ensure any sandboxing +on the manager is appropriate such that it can communicate with the client, but cannot perform +unauthorized actions. Other URI schemes are just as dangerous and could potentially be allowed by +this proposal - use cases are less defined and desirable for schemes like `file://` and are excluded +by this proposal. They can be added in a future proposal if a use case arises. diff --git a/proposals/1961-integrations-auth.md b/proposals/1961-integrations-auth.md new file mode 100644 index 00000000..1f868e4c --- /dev/null +++ b/proposals/1961-integrations-auth.md @@ -0,0 +1,73 @@ +# MSC1961: Integration manager authentication + +A set of common APIs needs to be defined for clients to be able to interact with an integration +manager. This proposal covers the authentication portion of that API. + +**Note**: this proposal is part of a larger "Integrations API" which has not yet been defined. +See [MSC1956](https://github.com/matrix-org/matrix-doc/pull/1956) for details. + + +## Proposal + +All specified APIs (except `/register`) will take an `Authorization` header with a `Bearer` token returned +from a call to `/register`. This token is used to authorize the request and to identify who is making the +request. The token may also be specified as the `access_token` query string parameter, similar to the +Client-Server API. + +#### POST `/_matrix/integrations/v1/account/register` + +Exchanges an OpenID object for a token which can be used to authorize future requests to the manager. + +Request body is an OpenID object as returned by `/_matrix/client/r0/user/:userId/openid/request_token`. + +Response is: +```json +{ + "token": "OpaqueString" +} +``` + +The token should consist of URL-safe base64 characters. Integration managers should be careful to validate +the OpenID object by ensuring the `/_matrix/federation/v1/openid/userinfo` response has a `sub` which belongs +to the `matrix_server_name` provided in the original OpenID object. + +Applications which register for a token are responsible for tracking which integration manager they are for. +This can usually be done by tracking the hostname of the integration manager and matching a token with it. + +#### GET `/_matrix/integrations/v1/account` + +Gets information about the token's owner, such as the user ID for which it belongs. + +Besides a token, no other information is required for the request. + +Response is: +```json +{ + "user_id": "@alice:example.org" +} +``` + +The `user_id` is the user ID which was represented in the OpenID object provided to `/register`. Integration +managers may be interested in also supplying information about the user's credit balance for paid integrations +here. Preferably, custom information is stored under a namespaced key like so: +```json +{ + "user_id": "@alice:example.org", + "org.example.paid.integrations": { + "credit": 20000 + } +} +``` + +#### POST `/_matrix/integrations/v1/account/logout` + +Logs the token out, rendering it useless for future requests. + +Request body is an empty object. Response body is also an empty object if successful. + + +## Security considerations + +Clients should be sure to call `/logout` where possible when the user is logging out or no longer needs access +to a given manager. Clients should additionally be cautious about which managers they register for tokens with, +as some integration managers may be untrusted. diff --git a/proposals/2002-rooms-v4.md b/proposals/2002-rooms-v4.md new file mode 100644 index 00000000..defacee1 --- /dev/null +++ b/proposals/2002-rooms-v4.md @@ -0,0 +1,54 @@ +# MSC 2002 - Rooms V4 + +This MSC proposes creating a new room version named v4 to allow servers to switch +event ID grammar to that proposed in MSC1884. + +## Proposal + +The new room version is called "4". The only difference between v4 and v3 is +that v4 rooms use the grammar for defining event IDs as proposed in MSC1884 - +expressing event IDs as url-safe base64 rather than plain base64 for the +convenience of client implementors. + +It is not proposed that servers change the default room version used when +creating new rooms, and it is not proposed that servers recommend upgrading +existing rooms to v4. + +## Rationale and Context + +We would like to default to creating rooms with a reasonably secure room +algorithm in upcoming Matrix 1.0. We do not want to default to creating v3 +rooms due to the inconvenience the v3 event ID grammar might cause, so instead +we are proposing favouring v4. + +Ideally we would also include other room algorithm changes in v4 (e.g. +honouring server signing key validity periods, as per +https://github.com/matrix-org/synapse/issues/4364), but as spec & +implementation work is still ongoing there, we are proposing using v4 as a +room version which can be supported in the wild before Matrix 1.0 and then +used as the initial default for creating rooms. The expectation is for the +versions of the spec which coincide with 1.0 to also support v5 rooms, but in +practice v5 will not be marked as default until it has sufficient adoption on +the public network. + +The expectation is never to recommend upgrading existing +rooms to v4, but instead v5 (once ready), to avoid overburdening room admins +with upgrade notifications. + +To conclude, the proposed plan is: + 1. Define room v4 as MSC1884 + 2. Introduce servers with v4 support into the public network as rapidly as possible + 3. Wait for enough servers to speak v4. Meanwhile: + 1. Define an MSC for honouring server signing key validity periods + 2. Implement this MSC + 3. Define room v5 as this MSC + 4. Release Matrix 1.0, defining room v4 as the new default for creating rooms, + but also shipping support for room v5 for the first time. + 5. Wait for enough servers to speak v5 rooms. + 6. Define room v5 as the new default for creating rooms. + 7. Define room versions prior to v5 as unsafe, thus prompting users to upgrade their + rooms to v5. + +The reason we don't wait for v5 to be widespread before releasing 1.0 is to avoid +delaying the 1.0 yet further. It is good enough for 1.0 to support v5 without it +also being the default for creating rooms. diff --git a/proposals/2010-spoilers.md b/proposals/2010-spoilers.md new file mode 100644 index 00000000..1d9f4dc9 --- /dev/null +++ b/proposals/2010-spoilers.md @@ -0,0 +1,74 @@ +# MSC 2010: Proposal to add client-side spoilers +Sometimes, while you want to put text into a spoiler to not have people accidentally read things that they don't want to see. + +For example, when discussing a new movie or a TV series, not everyone might have watched it yet. +In such cases it would make sense to add a spoiler so that only those who have seen the movie or +don't mind spoilers read the content. +Another example would be e.g. in mental health communities where certain people have certain +triggers. People could put talking about abuse or the like into a spoiler, to not accidentally +trigger anyone just reading along the conversation. +Furthermore this is helpful for bridging to other networks that already have a spoiler feature. + +To render the spoiler the content is hidden and then revealed once interacted somehow +(e.g. a click / hover). + +## Proposal +This proposal is about adding a new attribute to the `formatted_body` of messages with type +`m.room.message` and msgtype `m.text`. + +It adds a new attribute, `data-mx-spoiler`, to the `` tag. If the attribute is present the +contents of the span tag should be rendered as a spoiler. Optionally, you can specify a reason for +the spoiler by setting the attribute string. It could be rendered, for example, similar to this: + +![Spoiler rendering idea](images/2010-spoiler-example.gif) + +To preserve the semantics of a spoiler in the plaintext fallback it is recommended to upload the contents of the spoiler +as a text file and then link this: `[Spoiler](mxc://someserver/somefile)` and +`[Spoiler for reason](mxc://someserver/somefile)` respectively. + +### Example +Without reason: +``` +{ + "msgtype": "m.text", + "format": "org.matrix.custom.html", + "body": "Hello there, the movie was [spoiler](mxc://someserver/somefile)", + "formatted_body": "Hello there, the movie was awesome" +} +``` +With reason: +``` +{ + "msgtype": "m.text", + "format": "org.matrix.custom.html", + "body": "Hey [Spoiler for movie](mxc://someserver/somefile)", + "formatted_body": "Hey the movie was awesome" +} +``` + +## Tradeoffs +Instead of making this an attribute, an entirely new tag could be introduced (e.g. ``), +however that wouldn't be HTML-compliant. + +Instead of limiting the proposed `data-mx-spoiler` attribute only to the ``-tag it could be +added to all tags, however it might make implementations for clients more complicated. + +Alternatively the [details](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) tag could +be used. This, however, is a block element, and the spoilers are span elements. Furthermore +semantically there is a slight difference: with the details tag you hide something for a person +as it uses up a lot of screen space, while with a spoiler you hide something as a person might not +want to see it. + +## Potential issues +Depending on context it might make sense to put other events, such as `m.image`, into spoilers, +too. This MSC doesn't address that at all. Using +`` seems rather sub-optimal for that. + +This MSC doesn't take HTML block elements into account. + +Clients would have to come up with a way to input spoilers. This could be done, for example, +by adding a custom markdown tag (like discord does), so that you do `Text ||spoiler||`, however +that doesn't take a spoiler reason into account. + +## Security considerations +The spoiler reason needs to be properly escaped when rendered. diff --git a/proposals/2076-enforce-validity-periods.md b/proposals/2076-enforce-validity-periods.md new file mode 100644 index 00000000..b09aa2f4 --- /dev/null +++ b/proposals/2076-enforce-validity-periods.md @@ -0,0 +1,42 @@ +# MSC2076: Enforce key-validity periods when validating event signatures + +## Background + +The [Federation API +specification](https://matrix.org/docs/spec/server_server/r0.1.1.html#validating-hashes-and-signatures-on-received-events) +specifies that events should be validated via the signature verification +algorithm, but does not specify how the keys for that check should be obtained +and validated. + +In practice, the implementation has been as follows. The receiving server +first requests a copy of the key via the [`GET /_matrix/key/v2/server/` +API](https://matrix.org/docs/spec/server_server/r0.1.1.html#get-matrix-key-v2-server-keyid) +directly from the server which created the signature, or via the [`POST +/_matrix/key/v2/query` API](https://matrix.org/docs/spec/server_server/r0.1.1.html#post-matrix-key-v2-query) +from a trusted key server. Once such a key is obtained, it is then cached +forever. No check is made on the `valid_until_ts` field, and +`minimum_valid_until_ts` is set to zero for calls to `POST +/_matrix/key/v2/query`. + +This is highly unsatisfactory, as it means that, should a key be compromised, +then an attacker can spoof arbitrary events claiming to be from the compromised +server forever, since there is no revocation mechanism. + +## Proposal + +This MSC proposes to enforce the `valid_until_ts` property when validating +event signatures. In particular, the server must ensure that it has a copy of +the key with a `valid_until_ts` at least as large as the `origin_server_ts` of +the event being validated. If it does not have such a copy, it must try to +obtain one via the `GET /_matrix/key/v2/server/` or `POST +/_matrix/key/v2/query` APIs. For the latter, it must set +`minimum_valid_until_ts` to prompt the notary server to attempt to refresh the +key if appropriate. + +Since this changes the rules used to validate events, it will be introduced +with a new room version. This will reduce the risk of divergence between +servers in a room due to some servers accepting events which others reject. + +This MSC also proposes that the current situation - where `valid_until_ts` is +ignored - be formalised for the existing room versions v1-v4, rather than be +left as implementation-specific behaviour. diff --git a/proposals/2077-rooms-v5.md b/proposals/2077-rooms-v5.md new file mode 100644 index 00000000..9f8c1654 --- /dev/null +++ b/proposals/2077-rooms-v5.md @@ -0,0 +1,19 @@ +# MSC2077 - Room version 5 + +This MSC proposes creating room version 5, which will enforce the signing key +`valid_until_ts` timestamps proposed in +[MSC2076](https://github.com/matrix-org/matrix-doc/issues/2076). + +## Proposal + +The new room version is called `5`. The only difference between v5 and v4 is +that v5 rooms enforce the `valid_until_ts` timestamp on signing keys as +proposed in [MSC2076](https://github.com/matrix-org/matrix-doc/issues/2076). + +It is not yet proposed to change the default room version to v5. Version 5 will +be considered a "stable" version. + +## Notes + +See also [MSC2002](./2002-rooms-v4.md), which proposed room v4 but also +mentioned that a v5 was anticipated and gave some context for this change. diff --git a/proposals/2078-homeserver-password-resets.md b/proposals/2078-homeserver-password-resets.md new file mode 100644 index 00000000..8dd7d9c9 --- /dev/null +++ b/proposals/2078-homeserver-password-resets.md @@ -0,0 +1,154 @@ +# MSC2078 - Sending Third-Party Request Tokens via the Homeserver + +This MSC proposes removing the current requirement of the identity server to +send third-party request tokens, and allows homeservers to implement the +functionality instead. These request tokens are used to verify the identity of +the request author as an owner of the third-party ID (3PID). This can be used +for binding a 3PID to an account, or for resetting passwords via email or SMS. +The latter is what this proposal mainly focuses on, but be aware that it allows +for any task that requires requesting a token for a 3PID to be taken on by the +homeserver instead of the identity server. + +The intention is to put less trust in the identity server, which is currently +one of the most centralised components of Matrix. As it stands, an attacker in +control of a identity server can reset a user's password if the identity server +is considered trusted by that homeserver, and the user has registered at least +one 3PID. This is due to the identity server handling the job of confirming the +user's control of that identity. + +The MSC seeks to clarify that homeservers can take on the responsibility of +sending password reset tokens themselves, and a new response field that will +aid homeservers in doing so. + +# Background + +Currently when a client requests a 3PID token, it makes a call to one of the +`/requestToken` endpoints on the homeserver. For instance, during password +resets, a token is requested from either +[/_matrix/client/r0/account/password/email/requestToken](https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-account-password-email-requesttoken) +or +[/_matrix/client/r0/account/password/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-account-password-msisdn-requesttoken), +depending on the medium of the 3PID. These requests are supplied all the +necessary details as well as an `id_server` field containing the domain address +of a identity server trusted by the homeserver. + +In order to facilitate these requests, the homeserver will simply proxy them to +the identity server. The IS will send out a token via email or sms, the user +will click a link or enter the token into their client, and either the client +or the user's browser will make a request **directly to the identity server** +with the token for verification. The IS then informs the homeserver that +verification was successful. At this point you can likely see that there is +potential for abuse here, so instead Homeservers should be given the option to +stop proxying the request to the identity server, and instead just send and +validate the token themselves. + +## Proposal + +The homeserver should be allowed to either proxy `/requestToken` requests or +handle them itself. Specifically, this means that the homeserver can both send +password reset tokens (via email or SMS), as well as accept requests on an +arbitrary endpoint (with the same parameters as +[/_matrix/identity/api/v1/validate/email/submitToken](https://matrix.org/docs/spec/identity_service/r0.1.0.html#post-matrix-identity-api-v1-validate-email-submittoken)) +to verify that token. + +One additional complication that in the case of SMS, just a code is sent to a +person's phone. This is then given to the client, but the client may not know +where to send the code now, as it doesn't know whether the homeserver or +identity server generated it. + +In order to combat this problem, the field `submit_url` MUST be added in the +response from all of the variants of `/requestToken` in the Client-Server API, +if and only if the verification message contains a code the user is expected to +enter into the client (for instance in the case of a short code through SMS). +This URL is simply where the client should submit this token. The endpoint +should accept the same parameters as +[/_matrix/identity/api/v1/validate/{3pid_type}/submitToken](https://matrix.org/docs/spec/identity_service/r0.1.0.html#post-matrix-identity-api-v1-validate-email-submittoken) +in the Identity Service API. The only recommendation to homeserver developers +for this endpoint's path is to not be exactly the same as that of the identity +server, in order to prevent clashes between setups running both an identity +server and homeserver on the same domain. If `submit_url` is omitted, the +client MUST continue the same behaviour from before, which is to send the token +to the identity server directly. This is intended for backwards compatibility +with older servers. + +If the client receives a response to `/requestToken` with `submit_url`, it MUST +accept a token from user input, then make a POST request to the content of +`submit_url` with the `sid`, `client_secret` and user-entered token. +`submit_url` can lead to anywhere the homeserver deems necessary for +verification. To be clear the content of `id_server` does not matter here, the +client should just submit a POST request to the value of `submit_url`. Additionally +data MUST be submitted as a JSON body. + +An example exchange from the client's perspective is shown below: + +``` +POST https://homeserver.tld/_matrix/client/r0/account/password/email/requestToken + +{ + "client_secret": "monkeys_are_AWESOME", + "email": "alice@homeserver.tld", + "send_attempt": 1, + "id_server": "id.example.com" +} +``` + +If the server responds with a `submit_url` field, it means the client should +collect a token from the user and then submit it to the provided URL. + +``` +{ + "sid": "123abc", + "submit_url": "https://homeserver.tld/_homeserver/password_reset/msisdn/submitToken" +} +``` + +Since a `submit_url` was provided, the client will now collect a token from the +user, say "123456", and then submit that as a POST request to the +`"submit_url"`. + +``` +POST https://homeserver.tld/_homeserver/password_reset/msisdn/submitToken + +{ + "sid": "123abc", + "client_secret": "monkeys_are_AWESOME", + "token": "123456" +} +``` + +The client will then receive an appropriate response: + +``` +{ + "success": true +} +``` + +If the client did not receive a `submit_url` field, they should instead assume +that verification will be completed out of band (e.g. the user clicks a link in +their email and makes the submitToken request with their web browser). + +## Dismissed Alternatives + +Consideration was taken not to make `id_server` an optional field. Let's +assume for a moment that it was optional. Now, a client could send a request to +`/requestToken` omitting the `id_server` field. The homeserver however has +opted to continue proxying `/requestToken` to the identity server, even though +it knows this is potentially insecure. The homeserver now has no idea which +identity server to proxy the request to, and must return a failure to the +client. The client could then make another request with an `id_server`, but +we've now made two requests that ended up in the same outcome, instead of one, +in hopes of saving a very small amount of bandwidth by omitting the field +originally. + +At some point we should look into removing the `id_server` field altogether and +removing any email/SMS message sending from the identity server. This would +drastically reduce the amount of trust needed in the identity server and its +required ability. This is, however, a good first step. + +## Tradeoffs + +If homeservers choose to not proxy the request, they will need to implement the +ability to send emails and/or SMS messages. This is left as a detail for the +homeserver implementation. + diff --git a/proposals/2134-identity-hash-lookup.md b/proposals/2134-identity-hash-lookup.md new file mode 100644 index 00000000..2fdda034 --- /dev/null +++ b/proposals/2134-identity-hash-lookup.md @@ -0,0 +1,454 @@ +# MSC2134: Identity Hash Lookups + +[Issue #2130](https://github.com/matrix-org/matrix-doc/issues/2130) has been +created in response to a security issue brought up by an independent party. +To summarise the issue, when a user wants to ask an identity server which of +its contacts have registered a Matrix account, it performs a lookup against +an identity server. The client currently sends all of its contact details in +the form of plain-text addresses, meaning that the identity server can +identify and record every third-party ID (3PID) of the user's contacts. This +allows the identity server to collect email addresses and phone numbers that +have a high probability of being connected to a real person. This data could +then be used for marketing, political campaigns, etc. + +However, if these email addresses and phone numbers are hashed before they are +sent to the identity server, the server would have a more difficult time of +being able to recover the original addresses. This prevents contact +information of non-Matrix users being exposed to the lookup service. + +Yet, hashing is not perfect. While reversing a hash is not possible, it is +possible to build a [rainbow +table](https://en.wikipedia.org/wiki/Rainbow_table), which maps known email +addresses and phone numbers to their hash equivalents. When the identity +server receives a hash, it is then be able to look it up in its rainbow table +and find the corresponding 3PID. To prevent this, one would use a hashing +algorithm such as [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) with many +rounds, making the construction of a large rainbow table an infeasibly +expensive process. Unfortunately, this is impractical for our use case, as it +would require clients to also perform many, many rounds of hashing, linearly +dependent on the size of their address book, which would likely result in +lower-end mobile phones becoming overwhelmed. We are then forced to use a +fast hashing algorithm, at the cost of making rainbow tables easy to build. + +The rainbow table attack is not perfect, because one does need to know email +addresses and phone numbers to build it. While there are only so many +possible phone numbers, and thus it is relatively inexpensive to generate the +hash value for each one, the address space of email addresses is much, much +wider. If your email address does not use a common mail server, is decently long +or is not publicly known to attackers, it is unlikely that it would be +included in a rainbow table. + +Thus the approach of hashing, while adding complexity to implementation and +resource consumption of the client and identity server, does provide added +difficulty for the identity server to carry out contact detail harvesting, +which should be considered worthwhile. + +## Proposal + +This proposal suggests making changes to the Identity Service API's lookup +endpoints, consolidating them into a single `/lookup` endpoint. The endpoint +is to be on a `v2` path, to avoid confusion with the original `v1` `/lookup`. +The `/api` part is also dropped in order to preserve consistency across other +endpoints: + +- `/_matrix/identity/v2/lookup` + +A second endpoint is added for clients to request information about the form +the server expects hashes in. + +- `/_matrix/identity/v2/hash_details` + +The following back-and-forth occurs between the client and server. + +Let's say the client wants to check the following 3PIDs: + +``` +alice@example.com +bob@example.com +carl@example.com ++1 234 567 8910 +denny@example.com +``` + +The client will hash each 3PID as a concatenation of the medium and address, +separated by a space and a pepper, also separated by a space, appended to the +end. Note that phone numbers should be formatted as defined by +https://matrix.org/docs/spec/appendices#pstn-phone-numbers, before being +hashed). Note that "pepper" in this proposal simply refers to a public, +opaque string that is used to produce different hash results between identity +servers. Its value is not secret. + +First the client must append the medium (plus a space) to the address: + +``` +"alice@example.com" -> "alice@example.com email" +"bob@example.com" -> "bob@example.com email" +"carl@example.com" -> "carl@example.com email" +"+1 234 567 8910" -> "12345678910 msisdn" +"denny@example.com" -> "denny@example.com email" +``` + +Hashes must be peppered in order to reduce both the information an identity +server gains during the process, and attacks the client can perform. [0] + +In order for clients to know the pepper and hashing algorithm they should use, +identity servers must make the information available on the `/hash_details` +endpoint: + +``` +GET /_matrix/identity/v2/hash_details + +{ + "lookup_pepper": "matrixrocks", + "algorithms": ["sha256"] +} +``` + +The name `lookup_pepper` was chosen in order to account for pepper values +being returned for other endpoints in the future. The contents of +`lookup_pepper` MUST match the regular expression `[a-zA-Z0-9]+`, whether +hashing is being performed or not. When no hashing is occuring, a valid +pepper value of at least length 1 is still required. + +If hashing, the client appends the pepper to the end of the 3PID string, +after a space. + +``` +"alice@example.com email" -> "alice@example.com email matrixrocks" +"bob@example.com email" -> "bob@example.com email matrixrocks" +"carl@example.com email" -> "carl@example.com email matrixrocks" +"12345678910 msdisn" -> "12345678910 msisdn matrixrocks" +"denny@example.com email" -> "denny@example.com email matrixrocks" +``` + +Clients can cache the result of this endpoint, but should re-request it +during an error on `/lookup`, to handle identity servers which may rotate +their pepper values frequently. Clients MUST choose one of the given +`algorithms` values to hash the 3PID during lookup. + +Clients and identity servers MUST support SHA-256 as defined by [RFC +4634](https://tools.ietf.org/html/rfc4634), identified by the value +`"sha256"` in the `algorithms` array. SHA-256 was chosen as it is currently +used throughout the Matrix spec, as well as its properties of being quick to +hash. + +There are certain situations when an identity server cannot be expected to +compare hashed 3PID values; for example, when a server is connected to a +backend provider such as LDAP, it is not efficient for the identity server to +pull all of the addresses and hash them upon lookup. For this case, identity +servers can also support receiving plain-text 3PID addresses from clients. To +agree upon this, the value `"none"` can be added to the `"algorithms"` array +of `GET /hash_details`. The client can then choose to send plain-text values +by setting the `"algorithm"` value in `POST /lookup` to `"none"`. + +No hashing nor peppering will be performed if the client and server decide on +`"none"`, and 3PIDs will be sent in plain-text, similar to the v1 `/lookup` +API. When this occurs, it is STRONGLY RECOMMENDED for the client to prompt +the user before continuing. + +When performing a lookup, the pepper and hashing algorithm the client used +must be part of the request body (even when using the `"none"` algorithm +value). If they do not match what the server has on file (which may be the +case if the pepper was changed right after the client's request for it), then +the server must inform the client that they need to query the hash details +again, as opposed to just returning an empty response, which clients would +assume to mean that no contacts are registered on that identity server. + +If the algorithm is not supported by the server, the server should return a `400 +M_INVALID_PARAM`. If the pepper does not match the server's, the server should +return a new error code, `400 M_INVALID_PEPPER`. A new error code is not +defined for an unsupported algorithm as that is considered a client bug. + +The `M_INVALID_PEPPER` error response contains the correct `algorithm` and +`lookup_pepper` fields. This is to prevent the client from needing to query +`/hash_details` again, thus saving a request. `M_INVALID_PARAM` does not +include these fields. An example response to an incorrect pepper would be: + +``` +{ + "error": "Incorrect value for lookup_pepper", + "errcode": "M_INVALID_PEPPER", + "algorithm": "sha256", + "lookup_pepper": "matrixrocks" +} +``` + +Now comes time for the lookup. We'll first cover an example of the client +choosing the `"sha256"` algorithm. Note that the resulting hash digest MUST +be encoded in URL-safe unpadded base64 (similar to [room version 4's event +IDs](https://matrix.org/docs/spec/rooms/v4#event-ids)). Once hashing has been +performed, the client sends each hash in an array. + +``` +"alice@example.com email matrixrocks" -> "4kenr7N9drpCJ4AfalmlGQVsOn3o2RHjkADUpXJWZUc" +"bob@example.com email matrixrocks" -> "LJwSazmv46n0hlMlsb_iYxI0_HXEqy_yj6Jm636cdT8" +"carl@example.com email matrixrocks" -> "jDh2YLwYJg3vg9pEn3kaaXAP9jx-LlcotoH51Zgb9MA" +"12345678910 msisdn matrixrocks" -> "S11EvvwnUWBDZtI4MTRKgVuiRx76Z9HnkbyRlWkBqJs" +"denny@example.com email matrixrocks" -> "2tZto1arl2fUYtF6tQPJND69il3xke9OBlgFgnUt2ww" + +POST /_matrix/identity/v2/lookup + +{ + "addresses": [ + "4kenr7N9drpCJ4AfalmlGQVsOn3o2RHjkADUpXJWZUc", + "LJwSazmv46n0hlMlsb_iYxI0_HXEqy_yj6Jm636cdT8", + "jDh2YLwYJg3vg9pEn3kaaXAP9jx-LlcotoH51Zgb9MA", + "S11EvvwnUWBDZtI4MTRKgVuiRx76Z9HnkbyRlWkBqJs", + "2tZto1arl2fUYtF6tQPJND69il3xke9OBlgFgnUt2ww" + ], + "algorithm": "sha256", + "pepper": "matrixrocks" +} +``` + +The identity server, upon receiving these hashes, can simply compare against +the hashes of the 3PIDs it stores. The server then responds with the Matrix +IDs of those that match: + +``` +{ + "mappings": { + "4kenr7N9drpCJ4AfalmlGQVsOn3o2RHjkADUpXJWZUc": "@alice:example.com", + "S11EvvwnUWBDZtI4MTRKgVuiRx76Z9HnkbyRlWkBqJs": "@fred:example.com" + } +} +``` + +The client can now display which 3PIDs link to which Matrix IDs. + +For the case of the identity server sending, and the client choosing, +`"none"` as the algorithm, we would do the following. + +The client would first make `GET` a request to `/hash_details`, perhaps +receiving the response: + +``` +{ + "lookup_pepper": "matrixrocks", + "algorithms": ["none", "sha256"] +} +``` + +The client decides that it would like to use `"none"`, and thus ignores the +lookup pepper, as no hashing will occur. Appending a space and the 3PID +medium to each address is still necessary: + +``` +"alice@example.com" -> "alice@example.com email" +"bob@example.com" -> "bob@example.com email" +"carl@example.com" -> "carl@example.com email" +"+1 234 567 8910" -> "12345678910 msisdn" +"denny@example.com" -> "denny@example.com email" +``` + +The client then sends these off to the identity server in a `POST` request to +`/lookup`: + +``` +POST /_matrix/identity/v2/lookup + +{ + "addresses": [ + "alice@example.com email", + "bob@example.com email", + "carl@example.com email", + "12345678910 msisdn", + "denny@example.com email" + ], + "algorithm": "none", + "pepper": "matrixrocks" +} +``` + +Note that even though we haven't used the `lookup_pepper` value, we still +include the same one sent to us by the identity server in `/hash_details`. +The identity server should still return `400 M_INVALID_PEPPER` if the pepper +is incorrect. This simplifies things and can help ensure the client is +requesting `/hash_details` properly before each lookup request. + +Finally, the identity server will check its database for the Matrix user IDs +it has that correspond to these 3PID addresses, and returns them: + +``` +{ + "mappings": { + "alice@example.com email": "@alice:example.com", + "12345678910 msisdn": "@fred:example.com" + } +} +``` + +No parameter changes will be made to +[/bind](https://matrix.org/docs/spec/identity_service/r0.2.1#post-matrix-identity-api-v1-3pid-bind) +as part of this proposal. + +## Fallback considerations + +`v1` versions of these endpoints may be disabled at the discretion of the +implementation, and should return a `403 M_FORBIDDEN` error if so. + +If an identity server is too old and a HTTP 400 or 404 is received when +accessing the `v2` endpoint, clients should fallback to the `v1` endpoint +instead. However, clients should be aware that plain-text 3PIDs are required +for the `v1` endpoints, and are strongly encouraged to warn the user of this. + +## Tradeoffs + +* There is a small cost incurred by performing hashes before requests, but this + is outweighed by the privacy implications of sending plain-text addresses. + +## Security Considerations + +Hashes are still reversible with a rainbow table, but the provided pepper, +which can be rotated by identity servers at will, should help mitigate this. +Phone numbers (with their relatively short possible address space of 12 +numbers), short email addresses at popular domains, and addresses of both +types that have been leaked in database dumps are more susceptible to hash +reversal. + +Mediums and peppers are appended to the address as to prevent a common prefix +for each plain-text string, which prevents attackers from pre-computing the +internal state of the hash function. + +## Other considered solutions + +Bloom filters are an alternative method of providing private contact discovery. +However, they do not scale well due to requiring clients to download a large +filter that needs updating every time a new bind is made. + +Further considered solutions are explored in +https://signal.org/blog/contact-discovery/. Signal's eventual solution of +using Software Guard Extensions (detailed in +https://signal.org/blog/private-contact-discovery/) is considered impractical +for a federated network, as it requires specialised hardware. + +k-anonymity was considered as an alternative approach, in which the identity +server would never receive a full hash of a 3PID that it did not already know +about. Discussion and a walk-through of what a client/identity-server +interaction would look like are documented [in this Github +comment](https://github.com/matrix-org/matrix-doc/pull/2134#discussion_r298691748). + +While this solution seems like a win for privacy, its actual benefits are a +lot more nuanced. Let's explore them by performing a threat-model analysis: + +We consider three attackers: + + 1. A malicious third party trying to discover the identity server mappings + in the homeserver. + + The malicious third party scenario can only be protected against by rate + limiting lookups, given otherwise it looks identical to legitimate traffic. + + 1. An attacker who has stolen an IS db + + In theory the 3PIDs could be stored hashed with a static salt to protect + a stolen DB. This has been descoped from this MSC, and is largely an + orthogonal problem. + + 1. A compromised or malicious identity server, who may be trying to + determine the contents of a user's addressbook (including non-Matrix users) + +Our approaches for protecting against a malicious identity server are: + + * We resign ourselves to the IS knowing the 3PIDs at point of bind, as + otherwise it can't validate them. + + * To protect the 3PIDs of non-Matrix users: + + 1. We could hash the uploaded 3PIDs with a static pepper; however, a + malicious IS could pre-generate a rainbow table to reverse these hashes. + + 1. We could hash the uploaded 3PIDs with a slowly rotating pepper; a + malicious IS could generate a rainbow table in retrospect to reverse these + hashes (but wouldn't be able to reuse the table) + + 1. We could send partial hashes of the uploaded 3PIDs (with full salted + hashes to disambiguate the 3PIDs), have the IS respond with anonymised + partial results, to allow the IS to avoid reversing the 3PIDs (a + k-anonymity approach). However, the IS could still claim to have mappings + for all 3PIDs, and so receive all the salted hashes, and be able to + reverse them via rainbow tables for that salt. + +So, in terms of computational complexity for the attacker, respectively: + + 1. The attacker has to generate a rainbow table over all possible IDs once, + which can then be reused for subsequent attacks. + + 1. The attacker has to generate a rainbow table over all possible IDs for a + given lookup timeframe, which cannot be reused for subsequent attacks. + + 1. The attacker has to generate multiple but partial rainbow tables, one + per group of 3PIDs that share similar hash prefixes, which cannot then be + reused for any other attack. + +For making life hardest for an attacker, option 3 (k-anon) wins. However, it +also makes things harder for the client and server: + + * The client has to calculate new salted hashes for all 3PIDs every time it + uploads. + + * The server has to calculate new salted hashes for all partially-matching + 3PIDs hashes as it looks them up. + +It's worth noting that one could always just go and load up a malicious IS DB +with a huge pre-image set of mappings and thus see what uploaded 3PIDs match, +no matter what algorithm is used. + +For k-anon this would put the most computational onus on the server (as it +would effectively be creating a partial rainbow table for every lookup), but +this is probably not infeasible - so we've gone and added a lot of complexity +and computational cost for not much benefit, given the system can still be +trivially attacked. + +Finally, as more and more users come onto Matrix, their contact lists will +get more and more exposed anyway given the IS server has to be able to +identity Matrix-enabled 3PIDs to perform the lookup. + +Thus the conclusion is that while k-anon is harder to attack, it's unclear +that this is actually enough of an obstacle to meaningfully stop a malicious +IS. Therefore we should KISS and go for a simple hash lookup with a rotating +pepper (which is not much harder than a static pepper, especially if our +initial implementation doesn't bother rotating the pepper). Rather than +trying to make the k-anon approach work, we'd be better off spending that +time figuring out how to store 3pids as hashes in the DB (and in 3pid +bindings etc), or how to decentralise ISes in general. It's also worth noting +that a malicious server may fail to rotate the pepper, making the rotation +logic of questionable benefit. + +A radical model was also considered where the first portion of the +k-anonyminity scheme was done with an identity server, and the second would +be done with various homeservers who originally reported the 3PID to the +identity server. While interesting and more decentralised, some attacks are +still possible if the identity server is running an evil homeserver which it +can direct the client to send its hashes to. Discussion on this matter has +taken place in the MSC-specific room [starting at this +message](https://matrix.to/#/!LlraCeVuFgMaxvRySN:amorgan.xyz/$4wzTSsspbLVa6Lx5cBq6toh6P3TY3YnoxALZuO8n9gk?via=amorgan.xyz&via=matrix.org&via=matrix.vgorcum.com). + +Tangentially, identity servers would ideally just never receive plain-text +addresses, just storing and receiving hash values instead. However, it is +necessary for the identity server to have plain-text addresses during a +[bind](https://matrix.org/docs/spec/identity_service/r0.2.1#post-matrix-identity-api-v1-3pid-bind) +call, in order to send a verification email or sms message. It is not +feasible to defer this job to a homeserver, as the identity server cannot +trust that the homeserver has actually performed verification. Thus it may +not be possible to prevent plain-text 3PIDs of registered Matrix users from +being sent to the identity server at least once. Yet, it is possible that with +a few changes to other Identity Service endpoints, as described in [this +review +comment](https://github.com/matrix-org/matrix-doc/pull/2134#discussion_r309617900), +identity servers could refrain from storing any plaintext 3PIDs at rest. This +however, is a topic for a future MSC. + +## Conclusion + +This proposal outlines a simple method to stop bulk collection of user's +contact lists and their social graphs without any disastrous side effects. All +functionality which depends on the lookup service should continue to function +unhindered by the use of hashes. + +## Footnotes + +[0] Clients would have to generate a full rainbow table specific to the set +pepper to obtain all registered MXIDs, while the server would have to +generate a full rainbow table with the specific pepper to get the plaintext +3pids for non-matrix users. diff --git a/proposals/2140-terms-of-service-2.md b/proposals/2140-terms-of-service-2.md new file mode 100644 index 00000000..9f96a00b --- /dev/null +++ b/proposals/2140-terms-of-service-2.md @@ -0,0 +1,335 @@ +# MSC2140: Terms of Service API for Identity Servers and Integration Managers + +[MSC1692](https://github.com/matrix-org/matrix-doc/issues/1692) introduces a +method for homeservers to require that users read and agree to certain +documents before being permitted to use the service. This proposal introduces a +corresponding method that can be used with Identity Servers and Integration +Managers. + +Requirements for this proposal are: + * ISes and IMs should be able to give multiple documents a user must agree to + abide by + * Each document shoud be versioned + * ISes and IMs must, for each request that they handle, know that the user + making the request has agreed to their data being used. This need not be + absolute proof (we will always have to trust that the client actually + showed the document to the user) but it must be reasonably demonstrable that + the user has given informed consent for the client to use that service. + * ISes and IMs must be able to prevent users from using the service if they + have not provided agreement. + * A user should only have to agree to each version of each document once for + their Matrix ID, ie. having agreed to a set of terms in one client, they + should not have to agree to them again when using a different client. + * Documents should be de-duplicated between services. If two or more services + are hosted by the same organisation, the organisation should have the + option to give their users a single document that encompasses both services + (bearing in mind that the user must be able to opt-out of components of a + service whilst still being able to use the service without that component). + +Identity Servers do not currently require any kind of user login to access the +service and so are unable to track what users have agreed to what terms in the +way that Homeservers do. + +## Proposal + +Throuhgout this proposal, $prefix will be used to refer to the prefix of the +API in question, ie. `/_matrix/identity/v2` for the IS API and +`/_matrix/integrations/v1` for the IM API. + +Note the removal of the `/api` prefix and migration to v2 in the IS API +following convention from +[MSC2134](https://github.com/matrix-org/matrix-doc/issues/2134). + +This proposal introduces: + * A v2 API prefix, with authentication, for the Identity Service + * The `$prefix/terms` endpoint + * The `m.accepted_terms` section in account data + * `POST /_matrix/client/r0/account/3pid/unbind` endpoints on the client/server + API + +This proposal removes: + * The `bind_email` and `bind_msisdn` on the Homeserver `/register` endpoint + +This proposal relies on both Integration Managers and Identity Servers being +able to identify users by their MXID and store the fact that a given MXID has +indicated that they accept the terms given. Integration Managers already +identify users in this way by authenticating them using the OpenID endpoint on +the Homeserver. This proposal introduces the same mechanism to Identity Servers +and adds authentication across the Identity Service API. + +### IS API Authentication + +All current endpoints within `/_matrix/identity/api/v1/` will be duplicated +into `/_matrix/identity/v2`, noting that MSC2134 changes the behaviour of +lookups. Authentication is still expected on MSC2134's proposed endpoints. +Support for `application/x-form-www-urlencoded` parameters in requests will +be dropped from all endpoints. + +Any request to any endpoint within `/_matrix/identity/v2`, with the exception +of: + * `/_matrix/identity/v2` + * `/_matrix/identity/v2/pubkey/*` + * The new `$prefix/account/register` endpoint + * The new `GET /_matrix/identity/v2/terms` + * `$prefix/account/logout` + +...may return an error with `M_UNAUTHORIZED` errcode with HTTP status code 401. +This indicates that the user must authenticate with OpenID and supply a valid +`access_token`. + +Clients authenticate either via an `Authorization` header with a `Bearer` token +or an `access_token` query parameter. + +The existing endpoints under `/_matrix/identity/api/v1/` continue to be +unauthenticated but will be deprecated. ISes may support the old v1 API for as +long as they wish. Once ISes remove support for the old APIs, those endpoints +must return HTTP Status 404. Clients must update to use the v2 API as soon as +possible. + +OpenID authentication in the IS API will work the same as in the Integration Manager +API, as specified in [MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961). + +When clients supply an identity server to the Homeserver in order for the +Homeserver to make calls to the IS on its behalf, it must also supply its +access token for the Identity Server alongside in the `id_access_token` key of +the same JSON object. That is, in the main request object for `requestToken` +and `/_matrix/client/r0/rooms/{roomId}/invite` requests and in the +`threepidCreds` object when supplying 3PID credentials (eg. in the +`m.email.identity` UI auth stage). The server must also relay +`M_TERMS_NOT_SIGNED` errors back to the client. Exceptions to this are any +requests where the only IS operation the Homeserver may perform is unbinding, +ie. `/_matrix/client/r0/account/deactivate` and +`/_matrix/client/r0/account/3pid/delete`, in which case the unbind will be +authenticated by a signed request from the Homeserver. + +### HS Register API + +The `bind_email` and `bind_msisdn` options to `/_matrix/client/r0/register` in +the client/server API will be removed. Due to the fact that +`/_matrix/identity/v2/3pid/bind` requires authentication, it will no longer be +possible for the Homeserver to bind 3PIDs as part of the registration process. + +### IS Register API + +The following new APIs will be introduced to support OpenID auth as per +[MSC1961](https://github.com/matrix-org/matrix-doc/issues/1961): + + * `/_matrix/identity/v2/account/register` + * `/_matrix/identity/v2/account` + * `/_matrix/identity/v2/account/logout` + +Note again the removal of the `/api` prefix and migration to v2 following +convention from +[MSC2134](https://github.com/matrix-org/matrix-doc/issues/2134). + +### Terms API + +New API endpoints will be introduced: + +#### `GET $prefix/terms`: +This returns a set of documents that the user must agree to abide by in order +to use the service. Its response is similar to the structure used in the +`m.terms` UI auth flow of the Client/Server API: + +```json +{ + "policies": { + "terms_of_service": { + "version": "2.0", + "en": { + "name": "Terms of Service", + "url": "https://example.org/somewhere/terms-2.0-en.html" + }, + "fr": { + "name": "Conditions d'utilisation", + "url": "https://example.org/somewhere/terms-2.0-fr.html" + } + }, + "privacy_policy": { + "version": "1.2", + "en": { + "name": "Privacy Policy", + "url": "https://example.org/somewhere/privacy-1.2-en.html" + }, + "fr": { + "name": "Politique de confidentialité", + "url": "https://example.org/somewhere/privacy-1.2-fr.html" + } + } + } +} +``` + +Each document (ie. key/value pair in the 'policies' object) MUST be +uniquely identified by its URL. It is therefore strongly recommended +that the URL contains the version number of the document. The name +and version keys, however, are used only to provide a human-readable +description of the document to the user. + +This endpoint does *not* require authentication. + +#### `POST $prefix/terms`: +Requests to this endpoint have a single key, `user_accepts` whose value is +a list of URLs (given by the `url` field in the GET response) of documents that +the user has agreed to: + +```json +{ + "user_accepts": ["https://example.org/somewhere/terms-2.0-en.html"] +} +``` + +This endpoint requires authentication. + +The clients MUST include the correct URL for the language of the document that +was presented to the user and they agreed to. Servers should accept agreement +of any one language of each document as sufficient, regardless of what language +a client is operating in: users should not have to re-consent to documents if +they change their client to a different language. + +The server responds with an empty JSON object. The server must not assume that +the client will agree to all documents in a single request. + +### Accepted Terms Account Data + +This proposal also defines the `m.accepted_terms` section in User Account +Data in the client/server API that clients SHOULD use to track what sets of +terms the user has consented to. This has an array of URLs under the 'accepted' +key to which the user has agreed to. + +An `m.accepted_terms` section therefore resembles the following: + +```json +{ + "accepted": [ + "https://example.org/somewhere/terms-1.2-en.html", + "https://example.org/somewhere/privacy-1.2-en.html" + ] +} +``` + +Whenever a client submits a `POST $prefix/terms` request to an IS or IM or +completes an `m.terms` flow on the HS (or as soon as possible afterwards, ie. +after registration is complete), it SHOULD update this account data section +adding any the URLs of any additional documents that the user agreed to to this +list. + +### Terms Acceptance in the API + +Before any requests are made to an Identity Server or Integration Manager, +the client must use the `GET $prefix/terms` endpoint to fetch the set of +documents that the user must agree to in order to use the service. + +It then cross-references this set of documents against the `m.accepted_terms` +account data and presents to the user any documents that they have not already +agreed to, along with UI for them to indicate their agreement. If there are no +such documents (ie. if the `policies` dict is empty or the user has already +agreed to all documents) the client proceeds to perform the OpenID +registration. If there are new terms documents, the client prompts the user for +agreement, then once the user has indicated their agreement, it adds these URLs +to `m.accepted_terms` account data and then proceeds with OpenID +authentication, getting a token from the Homeserver and submitting this to the +service using the `register` endpoint. + +Having done this, if the user agreed to any new documents, it performs a `POST +$prefix/terms` request to signal to the server the set of documents that the +user has agreed to. + +Any request to any endpoint in the IM API, and the `/_matrix/identity/v2/` +namespace of the IS API, with the exception of `/_matrix/identity/v2` itself, +may return: + + * `M_UNAUTHORIZED` errcode with HTTP status code 401. This indicates that + the user must authenticate with OpenID and supply a valid `access_token`. + * `M_TERMS_NOT_SIGNED` errcode with HTTP status code 403. This indicates + that the user must agree to (new) terms in order to use or continue to + use the service. + +The `/_matrix/identity/v2/3pid/unbind` endpoint must not return either of these +errors if the request has a valid signature from a Homeserver, and is being authenticated as such. + +In summary, the process for using a service that has not previously been used +in the current login session is: + + * `GET $prefix/terms` + * Compare result with `m.accepted_terms` account data, get set of documents + pending agreement. + * If non-empty, show this set of documents to the user and wait for the user + to indicate their agreement. + * Add the newly agreed documents to `m.accepted_terms`. + * On success, or if there were no documents pending agreement, get an OpenID + token from the Homeserver and submit this token to the `register` endpoint. + Store the resulting access token. + * If the set of documents pending agreement was non-empty, Perform a + `POST $prefix/terms` request to the service with these documents. + +### `POST /_matrix/client/r0/account/3pid/unbind` + +A client uses this client/server API endpoint to request that the Homeserver +removes the given 3PID from the given Identity Server, or all Identity Servers. +Takes the same parameters as +`POST /_matrix/client/r0/account/3pid/delete`, ie. `id_server`, `medium`, +`address` and the newly added `is_token`. + +Returns the same as `POST /_matrix/client/r0/account/3pid/delete`. + +Clients may add IS bindings for 3PIDs that already exist on the user's +Homeserver account by using the `POST /_matrix/client/r0/account/3pid` +to re-add the 3PID. + +## Tradeoffs + +The Identity Service API previously did not require authentication, and OpenID +is reasonably complex, adding a significant burden to both clients and servers. +A custom HTTP header was also considered that could be added to assert that the +client agrees to a particular set of terms. We decided against this in favour +of re-using existing primitives that already exist in the Matrix ecosystem. +Custom HTTP headers are not used anywhere else within Matrix. This also gives a +very simple and natural way for ISes to enforce that users may only bind 3PIDs +to their own MXIDs. + +This introduces a different way of accepting terms from the client/server API +which uses User-Interactive Authentication. In the client/server API, the use +of UI auth allows terms acceptance to be integrated into the registration flow +in a simple and backwards-compatible way. Another option here would be to use +UI Auth on the register endpoint. This would also not allow users to register +before accepting the terms. However, this would then make the OpenID +registration process different and non-standard. + +The `m.accepted_terms` section contains only URLs of the documents that +have been agreed to. This loses information like the name and version of +the document, but: + * It would be up to the clients to copy this information correctly into + account data. + * Having just the URLs makes it much easier for clients to make a list + of URLs and find documents not already agreed to. + +## Potential issues + +This change deprecates all v1 endpoints and so will require clients to update +to continue working. + +## Security considerations + +Requiring authentication on the IS API means it will no longer be possible to +use it anonymously. + +It is assumed that once servers publish a given version of a document at a +given URL, the contents of that URL will not change. This could be mitigated by +identifying documents based on a hash of their contents rather than their URLs. +Agreement to terms in the client/server API makes this assumption, so this +proposal aims to be consistent. + + +## Conclusion + +This proposal adds an error response to all endpoints on the API and a custom +HTTP header on all requests that is used to signal agreement to a set of terms +and conditions. The use of the header is only necessary if the server has no +other means of tracking acceptance of terms per-user. The IS API is not +authenticated so ISes will have no choice but to use the header. The IM API is +authenticated so IMs may either use the header or store acceptance per-user. + +A separate endpoint is specified with a GET request for retrieving the set +of terms required and a POST to indicate that the user consents to those +terms. diff --git a/proposals/2174-move-redacts-key.md b/proposals/2174-move-redacts-key.md new file mode 100644 index 00000000..21212257 --- /dev/null +++ b/proposals/2174-move-redacts-key.md @@ -0,0 +1,17 @@ +# MSC2174: move the `redacts` property to `content` + +[`m.room.redaction`](https://matrix.org/docs/spec/client_server/r0.5.0#m-room-redaction) +events currently have an *event-level* property `redacts` which gives the event +ID of the event being redacted. + +The presence of this field at the event level, rather than under the `content` +key, is anomalous. This MSC proposes that, in a future room version, the +`redacts` property be moved under the `content` key. + +For backwards-compatibility with *older* clients, servers should add a `redacts` +property to the top level of `m.room.redaction` events in *newer* room versions +when serving such events over the Client-Server API. + +For improved compatibility with *newer* clients, servers should add a `redacts` +property to the `content` of `m.room.redaction` events in *older* room versions +when serving such events over the Client-Server API. diff --git a/proposals/2175-remove-creator-field.md b/proposals/2175-remove-creator-field.md new file mode 100644 index 00000000..6a9d8f3c --- /dev/null +++ b/proposals/2175-remove-creator-field.md @@ -0,0 +1,17 @@ +# MSC2175: Remove the `creator` field from `m.room.create` events + +[`m.room.create`](https://matrix.org/docs/spec/client_server/r0.5.0#m-room-create) +events have a mandatory `creator` property giving the ID of the user who +created the room. This field is redundant as it is always identical to the +`sender` of the create event. + +This MSC proposes that, in a future room version, this field should be removed +and that the `sender` field be used instead. + +Note that `creator` is mentioned in the [auth +rules](https://matrix.org/docs/spec/rooms/v1#authorization-rules). It can +safely be removed. + +`creator` is also mentioned as a key to be preserved during [Event +redaction](https://matrix.org/docs/spec/client_server/r0.5.0#redactions). It +should be removed from that list. diff --git a/proposals/2181-user-deactivated-errcode.md b/proposals/2181-user-deactivated-errcode.md new file mode 100644 index 00000000..8d2e1893 --- /dev/null +++ b/proposals/2181-user-deactivated-errcode.md @@ -0,0 +1,50 @@ +# Add an Error Code for Signaling a Deactivated User + +Currently, when a user attempts to log in, they will receive an `M_FORBIDDEN` +error code if their password is incorrect. However, if the user's account is +deactivated, they will also receive an `M_FORBIDDEN`, leaving clients in a +state where they are unable to inform the user that the reason they cannot +log in is that their account has been deactivated. This leads to confusion +and password resetting which ultimately results in frustration. + +## Proposal + +This proposal asks to create a new error code, `M_USER_DEACTIVATED`, that may +be returned whenever an action is attempted that requires an activited user, +but the authenticating user is deactivated. The HTTP code to return alongside +is `403`. + +An example of this could be returning `M_USER_DEACTIVATED` on `/login`, when +an identifier of a deactivated user is sent to the homeserver. Whether the +password has to be correct depends on whether the Homeserver implementation +removes login information on deactivation. This is an implementation detail. + +It should be noted that this proposal is not requiring implementations to +return `M_USER_DEACTIVATED` on any endpoints when a request from a +deactivated user appears. Instead it is simply defining the new error code, +recommends that it should be used in situations as described above, and that +the client should understand the meaning of it when it is received. + +## Tradeoffs + +The alternative is to continue returning an `M_FORBIDDEN`, but send back a +different error message. This is undesirable as clients are supposed to treat +the message as an opaque string, and should not be performing any +pattern-matching on it. + +## Potential issues + +None + +## Security considerations + +While the existence of a user was already public knowledge (one can check if +the User ID is available through +[/_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-r0-register-available), +this proposal would allow any user to be able to detect if a registered +account has been deactivated, depending on the homeserver's implementation. + +## Conclusion + +Adding `M_USER_DEACTIVATED` would better inform clients about the state of a +user's account, and lead to less confusion when they cannot log in. diff --git a/proposals/2197-search_filter_in_federation_publicrooms.md b/proposals/2197-search_filter_in_federation_publicrooms.md new file mode 100644 index 00000000..ef1521d1 --- /dev/null +++ b/proposals/2197-search_filter_in_federation_publicrooms.md @@ -0,0 +1,155 @@ +# MSC2197 – Search Filtering in Public Room Directory over Federation + +This MSC proposes introducing the `POST` method to the `/publicRooms` Federation +API endpoint, including a `filter` argument which allows server-side filtering +of rooms. + +We are motivated by the opportunity to make searching the public Room Directory +more efficient over Federation. + +## Motivation + +Although the Client-Server API includes the filtering capability in +`/publicRooms`, the Federation API currently does not. + +This leads to a situation that is wasteful of effort and network traffic for +both homeservers; searching a remote server involves first downloading its +entire room list and then filtering afterwards. + +## Proposal + +Having a filtered `/publicRooms` API endpoint means that irrelevant or +uninteresting rooms can be excluded from a room directory query response. +In turn, this means that these room directory query responses can be generated +more quickly and then, due to their smaller size, transmitted over the network +more quickly. + +These benefits have been utilised in the Client-Server API, which implements +search filtering using the `filter` JSON body parameter in the `POST` method on +the `/publicRooms` endpoint. + +Ignoring the `server` parameter in the Client-Server API, the following specific +differences are noticed between the Client-Server and Federation API's +`/publicRooms` endpoints: + +* the Federation API endpoint only accepts the `GET` method whereas the + Client-Server API accepts the `POST` method as well. +* the Federation API accepts `third_party_instance_id` and + `include_all_networks` parameters through the `GET` method, whereas the + Client-Server API only features these in the `POST` method. + +This MSC proposes to introduce support for the `POST` method in the Federation +API's `/publicRooms` endpoint, with all but one of the parameters from that of +the Client-Server API. + +The parameter that is intentionally omitted is the `server` query parameter, as +it does not make sense to include it – the requesting homeserver could make a +direct request instead of requesting that a request be relayed. + +The parameters which are copied, however, shall have the same semantics as +they do in the Client-Server API. + +In the interest of clarity, the proposed parameter set is listed below, along +with a repetition of the definitions of used substructures. The response format +has been omitted as it is the same as that of the current Client-Server and +Federation APIs, which do not differ in this respect. + +### `POST /_matrix/federation/v1/publicRooms` + +#### Query Parameters + +There are no query parameters. Notably, we intentionally do not inherit the +`server` query parameter from the Client-Server API. + +#### JSON Body Parameters + +* `limit` (`integer`): Limit the number of search results returned. +* `since` (`string`): A pagination token from a previous request, allowing + clients to get the next (or previous) batch of rooms. The direction of + pagination is specified solely by which token is supplied, rather than via an + explicit flag. +* `filter` (`Filter`): Filter to apply to the results. +* `include_all_networks` (`boolean`): Whether or not to include all known + networks/protocols from application services on the homeserver. + Defaults to false. +* `third_party_instance_id` (`boolean`): The specific third party + network/protocol to request from the homeserver. + Can only be used if `include_all_networks` is false. + +### `Filter` Parameters + +* `generic_search_term` (`string`): A string to search for in the room metadata, +e.g. name, topic, canonical alias etc. (Optional). + +## Tradeoffs + +An alternative approach might be for implementations to carry on as they are but +also cache (and potentially index) remote homeservers' room directories. +This would not require a spec change. + +However, this would be unsatisfactory because it would lead to outdated room +directory results and/or caches that provide no benefit (as room directory +searches are generally infrequent enough that a cache would be outdated before +being reused, on small – if not most – homeservers). + +## Potential issues + +### Backwards Compatibility + +After this proposal is implemented, outdated homeservers will still exist which +do not support the room filtering functionality specified in this MSC. In this +case, homeservers will have to fall-back to downloading the entire room +directory and performing the filtering themselves, as currently happens. +This is not considered a problem since it will not lead to a situation that is +any worse than the current one, and it is expected that large homeservers +– which cause the most work with the current search implementations – +would be quick to upgrade to support this feature once it is available. + +As the `POST` method was not previously accepted on the `/publicRooms` endpoint +over federation, then requesting servers should fall back to the old behaviour, +if one of the following errors is encountered: + +- an HTTP `400` response with an `M_UNRECOGNIZED` standard error response + `errcode` (this is what would be typically expected in this situation) +- a `404` (Not Found) HTTP error response +- a `405` (Method Not Allowed) HTTP error response + +## Security considerations + +There are no known security considerations. + +## Privacy considerations + +At present, remote homeservers do not learn about what a user has searched for. + +However, under this proposal, in the context of using the Federation API to +forward on queries from the Client-Server API, a client's homeserver would end +up sharing the client's search terms with a remote homeserver, which may not be +operated by the same party or even trusted. For example, users' search terms +could be logged. + +The privacy implications of this proposal are not overly major, as the data +that's being shared is [\[1\]][1]: + +- only covered by GDPR if: + - the search terms contain personal data, or + - the user's homeserver IP address or domain name is uniquely identifying + (because it's a single-person homeserver, perhaps) +- likely to be *expected* to be shared with the remote homeserver + +[1]: https://github.com/matrix-org/matrix-doc/pull/2197#issuecomment-517641751 + +For the sake of clarity, clients are strongly encouraged to display a warning +that a remote search will take the user's data outside the jurisdiction of their +own homeserver, before using the `server` parameter of the Client-Server API +`/publicRooms`, as it can be assumed that this will lead to the server invoking +the Federation API's `/publicRooms` – on the specified remote server – with the +user's search terms. + +## Conclusion + +By allowing homeservers to pass on search filters, we enable remote homeservers' +room directories to be efficiently searched, because, realistically speaking, +only the remote homeserver is in a position to be able to perform search +efficiently, by taking advantage of indexing and other such optimisations. + diff --git a/proposals/2229-rebind-existing-3pid.md b/proposals/2229-rebind-existing-3pid.md new file mode 100644 index 00000000..83883c78 --- /dev/null +++ b/proposals/2229-rebind-existing-3pid.md @@ -0,0 +1,101 @@ +# Allowing 3PID Owners to Rebind + +``` +3PID + noun + + A "third-party identifier" such as an email address or phone number, that + can be tied to your Matrix ID in order for your contacts outside of + Matrix to find you, typically with the help of an identity server. + +Identity server + noun + + A queryable server that holds mappings between 3PIDs and Matrix IDs. + +Bind + verb + + Create a mapping between a 3PID and a Matrix ID. Useful for people to + find a user based on their existing third-party contact information. +``` + +As part of the on-going privacy work, Matrix client applications are +attempting to make the concept of an identity server clearer to the user, as +well as allowing a user to interact with multiple identity servers while +logged in. In facilitating this, Matrix clients should be able to allow +logged-in users the ability to pick an identity server, see what 3PIDs they +currently have bound to their Matrix ID, and bind/unbind addresses as they +desire. + +When implementing this functionality, a technicality in the spec was found +to prevent certain abilities for a user. A user could not add a 3PID to their +homeserver before binding it to an identity server. It also prevents users +from binding the same 3PID to multiple identity servers. The line "The +homeserver must check that the given email address is **not** already +associated with an account on this homeserver." appears under the [POST +/_matrix/client/r0/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) +endpoint description. The same goes for the [equivalent msisdn (phone) +endpoint](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken). + +When a user adds an email to their account on their homeserver, they can +choose to bind that email to an identity server at the same time. This is +specified through a `bind` boolean. If the user first adds the 3PID with +`bind: false`, then decides they want to bind that 3PID to an identity server +to make themselves discoverable by it, by making another request with `bind: +true`, the homeserver will reject the second request, because this 3PID is +already tied to the user's account. + +Similarly, when a user initially sends their 3PID with `bind: true` through a +homeserver to identity server A, the homeserver keeps a record and attaches +the address to the local account. If the user then switches to identity +server B to try and do the same, the homeserver will reject the second +request as this address has already been bound. + +## Proposal + +This proposal calls for allowing 3PID owners to rebind their 3PIDs using the +[`POST +/_matrix/client/r0/account/3pid/email/requestToken`](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-email-requesttoken) +and [`POST +/_matrix/client/r0/account/3pid/msisdn/requestToken`](https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-account-3pid-msisdn-requesttoken) +endpoints by extending the definition of what homeservers should check before +rejecting a bind. + +Homeservers should reject the binding of a 3PID if it has already been bound, +**unless** the requesting user is the one who originally bound that 3PID. If +so, then they should be able to bind it again and again if they so choose. + +In doing so, users would be able to rebind their 3PIDs, even if the +homeserver has already been made aware of it. + +## Tradeoffs + +Identity servers will still let 3PIDs be rebound to another Matrix ID, while +a single homeserver won't let a 3PID transition between two users. If one +thinks about typical internet services however, you aren't allowed to simply +take an email address from another account even if you have control of it, so +this shouldn't be too unintuitive. + +## Potential issues + +Newer clients will expect homeservers to allow them to switch between +identity servers and bind/rebind emails as they please. If dealing with an +older homeserver, clients will receive an `HTTP 400 M_THREEPID_IN_USE`. +Clients should be prepared to understand that this may just mean they are +dealing with an old homeserver, versus the 3PID already being bound on this +homeserver by another user. + +## Security considerations + +None. + +## Conclusion + +By lifting the restriction of not allowing a user to bind a 3PID multiple +times, we allow the basic ability of publishing a 3PID after associating it +with an account, as well as allow users to interact with multiple identity +servers on the same account with the same 3PIDs. This not only allows the +user to play around and gain a better understanding of the purpose of an +identity server, but it is also one step towards further decentralisation in +the identity server space. diff --git a/proposals/2230-identity-server-account-data.md b/proposals/2230-identity-server-account-data.md new file mode 100644 index 00000000..19fb3062 --- /dev/null +++ b/proposals/2230-identity-server-account-data.md @@ -0,0 +1,71 @@ +# MSC2230: Store Identity Server in Account Data + +The URL of the Identity Server to use is currently specified at registration and +login time and then used for the lifetime of a login session. If users wish to +specify a custom one, they must do so each time they log in on every client. +Once they have chosen an Identity Server to advertise their 3PIDs on, it would +be normal that they would wish to continue using this Identity Server for all +Identity requests in their account accross all clients. This proposal aims to +make this easier. + +## Proposal + +The base URL of the Identity Server is to be stored in user account data. It +shall be stored in the same format as in a .well-known file under the event type +`m.identity_server` and shall comprise a single key, `base_url` which is the +base URL of the ID Server to use (that is, the part before `/_matrix`, including +`https://`). + +Upon registration or login, a client SHOULD refrain from performing any requests +to the Identity Server until the account data has been fetched from the server. +Once it has the account data, it SHOULD check for the presence of the +`m.identity_server` key. If present, the `base_url` in this key SHOULD be used +as the Identity Server base URL for the duration of the login session. If this +key is not present, the client SHOULD use whatever value it would have used prior +to this MSC. It should not update the account data in this situation. + +Client SHOULD listen for changes in the `m.identity_server` account data value +and update the URL that they use for ID Server requests accordingly. + +Clients can offer a way for users to change the ID server being used. If they +do, the client MUST update the value of `m.identity_server` accordingly. + +The `m.identity_server` may be present with a `base_url` of `null`. In this case, +clients MUST treat this as no ID Server URL being set and not perform ID +Server requests, disabling any functionality that requires such requests. + +Conversely, if a user wishes to disable ID Server functionality, the client +shall action this by setting the `base_url` of the `m.identity_server` +account data entry to `null`. + +### Transition Period + +Clients will continue to use whatever IS URLs they currently use until the +user sets one explicitly, at which point it will be written to account data +and all clients will start using this value. + +## Tradeoffs + +There are a number of ways to transition to this new scheme. Clients could +populate the account data with their current ID Server URL as soon as +possible, and immediately use any new value seen on account data. This +would a much faster migration but any users with clients using different +ID Servers would suddenly find all their clients using the ID Server of +whichever client they updated first. + +## Potential issues + +Users will no longer be able to have different clients configured with +different ID Servers. + +## Security considerations + +An attacker would be able to force all a user clients to use a given ID Server +if they gained control of any of a user's logins. + +## Conclusion + +This makes the ID server an account setting which means it persists between +logins. The intention would be to phase out clients ever asking for an ID +Server URL at registration or login: this will be much easier for users to +understand whilst still retaining the flexibilty for those who want it. diff --git a/proposals/images/2010-spoiler-example.gif b/proposals/images/2010-spoiler-example.gif new file mode 100644 index 00000000..3716bd77 Binary files /dev/null and b/proposals/images/2010-spoiler-example.gif differ diff --git a/scripts/continuserv/main.go b/scripts/continuserv/main.go index 2ef6fed9..1bd07e6e 100644 --- a/scripts/continuserv/main.go +++ b/scripts/continuserv/main.go @@ -52,7 +52,7 @@ func main() { walker := makeWalker(dir, w) paths := []string{"api", "changelogs", "event-schemas", "scripts", - "specification"} + "specification", "schemas", "data-definitions"} for _, p := range paths { filepath.Walk(path.Join(dir, p), walker) diff --git a/scripts/css/tables.css b/scripts/css/tables.css new file mode 100644 index 00000000..03ee1d85 --- /dev/null +++ b/scripts/css/tables.css @@ -0,0 +1,4 @@ +/* Column with header cells */ +table.docutils tbody th.stub { + background: #eeeeee; +} diff --git a/scripts/templating/matrix_templates/sections.py b/scripts/templating/matrix_templates/sections.py index 5961aa24..7000916b 100644 --- a/scripts/templating/matrix_templates/sections.py +++ b/scripts/templating/matrix_templates/sections.py @@ -18,6 +18,7 @@ import inspect import json import os import logging +import re logger = logging.getLogger(__name__) @@ -110,12 +111,13 @@ class MatrixSections(Sections): # Special function: Returning a dict will specify multiple sections where # the key is the section name and the value is the value of the section def render_group_events(self): - # map all event schemata to the form $EVENTTYPE_event with s/./_/g - # e.g. m_room_topic_event + # map all event schemata to the form $EVENTTYPE_event with s/.#/_/g + # e.g. m_room_topic_event or m_room_message_m_text_event schemas = self.units.get("event_schemas") renders = {} for event_type in schemas: - renders[event_type.replace(".", "_") + "_event"] = self._render_events( + underscored_event_type = event_type.replace(".", "_").replace("$", "_") + renders[underscored_event_type + "_event"] = self._render_events( lambda x: x == event_type, sorted ) return renders @@ -124,7 +126,7 @@ class MatrixSections(Sections): def filterFn(eventType): return ( eventType.startswith("m.room") and - not eventType.startswith("m.room.message#m.") + not eventType.startswith("m.room.message$m.") ) return self._render_events(filterFn, sorted) @@ -137,16 +139,22 @@ class MatrixSections(Sections): ]["subtitle"] sections = [] msgtype_order = [ - "m.room.message#m.text", "m.room.message#m.emote", - "m.room.message#m.notice", "m.room.message#m.image", - "m.room.message#m.file" + "m.room.message$m.text", "m.room.message$m.emote", + "m.room.message$m.notice", "m.room.message$m.image", + "m.room.message$m.file" + ] + excluded_types = [ + # We exclude server notices from here because we handle them in a + # dedicated module. We do not want to confuse developers this early + # in the spec. + "m.room.message$m.server_notice", ] other_msgtypes = [ - k for k in schemas.keys() if k.startswith("m.room.message#") and - k not in msgtype_order + k for k in schemas.keys() if k.startswith("m.room.message$") and + k not in msgtype_order and k not in excluded_types ] for event_name in (msgtype_order + other_msgtypes): - if not event_name.startswith("m.room.message#m."): + if not event_name.startswith("m.room.message$m."): continue sections.append(template.render( example=examples[event_name][0], @@ -218,3 +226,19 @@ class MatrixSections(Sections): examples=swagger_def['examples'], title_kind=subtitle_title_char) return rendered + + def render_sas_emoji_table(self): + emoji = self.units.get("sas_emoji") + rendered = ".. csv-table::\n" + rendered += " :header: \"Number\", \"Emoji\", \"Unicode\", \"Description\"\n" + rendered += " :widths: 10, 10, 15, 20\n" + rendered += "\n" + for row in emoji: + rendered += " %d, \"%s\", \"``%s``\", \"%s\"\n" % ( + row['number'], + row['emoji'], + row['unicode'], + row['description'], + ) + rendered += "\n" + return rendered diff --git a/scripts/templating/matrix_templates/templates/events.tmpl b/scripts/templating/matrix_templates/templates/events.tmpl index 0955cf4c..f55be73f 100644 --- a/scripts/templating/matrix_templates/templates/events.tmpl +++ b/scripts/templating/matrix_templates/templates/events.tmpl @@ -1,7 +1,14 @@ {% import 'tables.tmpl' as tables -%} +{% if (event.type_with_msgtype) %} +``{{event.type_with_msgtype}}`` +{{(4 + event.type_with_msgtype | length) * title_kind}} +{% endif -%} + +{% if (not event.type_with_msgtype) %} ``{{event.type}}`` {{(4 + event.type | length) * title_kind}} +{% endif -%} {% if (event.typeof | length) %} *{{event.typeof}}* diff --git a/scripts/templating/matrix_templates/templates/http-api.tmpl b/scripts/templating/matrix_templates/templates/http-api.tmpl index 0b9207d9..d2ee3ff7 100644 --- a/scripts/templating/matrix_templates/templates/http-api.tmpl +++ b/scripts/templating/matrix_templates/templates/http-api.tmpl @@ -1,22 +1,26 @@ {% import 'tables.tmpl' as tables -%} -``{{endpoint.method}} {{endpoint.path}}`` -{{(5 + (endpoint.path | length) + (endpoint.method | length)) * title_kind}} {% if "deprecated" in endpoint and endpoint.deprecated -%} +Deprecated: ``{{endpoint.method}} {{endpoint.path}}`` +{{(17 + (endpoint.path | length) + (endpoint.method | length)) * title_kind}} + .. WARNING:: This API is deprecated and will be removed from a future release. +{% else %} +``{{endpoint.method}} {{endpoint.path}}`` +{{(5 + (endpoint.path | length) + (endpoint.method | length)) * title_kind}} {% endif -%} {{endpoint.desc}} -{{":Rate-limited: Yes." if endpoint.rate_limited else "" }} -{{":Requires auth: Yes." if endpoint.requires_auth else "" }} +{{":Rate-limited: Yes." if endpoint.rate_limited else ":Rate-limited: No." }} +{{":Requires auth: Yes." if endpoint.requires_auth else ":Requires auth: No." }} .. class:: httpheaders - + Request format: - + {% if (endpoint.req_param_by_loc | length) %} {{ tables.split_paramtable(endpoint.req_param_by_loc) }} {% if (endpoint.req_body_tables) %} @@ -33,7 +37,7 @@ {% if endpoint.res_headers is not none -%} .. class:: httpheaders - + Response headers: {{ tables.paramtable(endpoint.res_headers.rows) }} @@ -42,7 +46,7 @@ {% if endpoint.res_tables|length > 0 -%} .. class:: httpheaders - + Response format: {% for table in endpoint.res_tables -%} @@ -54,7 +58,7 @@ {% endif -%} .. class:: httpheaders - + Example request: .. code:: http @@ -64,7 +68,7 @@ {% if endpoint.responses|length > 0 -%} .. class:: httpheaders - + Response{{"s" if endpoint.responses|length > 1 else "" }}: {% endif -%} @@ -78,7 +82,7 @@ {% if res["example"] -%} .. class:: httpheaders - + Example .. code:: json diff --git a/scripts/templating/matrix_templates/units.py b/scripts/templating/matrix_templates/units.py index c1755119..04e6f8a9 100644 --- a/scripts/templating/matrix_templates/units.py +++ b/scripts/templating/matrix_templates/units.py @@ -59,6 +59,8 @@ TARGETS = os.path.join(matrix_doc_dir, "specification/targets.yaml") ROOM_EVENT = "core-event-schema/room_event.yaml" STATE_EVENT = "core-event-schema/state_event.yaml" +SAS_EMOJI_JSON = os.path.join(matrix_doc_dir, "data-definitions/sas-emoji.json") + logger = logging.getLogger(__name__) # a yaml Loader which loads mappings into OrderedDicts instead of regular @@ -521,6 +523,7 @@ class MatrixUnits(Units): path_template = path example_query_params = [] example_body = "" + example_mime = "application/json" for param in endpoint_swagger.get("parameters", []): # even body params should have names, otherwise the active docs don't work. param_name = param["name"] @@ -533,6 +536,10 @@ class MatrixUnits(Units): example_body = get_example_for_param(param) continue + if param_loc == "header": + if param["name"] == "Content-Type" and param["x-example"]: + example_mime = param["x-example"] + # description desc = param.get("description", "") if param.get("required"): @@ -610,8 +617,8 @@ class MatrixUnits(Units): example_query_params) if example_body: endpoint["example"][ - "req"] = "%s %s%s HTTP/1.1\nContent-Type: application/json\n\n%s" % ( - method.upper(), path_template, query_string, example_body + "req"] = "%s %s%s HTTP/1.1\nContent-Type: %s\n\n%s" % ( + method.upper(), path_template, query_string, example_mime, example_body ) else: endpoint["example"]["req"] = "%s %s%s HTTP/1.1\n\n" % ( @@ -743,6 +750,7 @@ class MatrixUnits(Units): with open(filepath, encoding="utf-8") as f: event_schema = yaml.load(f, OrderedLoader) + event_schema = resolve_references(filepath, event_schema) schema_info = process_data_type( event_schema, @@ -790,7 +798,7 @@ class MatrixUnits(Units): if not filename.startswith("m."): continue - event_name = filename.split("#")[0] + event_name = filename.split("$")[0] filepath = os.path.join(path, filename) logger.info("Reading event example: %s" % filepath) try: @@ -846,6 +854,7 @@ class MatrixUnits(Units): "title": None, "desc": None, "msgtype": None, + "type_with_msgtype": None, # for the template's sake "content_fields": [ # ] @@ -884,6 +893,7 @@ class MatrixUnits(Units): ) if msgtype: schema["msgtype"] = msgtype[0] # enum prop + schema["type_with_msgtype"] = schema["type"] + " (" + msgtype[0] + ")" # link to msgtypes for m.room.message if schema["type"] == "m.room.message" and not msgtype: @@ -892,6 +902,14 @@ class MatrixUnits(Units): "`m.room.message msgtypes`_." ) + # method types for m.key.verification.start + if schema["type"] == "m.key.verification.start": + methods = Units.prop( + json_schema, "properties/content/properties/method/enum" + ) + if methods: + schema["type_with_msgtype"] = schema["type"] + " (" + methods[0] + ")" + # Assign state key info if it has some if schema["typeof"] == "State Event": skey_desc = Units.prop( @@ -952,6 +970,9 @@ class MatrixUnits(Units): if re.match("^[=]{3,}$", line.strip()): # the last line was a header - use that as our new title_part title_part = prev_line.strip() + # take off the last line from the changelog_body_lines because it's the title + if len(changelog_body_lines) > 0: + changelog_body_lines = changelog_body_lines[:len(changelog_body_lines) - 1] continue if re.match("^[-]{3,}$", line.strip()): # the last line is a subheading - drop this line because it's the underline @@ -965,6 +986,7 @@ class MatrixUnits(Units): # 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') + prev_line = line if len(changelog_body_lines) > 0: changelogs[api_name] = "".join(changelog_body_lines) @@ -1081,3 +1103,21 @@ class MatrixUnits(Units): "string": git_version, "revision": git_commit } + + def load_sas_emoji(self): + with open(SAS_EMOJI_JSON, 'r', encoding='utf-8') as sas_json: + emoji = json.load(sas_json) + + # Verify the emoji matches the unicode + for c in emoji: + e = c['emoji'] + logger.info("Checking emoji %s (%s)", e, c['description']) + u = re.sub(r'U\+([0-9a-fA-F]+)', lambda m: chr(int(m.group(1), 16)), c['unicode']) + if e != u: + raise Exception("Emoji %s should be %s not %s" % ( + c['description'], + repr(e), + c['unicode'], + )) + + return emoji diff --git a/scripts/test-and-build.sh b/scripts/test-and-build.sh index 710b03dd..f45e2da6 100755 --- a/scripts/test-and-build.sh +++ b/scripts/test-and-build.sh @@ -30,5 +30,5 @@ go get gopkg.in/fsnotify/fsnotify.v1 # build the spec for matrix.org. # (we don't actually use it on travis, but it's still useful to check we -# can build it. On Jenkins, this is then used to deploy to matrix.org). +# can build it. On Buildkite, this is then used to deploy to matrix.org). ./scripts/generate-matrix-org-assets diff --git a/specification/appendices/identifier_grammar.rst b/specification/appendices/identifier_grammar.rst index ea805955..4cce1f90 100644 --- a/specification/appendices/identifier_grammar.rst +++ b/specification/appendices/identifier_grammar.rst @@ -91,11 +91,10 @@ The Matrix protocol uses a common format to assign unique identifiers to a number of entities, including users, events and rooms. Each identifier takes the form:: - &localpart:domain + &string -where ``&`` represents a 'sigil' character; ``domain`` is the `server name`_ of -the homeserver which allocated the identifier, and ``localpart`` is an -identifier allocated by that homeserver. +where ``&`` represents a 'sigil' character; ``string`` is the string which makes +up the identifier. The sigil characters are as follows: @@ -105,8 +104,17 @@ The sigil characters are as follows: * ``+``: Group ID * ``#``: Room alias +User IDs, group IDs, room IDs, room aliases, and sometimes event IDs take the form:: + + &localpart:domain + +where ``domain`` is the `server name`_ of the homeserver which allocated the +identifier, and ``localpart`` is an identifier allocated by that homeserver. + The precise grammar defining the allowable format of an identifier depends on -the type of identifier. +the type of identifier. For example, event IDs can sometimes be represented with +a ``domain`` component under some conditions - see the `Event IDs <#room-ids-and-event-ids>`_ +section below for more information. User Identifiers ++++++++++++++++ @@ -182,7 +190,7 @@ history includes events with a ``sender`` which does not conform. In order to handle these rooms successfully, clients and servers MUST accept user IDs with localparts from the expanded character set:: - extended_user_id_char = %x21-39 / %x3B-7F ; all ascii printing chars except : + extended_user_id_char = %x21-39 / %x3B-7E ; all ascii printing chars except : Mapping from other character sets <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< diff --git a/specification/appendices/signing_json.rst b/specification/appendices/signing_json.rst index 795d6669..8036950e 100644 --- a/specification/appendices/signing_json.rst +++ b/specification/appendices/signing_json.rst @@ -59,7 +59,7 @@ Grammar +++++++ Adapted from the grammar in http://tools.ietf.org/html/rfc7159 removing -insignificant whitespace, fractions, exponents and redundant character escapes +insignificant whitespace, fractions, exponents and redundant character escapes. .. code:: diff --git a/specification/appendices/test_vectors.rst b/specification/appendices/test_vectors.rst index e2b8fb58..05b115db 100644 --- a/specification/appendices/test_vectors.rst +++ b/specification/appendices/test_vectors.rst @@ -91,11 +91,17 @@ Given the following minimally-sized event: .. code:: json { - "event_id": "$0:domain", + "room_id": "!x:domain", + "sender": "@a:domain", "origin": "domain", "origin_server_ts": 1000000, "signatures": {}, + "hashes": {}, "type": "X", + "content": {}, + "prev_events": [], + "auth_events": [], + "depth": 3, "unsigned": { "age_ts": 1000000 } @@ -106,15 +112,20 @@ The event signing algorithm should emit the following signed event: .. code:: json { - "event_id": "$0:domain", + "auth_events": [], + "content": {}, + "depth": 3, "hashes": { - "sha256": "6tJjLpXtggfke8UxFhAKg82QVkJzvKOVOOSjUDK4ZSI" + "sha256": "5jM4wQpv6lnBo7CLIghJuHdW+s2CMBJPUOGOC89ncos" }, "origin": "domain", "origin_server_ts": 1000000, + "prev_events": [], + "room_id": "!x:domain", + "sender": "@a:domain", "signatures": { "domain": { - "ed25519:1": "2Wptgo4CwmLo/Y8B8qinxApKaCkBG2fjTWB7AbP5Uy+aIbygsSdLOFzvdDjww8zUVKCmI02eP9xtyJxc/cLiBA" + "ed25519:1": "KxwGjPSDEtvnFgU00fwFz+l6d2pJM6XBIaMEn81SXPTRl16AqLAYqfIReFGZlHi5KLjAWbOoMszkwsQma+lYAg" } }, "type": "X", @@ -129,7 +140,7 @@ Given the following event containing redactable content: { "content": { - "body": "Here is the message content", + "body": "Here is the message content" }, "event_id": "$0:domain", "origin": "domain", @@ -149,7 +160,7 @@ The event signing algorithm should emit the following signed event: { "content": { - "body": "Here is the message content", + "body": "Here is the message content" }, "event_id": "$0:domain", "hashes": { diff --git a/specification/application_service_api.rst b/specification/application_service_api.rst index 865544dd..302f0980 100644 --- a/specification/application_service_api.rst +++ b/specification/application_service_api.rst @@ -36,7 +36,7 @@ Changelog --------- -.. topic:: Version: unstable +.. topic:: Version: %APPSERVICE_RELEASE_LABEL% {{application_service_changelog}} This version of the specification is generated from @@ -52,6 +52,7 @@ 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 `_ @@ -187,6 +188,15 @@ An example registration file for an IRC-bridging application service is below: Homeserver -> Application Service API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Authorization ++++++++++++++ + +Homeservers MUST include a query parameter named ``access_token`` containing the +``hs_token`` from the application service's registration when making requests to +the application service. Application services MUST verify the provided ``access_token`` +matches their known ``hs_token``, failing the request with a ``M_FORBIDDEN`` error +if it does not match. + Legacy routes +++++++++++++ @@ -203,14 +213,14 @@ should fall back to the older endpoints for the application service. The older endpoints have the exact same request body and response format, they just belong at a different path. The equivalent path for each is as follows: -* ``/_matrix/app/v1/transactions/{txnId}`` becomes ``/transactions/{txnId}`` -* ``/_matrix/app/v1/users/{userId}`` becomes ``/users/{userId}`` -* ``/_matrix/app/v1/rooms/{roomAlias}`` becomes ``/rooms/{roomAlias}`` -* ``/_matrix/app/v1/thirdparty/protocol/{protocol}`` becomes ``/_matrix/app/unstable/thirdparty/protocol/{protocol}`` -* ``/_matrix/app/v1/thirdparty/user/{user}`` becomes ``/_matrix/app/unstable/thirdparty/user/{user}`` -* ``/_matrix/app/v1/thirdparty/location/{location}`` becomes ``/_matrix/app/unstable/thirdparty/location/{location}`` -* ``/_matrix/app/v1/thirdparty/user`` becomes ``/_matrix/app/unstable/thirdparty/user`` -* ``/_matrix/app/v1/thirdparty/location`` becomes ``/_matrix/app/unstable/thirdparty/location`` +* ``/_matrix/app/v1/transactions/{txnId}`` should fall back to ``/transactions/{txnId}`` +* ``/_matrix/app/v1/users/{userId}`` should fall back to ``/users/{userId}`` +* ``/_matrix/app/v1/rooms/{roomAlias}`` should fall back to ``/rooms/{roomAlias}`` +* ``/_matrix/app/v1/thirdparty/protocol/{protocol}`` should fall back to ``/_matrix/app/unstable/thirdparty/protocol/{protocol}`` +* ``/_matrix/app/v1/thirdparty/user/{user}`` should fall back to ``/_matrix/app/unstable/thirdparty/user/{user}`` +* ``/_matrix/app/v1/thirdparty/location/{location}`` should fall back to ``/_matrix/app/unstable/thirdparty/location/{location}`` +* ``/_matrix/app/v1/thirdparty/user`` should fall back to ``/_matrix/app/unstable/thirdparty/user`` +* ``/_matrix/app/v1/thirdparty/location`` should fall back to ``/_matrix/app/unstable/thirdparty/location`` Homeservers should periodically try again for the newer endpoints because the application service may have been updated. diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index f9f815f7..916604a3 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -45,6 +45,7 @@ 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.5.0 `_ - `r0.4.0 `_ - `r0.3.0 `_ - `r0.2.0 `_ @@ -57,6 +58,8 @@ The following other versions are also available, in reverse chronological order: API Standards ------------- +.. TODO: Move a lot of this to a common area for all specs. + .. TODO Need to specify any HMAC or access_token lifetime/ratcheting tricks We need to specify capability negotiation for extensible transports @@ -82,7 +85,6 @@ names in JSON objects passed over the API also follow this convention. ``/createRoom``. A future version of this specification will address the inconsistency. - Any errors which occur at the Matrix API level MUST return a "standard error response". This is a JSON object which looks like: @@ -148,6 +150,10 @@ Other error codes the client might encounter are: :``M_UNAUTHORIZED``: The request was not correctly authorized. Usually due to login failures. +:``M_USER_DEACTIVATED``: + The user ID associated with the request has been deactivated. Typically for + endpoints that prove authentication, such as ``/login``. + :``M_USER_IN_USE``: Encountered when trying to register a user ID which has been taken. @@ -219,9 +225,12 @@ Other error codes the client might encounter are: to modify state (eg: sending messages, account data, etc) and not routes which only read state (eg: ``/sync``, get account data, etc). +:``M_CANNOT_LEAVE_SERVER_NOTICE_ROOM``: + The user is unable to reject an invite to join the server notices room. See the + `Server Notices <#server-notices>`_ module for more information. + .. 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 .. _sect:txn_ids: @@ -239,6 +248,9 @@ recommended. {{versions_cs_http_api}} + +.. _`CORS`: + Web Browser Clients ------------------- @@ -247,9 +259,14 @@ web browser or similar environment. In these cases, the homeserver should respon to pre-flight requests and supply Cross-Origin Resource Sharing (CORS) headers on all requests. -When a client approaches the server with a pre-flight (``OPTIONS``) request, the -server should respond with the CORS headers for that route. The recommended CORS -headers to be returned by servers on all requests are: +Servers MUST expect that clients will approach them with ``OPTIONS`` requests, +allowing clients to discover the CORS headers. All endpoints in this specification s +upport the ``OPTIONS`` method, however the server MUST NOT perform any logic defined +for the endpoints when approached with an ``OPTIONS`` request. + +When a client approaches the server with a request, the server should respond with +the CORS headers for that route. The recommended CORS headers to be returned by +servers on all requests are: .. code:: @@ -286,13 +303,17 @@ In this section, the following terms are used with specific meanings: ``FAIL_ERROR`` Inform the user that auto-discovery did not return any usable URLs. Do not continue further with the current login process. At this point, valid data - was obtained, but no homeserver is available to serve the client. No further + was obtained, but no server is available to serve the client. No further guess should be attempted and the user should make a conscientious decision what to do next. Well-known URI ~~~~~~~~~~~~~~ +.. Note:: + Servers hosting the ``.well-known`` JSON file SHOULD offer CORS headers, as + per the `CORS`_ section in this specification. + The ``.well-known`` method uses a JSON file at a predetermined location to specify parameter values. The flow for this method is as follows: @@ -403,8 +424,10 @@ an additional stage. This exchange continues until the final success. For each endpoint, a server offers one or more 'flows' that the client can use to authenticate itself. Each flow comprises a series of stages, as described -above. The client is free to choose which flow it follows. When all stages in a -flow are complete, authentication is complete and the API call succeeds. +above. The client is free to choose which flow it follows, however the flow's +stages must be completed in order. Failing to follow the flows in order must +result in an HTTP 401 response, as defined below. When all stages in a flow +are complete, authentication is complete and the API call succeeds. User-interactive API in the REST API <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @@ -448,11 +471,10 @@ params presented, that type may be present as a key in this dictionary. For example, the public part of an OAuth client ID could be given here. session - This is a session identifier that the client must pass back to the home - server, if one is provided, in subsequent attempts to authenticate in the same - API call. + This is a session identifier that the client must pass back to the homeserver, + if one is provided, in subsequent attempts to authenticate in the same API call. -The client then chooses a flow and attempts to complete one of the stages. It +The client then chooses a flow and attempts to complete the first stage. It does this by resubmitting the same request with the addition of an ``auth`` key in the object that it submits. This dictionary contains a ``type`` key whose value is the name of the authentication type that the client is attempting to complete. @@ -553,7 +575,10 @@ message in the standard format. For example: } If the client has completed all stages of a flow, the homeserver performs the -API call and returns the result as normal. +API call and returns the result as normal. Completed stages cannot be retried +by clients, therefore servers must return either a 401 response with the completed +stages, or the result of the API call if all stages were completed when a client +retries a stage. Some authentication types may be completed by means other than through the Matrix client, for example, an email confirmation may be completed when the user @@ -618,6 +643,7 @@ This specification defines the following auth types: - ``m.login.recaptcha`` - ``m.login.oauth2`` - ``m.login.email.identity`` + - ``m.login.msisdn`` - ``m.login.token`` - ``m.login.dummy`` @@ -754,17 +780,17 @@ the auth code. Homeservers can choose any path for the ``redirect URI``. Once the OAuth flow has completed, the client retries the request with the session only, as above. -Email-based (identity server) -<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +Email-based (identity / homeserver) +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< :Type: ``m.login.email.identity`` :Description: Authentication is supported by authorising an email address with an identity - server. + server, or homeserver if supported. Prior to submitting this, the client should authenticate with an identity -server. After authenticating, the session information should be submitted to -the homeserver. +server (or homeserver). After authenticating, the session information should +be submitted to the homeserver. To use this authentication type, clients should submit an auth dict as follows: @@ -782,6 +808,34 @@ To use this authentication type, clients should submit an auth dict as follows: "session": "" } +Phone number/MSISDN-based (identity / homeserver) +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +:Type: + ``m.login.msisdn`` +:Description: + Authentication is supported by authorising a phone number with an identity + server, or homeserver if supported. + +Prior to submitting this, the client should authenticate with an identity +server (or homeserver). After authenticating, the session information should +be submitted to the homeserver. + +To use this authentication type, clients should submit an auth dict as follows: + +.. code:: json + + { + "type": "m.login.msisdn", + "threepidCreds": [ + { + "sid": "", + "client_secret": "", + "id_server": "" + } + ], + "session": "" + } + Dummy Auth <<<<<<<<<< :Type: @@ -789,7 +843,14 @@ Dummy Auth :Description: Dummy authentication always succeeds and requires no extra parameters. Its purpose is to allow servers to not require any form of User-Interactive - Authentication to perform a request. + Authentication to perform a request. It can also be used to differentiate + flows where otherwise one flow would be a subset of another flow. eg. if + a server offers flows ``m.login.recaptcha`` and ``m.login.recaptcha, + m.login.email.identity`` and the client completes the recaptcha stage first, + the auth would succeed with the former flow, even if the client was intending + to then complete the email auth stage. A server can instead send flows + ``m.login.recaptcha, m.login.dummy`` and ``m.login.recaptcha, + m.login.email.identity`` to fix the ambiguity. To use this authentication type, clients should submit an auth dict with just the type and session, if provided: @@ -1257,10 +1318,70 @@ to keep moving forwards. Filtering --------- -Filters can be created on the server and can be passed as as a parameter to APIs +Filters can be created on the server and can be passed as a parameter to APIs which return events. These filters alter the data returned from those APIs. Not all APIs accept filters. +Lazy-loading room members +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Membership events often take significant resources for clients to track. In an +effort to reduce the number of resources used, clients can enable "lazy-loading" +for room members. By doing this, servers will attempt to only send membership events +which are relevant to the client. + +It is important to understand that lazy-loading is not intended to be a +perfect optimisation, and that it may not be practical for the server to +calculate precisely which membership events are relevant to the client. As a +result, it is valid for the server to send redundant membership events to the +client to ease implementation, although such redundancy should be minimised +where possible to conserve bandwidth. + +In terms of filters, lazy-loading is enabled by enabling ``lazy_load_members`` +on a ``RoomEventFilter`` (or a ``StateFilter`` in the case of ``/sync`` only). +When enabled, lazy-loading aware endpoints (see below) will only include +membership events for the ``sender`` of events being included in the response. +For example, if a client makes a ``/sync`` request with lazy-loading enabled, +the server will only return membership events for the ``sender`` of events in +the timeline, not all members of a room. + +When processing a sequence of events (e.g. by looping on ``/sync`` or +paginating ``/messages``), it is common for blocks of events in the sequence +to share a similar set of senders. Rather than responses in the sequence +sending duplicate membership events for these senders to the client, the +server MAY assume that clients will remember membership events they have +already been sent, and choose to skip sending membership events for members +whose membership has not changed. These are called 'redundant membership +events'. Clients may request that redundant membership events are always +included in responses by setting ``include_redundant_members`` to true in the +filter. + +The expected pattern for using lazy-loading is currently: + +* Client performs an initial /sync with lazy-loading enabled, and receives + only the membership events which relate to the senders of the events it + receives. +* Clients which support display-name tab-completion or other operations which + require rapid access to all members in a room should call /members for the + currently selected room, with an ``?at`` parameter set to the /sync + response's from token. The member list for the room is then maintained by + the state in subsequent incremental /sync responses. +* Clients which do not support tab-completion may instead pull in profiles for + arbitrary users (e.g. read receipts, typing notifications) on demand by + querying the room state or ``/profile``. + +.. TODO-spec + This implies that GET /state should also take an ``?at`` param + +The current endpoints which support lazy-loading room members are: + +* |/sync|_ +* |/rooms//messages|_ +* |/rooms/{roomId}/context/{eventId}|_ + +API endpoints +~~~~~~~~~~~~~ + {{filter_cs_http_api}} Events @@ -1314,6 +1435,90 @@ the event ``type`` key SHOULD follow the Java package naming convention, e.g. ``com.example.myapp.event``. This ensures event types are suitably namespaced for each application and reduces the risk of clashes. +.. Note:: + Events are not limited to the types defined in this specification. New or custom + event types can be created on a whim using the Java package naming convention. + For example, a ``com.example.game.score`` event can be sent by clients and other + clients would receive it through Matrix, assuming the client has access to the + ``com.example`` namespace. + +Note that the structure of these events may be different than those in the +server-server API. + +{{common_event_fields}} + +{{common_room_event_fields}} + +.. This is normally where we'd put the common_state_event_fields variable for the +.. magic table of what makes up a state event, however the table is verbose in our +.. custom rendering of swagger. To combat this, we just hardcode this particular +.. table. + +State Event Fields +++++++++++++++++++ + +In addition to the fields of a Room Event, State Events have the following fields. + ++--------------+--------------+-------------------------------------------------------------+ +| Key | Type | Description | ++==============+==============+=============================================================+ +| state_key | string | **Required.** A unique key which defines the overwriting | +| | | semantics for this piece of room state. This value is often | +| | | a zero-length string. The presence of this key makes this | +| | | event a State Event. State keys starting with an ``@`` are | +| | | reserved for referencing user IDs, such as room members. | +| | | With the exception of a few events, state events set with | +| | | a given user's ID as the state key MUST only be set by that | +| | | user. | ++--------------+--------------+-------------------------------------------------------------+ +| prev_content | EventContent | Optional. The previous ``content`` for this event. If there | +| | | is no previous content, this key will be missing. | ++--------------+--------------+-------------------------------------------------------------+ + + +Size limits +~~~~~~~~~~~ + +The complete event MUST NOT be larger than 65535 bytes, when formatted as a +`PDU for the Server-Server protocol <../server_server/%SERVER_RELEASE_LABEL%#pdus>`_, +including any signatures, and encoded as `Canonical JSON`_. + +There are additional restrictions on sizes per key: + +- ``sender`` MUST NOT exceed 255 bytes (including domain). +- ``room_id`` MUST NOT exceed 255 bytes. +- ``state_key`` MUST NOT exceed 255 bytes. +- ``type`` MUST NOT exceed 255 bytes. +- ``event_id`` MUST NOT exceed 255 bytes. + +Some event types have additional size restrictions which are specified in +the description of the event. Additional keys have no limit other than that +implied by the total 65 KB limit on events. + +.. _`Canonical JSON`: ../appendices.html#canonical-json + +Room Events +~~~~~~~~~~~ +.. NOTE:: + This section is a work in progress. + +This specification outlines several standard event types, all of which are +prefixed with ``m.`` + +{{m_room_aliases_event}} + +{{m_room_canonical_alias_event}} + +{{m_room_create_event}} + +{{m_room_join_rules_event}} + +{{m_room_member_event}} + +{{m_room_power_levels_event}} + +{{m_room_redaction_event}} + Syncing ~~~~~~~ @@ -1493,7 +1698,6 @@ the following list: - ``room_id`` - ``sender`` - ``state_key`` -- ``prev_content`` - ``content`` - ``hashes`` - ``signatures`` @@ -1525,6 +1729,16 @@ property of the redacted event, under the ``redacted_because`` key. When a client receives a redaction event it should change the redacted event in the same way a server does. +.. NOTE:: + + Redacted events can still affect the state of the room. When redacted, + state events behave as though their properties were simply not specified, + except those protected by the redaction algorithm. For example, + a redacted ``join`` event will still result in the user being considered joined. + Similarly, a redacted topic does not necessarily cause the topic to revert to + what is was prior to the event - it causes the topic to be removed from the room. + + Events ++++++ diff --git a/specification/events.rst b/specification/events.rst deleted file mode 100644 index c5e4a288..00000000 --- a/specification/events.rst +++ /dev/null @@ -1,73 +0,0 @@ -.. Copyright 2016 OpenMarket 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. - -Event Structure -=============== - -All communication in Matrix is expressed in the form of data objects called -Events. These are the fundamental building blocks common to the client-server, -server-server and application-service APIs, and are described below. - -Note that the structure of these events may be different than those in the -server-server API. - -{{common_event_fields}} - -{{common_room_event_fields}} - -{{common_state_event_fields}} - - -Size limits ------------ - -The complete event MUST NOT be larger than 65535 bytes, when formatted as a -`PDU for the Server-Server protocol <../server_server/%SERVER_RELEASE_LABEL%#pdus>`_, -including any signatures, and encoded as `Canonical JSON`_. - -There are additional restrictions on sizes per key: - -- ``sender`` MUST NOT exceed 255 bytes (including domain). -- ``room_id`` MUST NOT exceed 255 bytes. -- ``state_key`` MUST NOT exceed 255 bytes. -- ``type`` MUST NOT exceed 255 bytes. -- ``event_id`` MUST NOT exceed 255 bytes. - -Some event types have additional size restrictions which are specified in -the description of the event. Additional keys have no limit other than that -implied by the total 65 KB limit on events. - -Room Events ------------ -.. NOTE:: - This section is a work in progress. - -This specification outlines several standard event types, all of which are -prefixed with ``m.`` - -{{m_room_aliases_event}} - -{{m_room_canonical_alias_event}} - -{{m_room_create_event}} - -{{m_room_join_rules_event}} - -{{m_room_member_event}} - -{{m_room_power_levels_event}} - -{{m_room_redaction_event}} - -.. _`Canonical JSON`: ../appendices.html#canonical-json diff --git a/specification/feature_profiles.rst b/specification/feature_profiles.rst index c6b8ef4c..bb638380 100644 --- a/specification/feature_profiles.rst +++ b/specification/feature_profiles.rst @@ -1,4 +1,5 @@ .. Copyright 2016 OpenMarket Ltd +.. Copyright 2019 The Matrix.org Foundation C.I.C. .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -32,33 +33,67 @@ Summary Module / Profile Web Mobile Desktop CLI Embedded ===================================== ========== ========== ========== ========== ========== `Instant Messaging`_ Required Required Required Required Optional + `Direct Messaging`_ Required Required Required Required Optional + `Mentions`_ Required Required Required Optional Optional `Presence`_ Required Required Required Required Optional `Push Notifications`_ Optional Required Optional Optional Optional `Receipts`_ Required Required Required Required Optional + `Fully read markers`_ Optional Optional Optional Optional Optional `Typing Notifications`_ Required Required Required Required Optional `VoIP`_ Required Required Required Optional Optional + `Ignoring Users`_ Required Required Required Optional Optional + `Reporting Content`_ Optional Optional Optional Optional Optional `Content Repository`_ Required Required Required Optional Optional `Managing History Visibility`_ Required Required Required Required Optional `Server Side Search`_ Optional Optional Optional Optional Optional + `Room Upgrades`_ Required Required Required Required Optional `Server Administration`_ Optional Optional Optional Optional Optional `Event Context`_ Optional Optional Optional Optional Optional `Third Party Networks`_ Optional Optional Optional Optional Optional + `Send-to-Device Messaging`_ Optional Optional Optional Optional Optional + `Device Management`_ Optional Optional Optional Optional Optional + `End-to-End Encryption`_ Optional Optional Optional Optional Optional + `Guest Accounts`_ Optional Optional Optional Optional Optional + `Room Previews`_ Optional Optional Optional Optional Optional + `Client Config`_ Optional Optional Optional Optional Optional + `SSO Login`_ Optional Optional Optional Optional Optional + `OpenID`_ Optional Optional Optional Optional Optional + `Stickers`_ Optional Optional Optional Optional Optional + `Server ACLs`_ Optional Optional Optional Optional Optional + `Server Notices`_ Optional Optional Optional Optional Optional ===================================== ========== ========== ========== ========== ========== *Please see each module for more details on what clients need to implement.* .. _Instant Messaging: `module:im`_ +.. _Direct Messaging: `module:dm`_ +.. _Mentions: `module:mentions`_ .. _Presence: `module:presence`_ .. _Push Notifications: `module:push`_ .. _Receipts: `module:receipts`_ +.. _Fully read markers: `module:read-markers`_ .. _Typing Notifications: `module:typing`_ .. _VoIP: `module:voip`_ +.. _Ignoring Users: `module:ignore_users`_ +.. _Reporting Content: `module:report_content`_ .. _Content Repository: `module:content`_ .. _Managing History Visibility: `module:history-visibility`_ .. _Server Side Search: `module:search`_ +.. _Room Upgrades: `module:room-upgrades`_ .. _Server Administration: `module:admin`_ .. _Event Context: `module:event-context`_ .. _Third Party Networks: `module:third-party-networks`_ +.. _Send-to-Device Messaging: `module:to_device`_ +.. _Device Management: `module:device-management`_ +.. _End-to-End Encryption: `module:e2e`_ +.. _Guest Accounts: `module:guest-access`_ +.. _Room Previews: `module:room-previews`_ +.. _Client Config: `module:account_data`_ +.. _SSO Login: `module:sso_login`_ +.. _OpenID: `module:openid`_ +.. _Stickers: `module:stickers`_ +.. _Server ACLs: `module:server-acls`_ +.. Server Notices already has a link elsewhere. Clients ------- @@ -110,4 +145,3 @@ This is a client which is typically running on an embedded device such as a kettle, fridge or car. These clients tend to perform a few operations and run in a resource constrained environment. Like embedded applications, they are not intended to be fully-fledged communication systems. - diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 88c7a8ab..f8211f70 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -51,11 +51,14 @@ 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.2.0 `_ - `r0.1.0 `_ General principles ------------------ +.. TODO: TravisR - Define auth for IS v2 API in a future PR + The purpose of an identity server is to validate, store, and answer questions about the identities of users. In particular, it stores associations of the form "identifier X represents the same user as identifier Y", where identities may @@ -175,6 +178,8 @@ Status check {{ping_is_http_api}} +{{v2_ping_is_http_api}} + Key management -------------- @@ -194,11 +199,15 @@ service's long-term keys. {{pubkey_is_http_api}} +{{v2_pubkey_is_http_api}} + Association lookup ------------------ {{lookup_is_http_api}} +.. TODO: TravisR - Add v2 lookup API in future PR + Establishing associations ------------------------- @@ -242,16 +251,22 @@ Email associations {{email_associations_is_http_api}} +{{v2_email_associations_is_http_api}} + Phone number associations ~~~~~~~~~~~~~~~~~~~~~~~~~ {{phone_associations_is_http_api}} +{{v2_phone_associations_is_http_api}} + General ~~~~~~~ {{associations_is_http_api}} +{{v2_associations_is_http_api}} + Invitation storage ------------------ @@ -266,6 +281,8 @@ long-term private key for the identity server. {{store_invite_is_http_api}} +{{v2_store_invite_is_http_api}} + Ephemeral invitation signing ---------------------------- @@ -276,7 +293,9 @@ this isn't possible. {{invitation_signing_is_http_api}} +{{v2_invitation_signing_is_http_api}} + .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 .. _`3PID Types`: ../appendices.html#pid-types .. _`Signing JSON`: ../appendices.html#signing-json -.. _`/3pid/onbind`: ../server_server/r0.1.1.html#put-matrix-federation-v1-3pid-onbind +.. _`/3pid/onbind`: ../server_server/%SERVER_RELEASE_LABEL%.html#put-matrix-federation-v1-3pid-onbind diff --git a/specification/index.rst b/specification/index.rst index 19c5ad22..bf554e4a 100644 --- a/specification/index.rst +++ b/specification/index.rst @@ -50,16 +50,34 @@ one of the above APIs. The `Matrix Client-Server API Swagger Viewer `_ is useful for browsing the Client-Server API. + +Matrix versions +~~~~~~~~~~~~~~~ + +.. Note:: + As of June 10th 2019, the Matrix specification is considered out of beta - + indicating that all currently released APIs are considered stable and secure + to the best of our knowledge, and the spec should contain the complete + information necessary to develop production-grade implementations of Matrix + without the need for external reference. + +Matrix 1.0 (released June 10th, 2019) consists of the following minimum API +versions: + +======================= ======= +API/Specification Version +======================= ======= +Client-Server API r0.5.0 +Server-Server API r0.1.2 +Application Service API r0.1.1 +Identity Service API r0.1.1 +Push Gateway API r0.1.0 +Room Version v5 +======================= ======= + + Introduction to the Matrix APIs ------------------------------- -.. WARNING:: - The Matrix specification is still evolving: the APIs are not yet frozen - and this document is in places a work in progress or stale. We have made every - effort to clearly flag areas which are still being finalised. - We're publishing it at this point because it's complete enough to be more than - useful and provide a canonical reference to how Matrix is evolving. Our end - goal is to mirror WHATWG's `Living Standard - `_. Matrix is a set of open APIs for open-federated Instant Messaging (IM), Voice over IP (VoIP) and Internet of Things (IoT) communication, designed to create @@ -348,6 +366,12 @@ pushed over federation to the participating servers in a room, currently using full mesh topology. Servers may also request backfill of events over federation from the other servers participating in a room. +.. Note:: + Events are not limited to the types defined in this specification. New or custom + event types can be created on a whim using the Java package naming convention. + For example, a ``com.example.game.score`` event can be sent by clients and other + clients would receive it through Matrix, assuming the client has access to the + ``com.example`` namespace. Room Aliases ++++++++++++ @@ -425,6 +449,37 @@ 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? + +Common concepts +--------------- + +Various things are common throughout all of the Matrix APIs. They are +documented here. + +.. TODO: Some words about trailing slashes. See https://github.com/matrix-org/matrix-doc/issues/2107 + +Namespacing +~~~~~~~~~~~ + +Namespacing helps prevent conflicts between multiple applications and the specification +itself. Where namespacing is used, ``m.`` prefixes are used by the specification to +indicate that the field is controlled by the specification. Custom or non-specified +namespaces used in the wild MUST use the Java package naming convention to prevent +conflicts. + +As an example, event types defined in the specification are namespaced under the +special ``m.`` prefix, however any client can send a custom event type, such as +``com.example.game.score`` (assuming the client has rights to the ``com.example`` +namespace) without needing to put the event into the ``m.`` namespace. + +Timestamps +~~~~~~~~~~ + +Unless otherwise stated, timestamps are measured as milliseconds since the Unix epoch. +Throughout the specification this may be referred to as POSIX, Unix, or just "time in +milliseconds". + + .. _`room versions`: Room Versions @@ -485,7 +540,7 @@ 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 +running a stable version. Servers SHOULD use room version 4 as the default room version when creating new rooms. The available room versions are: @@ -493,6 +548,8 @@ 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. +* `Version 4 `_ - **Stable**. Builds on v3 by using URL-safe base64 for event IDs. +* `Version 5 `_ - **Stable**. Introduces enforcement of signing key validity periods. Specification Versions ---------------------- diff --git a/specification/modules.rst b/specification/modules.rst index 36f79cfd..1bc88445 100644 --- a/specification/modules.rst +++ b/specification/modules.rst @@ -1,4 +1,5 @@ .. Copyright 2016 OpenMarket Ltd +.. Copyright 2019 The Matrix.org Foundation C.I.C. .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -15,3 +16,12 @@ Modules ======= +Modules are parts of the Client-Server API which are not universal to all +endpoints. Modules are strictly defined within this specification and +should not be mistaken for experimental extensions or optional features. +A compliant server implementation MUST support all modules and supporting +specification (unless the implementation only targets clients of certain +profiles, in which case only the required modules for those feature profiles +MUST be implemented). A compliant client implementation MUST support all +the required modules and supporting specification for the `Feature Profile <#feature-profiles>`_ +it targets. diff --git a/specification/modules/_template.rst b/specification/modules/_template.rst index aa4f93db..d1fef7f5 100644 --- a/specification/modules/_template.rst +++ b/specification/modules/_template.rst @@ -15,6 +15,7 @@ Module Heading ============== +.. NOTE: Prefer to identify-modules-with-dashes despite historical examples. .. _module:short-name: A short summary of the module. What features does this module provide? An anchor @@ -67,4 +68,3 @@ This includes privacy leaks: for example leaking presence info. How do misbehaving clients or servers impact this module? This section should always be included, if only to say "we've thought about it but there isn't anything to do here". - diff --git a/specification/modules/content_repo.rst b/specification/modules/content_repo.rst index e7bdb044..1e3d1866 100644 --- a/specification/modules/content_repo.rst +++ b/specification/modules/content_repo.rst @@ -1,4 +1,5 @@ .. Copyright 2016 OpenMarket Ltd +.. Copyright 2019 The Matrix.org Foundation C.I.C. .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -17,26 +18,35 @@ Content repository .. _module:content: -This module allows users to upload content to their homeserver which is -retrievable from other homeservers. Its' purpose is to allow users to share -attachments in a room. Content locations are represented as Matrix Content (MXC) -URIs. They look like:: +The content repository (or "media repository") allows users to upload +files to their homeserver for later user. For example, files which the +user wants to send to a room would be uploaded here, as would an avatar +the user wants to use. + +Uploads are POSTed to a resource on the user's local homeserver which +returns a MXC URI which can later be used to GET the download. Content +is downloaded from the recipient's local homeserver, which must first +transfer the content from the origin homeserver using the same API +(unless the origin and destination homeservers are the same). + +When serving content, the server SHOULD provide a ``Content-Security-Policy`` +header. The recommended policy is ``sandbox; default-src 'none'; script-src +'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src +'self';``. + +Matrix Content (MXC) URIs +------------------------- + +.. _`MXC URI`: + +Content locations are represented as Matrix Content (MXC) URIs. They look +like:: mxc:/// : The name of the homeserver where this content originated, e.g. matrix.org : An opaque ID which identifies the content. -Uploads are POSTed to a resource on the user's local homeserver which returns a -token which is used to GET the download. Content is downloaded from the -recipient's local homeserver, which must first transfer the content from the -origin homeserver using the same API (unless the origin and destination -homeservers are the same). - -When serving content, the server SHOULD provide a ``Content-Security-Policy`` -header. The recommended policy is ``sandbox; default-src 'none'; script-src -'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src -'self';``. Client behaviour ---------------- @@ -47,6 +57,11 @@ Clients can upload and download content using the following HTTP APIs. Thumbnails ~~~~~~~~~~ +The homeserver SHOULD be able to supply thumbnails for uploaded images and +videos. The exact file types which can be thumbnailed are not currently +specified - see `Issue #1938 `_ +for more information. + The thumbnail methods are "crop" and "scale". "scale" tries to return an image where either the width or the height is smaller than the requested size. The client should then scale and letterbox the image if it needs to @@ -55,18 +70,29 @@ width and height are close to the requested size and the aspect matches the requested size. The client should scale the image if it needs to fit within a given rectangle. +The dimensions given to the thumbnail API are the minimum size the client +would prefer. Servers must never return thumbnails smaller than the client's +requested dimensions, unless the content being thumbnailed is smaller than +the dimensions. When the content is smaller than the requested dimensions, +servers should return the original content rather than thumbnail it. + +Servers SHOULD produce thumbnails with the following dimensions and methods: + +* 32x32, crop +* 96x96, crop +* 320x240, scale +* 640x480, scale +* 800x600, scale + In summary: * "scale" maintains the original aspect ratio of the image * "crop" provides an image in the aspect ratio of the sizes given in the request + * The server will return an image larger than or equal to the dimensions requested + where possible. -Server behaviour ----------------- - -Homeservers may generate thumbnails for content uploaded to remote -homeservers themselves or may rely on the remote homeserver to thumbnail -the content. Homeservers may return thumbnails of a different size to that -requested. However homeservers should provide exact matches where reasonable. -Homeservers must never upscale images. +Servers MUST NOT upscale thumbnails under any circumstance. Servers MUST NOT +return a smaller thumbnail than requested, unless the original content makes +that impossible. Security considerations ----------------------- @@ -88,16 +114,20 @@ UTF-8 encoded traversals, etc). Homeservers have additional content-specific concerns: - Clients may try to upload very large files. Homeservers should not store files - that are too large and should not serve them to clients. + that are too large and should not serve them to clients, returning a HTTP 413 + error with the ``M_TOO_LARGE`` code. - Clients may try to upload very large images. Homeservers should not attempt to - generate thumbnails for images that are too large. + generate thumbnails for images that are too large, returning a HTTP 413 error + with the ``M_TOO_LARGE`` code. - Remote homeservers may host very large files or images. Homeservers should not - proxy or thumbnail large files or images from remote homeservers. + proxy or thumbnail large files or images from remote homeservers, returning a + HTTP 502 error with the ``M_TOO_LARGE`` code. - Clients may try to upload a large number of files. Homeservers should limit the - number and total size of media that can be uploaded by clients. + number and total size of media that can be uploaded by clients, returning a + HTTP 403 error with the ``M_FORBIDDEN`` code. - Clients may try to access a large number of remote files through a homeserver. Homeservers should restrict the number and size of remote files that it caches. diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index 59d241b6..7758e2c1 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -1,4 +1,5 @@ .. Copyright 2016 OpenMarket Ltd +.. Copyright 2019 The Matrix.org Foundation C.I.C. .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -18,7 +19,7 @@ End-to-End Encryption .. _module:e2e: Matrix optionally supports end-to-end encryption, allowing rooms to be created -whose conversation contents is not decryptable or interceptable on any of the +whose conversation contents are not decryptable or interceptable on any of the participating homeservers. Key Distribution @@ -182,7 +183,7 @@ process: field as the ``from`` parameter. If the client is tracking the device list of any of the users listed in the response, it marks them as outdated. It combines this list with those already flagged as outdated, and initiates a - |/keys/query|_ requests for all of them. + |/keys/query|_ request for all of them. .. Warning:: @@ -383,20 +384,10 @@ man-in-the-middle. This verification process requires an out-of-band channel: there is no way to do it within Matrix without trusting the administrators of the homeservers. -In Matrix, the basic process for device verification is for Alice to verify -that the public Ed25519 signing key she received via ``/keys/query`` for Bob's -device corresponds to the private key in use by Bob's device. For now, it is -recommended that clients provide mechanisms by which the user can see: - -1. The public part of their device's Ed25519 signing key, encoded using - `unpadded Base64`_. - -2. The list of devices in use for each user in a room, along with the public - Ed25519 signing key for each device, again encoded using unpadded Base64. - -Alice can then meet Bob in person, or contact him via some other trusted -medium, and ask him to read out the Ed25519 key shown on his device. She -compares this with the value shown for his device on her client. +In Matrix, verification works by Alice meeting Bob in person, or contacting him +via some other trusted medium, and use `SAS Verification`_ to interactively +verify Bob's devices. Alice and Bob may also read aloud their unpadded base64 +encoded Ed25519 public key, as returned by ``/keys/query``. Device verification may reach one of several conclusions. For example: @@ -420,7 +411,330 @@ Device verification may reach one of several conclusions. For example: protocol to verify that a given message was sent from a device holding that Ed25519 private key, or to encrypt a message so that it may only be decrypted by such a device. For the Olm protocol, this is documented at - https://matrix.org/git/olm/about/docs/signing.rst. + https://matrix.org/docs/olm_signing.html. + + +Key verification framework +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Verifying keys manually by reading out the Ed25519 key is not very user friendly, +and can lead to errors. In order to help mitigate errors, and to make the process +easier for users, some verification methods are supported by the specification. +The methods all use a common framework for negotiating the key verification. + +To use this framework, Alice's client would send ``m.key.verification.request`` +events to Bob's devices. All of the ``to_device`` messages sent to Bob MUST have +the same ``transaction_id`` to indicate they are part of the same request. This +allows Bob to reject the request on one device, and have it apply to all of his +devices. Similarly, it allows Bob to process the verification on one device without +having to involve all of his devices. + +When Bob's device receives a ``m.key.verification.request``, it should prompt Bob +to verify keys with Alice using one of the supported methods in the request. If +Bob's device does not understand any of the methods, it should not cancel the request +as one of his other devices may support the request. Instead, Bob's device should +tell Bob that an unsupported method was used for starting key verification. The +prompt for Bob to accept/reject Alice's request (or the unsupported method prompt) +should be automatically dismissed 10 minutes after the ``timestamp`` field or 2 +minutes after Bob's client receives the message, whichever comes first, if Bob +does not interact with the prompt. The prompt should additionally be hidden if +an appropriate ``m.key.verification.cancel`` message is received. + +If Bob rejects the request, Bob's client must send a ``m.key.verification.cancel`` +message to Alice's device. Upon receipt, Alice's device should tell her that Bob +does not want to verify her device and send ``m.key.verification.cancel`` messages +to all of Bob's devices to notify them that the request was rejected. + +If Bob accepts the request, Bob's device starts the key verification process by +sending a ``m.key.verification.start`` message to Alice's device. Upon receipt +of this message, Alice's device should send a ``m.key.verification.cancel`` message +to all of Bob's other devices to indicate the process has been started. The start +message must use the same ``transaction_id`` from the original key verification +request if it is in response to the request. The start message can be sent indepdently +of any request. + +Individual verification methods may add additional steps, events, and properties to +the verification messages. Event types for methods defined in this specification must +be under the ``m.key.verification`` namespace and any other event types must be namespaced +according to the Java package naming convention. + +Any of Alice's or Bob's devices can cancel the key verification request or process +at any time with a ``m.key.verification.cancel`` message to all applicable devices. + +This framework yields the following handshake, assuming both Alice and Bob each have +2 devices, Bob's first device accepts the key verification request, and Alice's second +device initiates the request. Note how Alice's first device is not involved in the +request or verification process. + +:: + + +---------------+ +---------------+ +-------------+ +-------------+ + | AliceDevice1 | | AliceDevice2 | | BobDevice1 | | BobDevice2 | + +---------------+ +---------------+ +-------------+ +-------------+ + | | | | + | | m.key.verification.request | | + | |---------------------------------->| | + | | | | + | | m.key.verification.request | | + | |-------------------------------------------------->| + | | | | + | | m.key.verification.start | | + | |<----------------------------------| | + | | | | + | | m.key.verification.cancel | | + | |-------------------------------------------------->| + | | | | + + +After the handshake, the verification process begins. + +{{m_key_verification_request_event}} + +{{m_key_verification_start_event}} + +{{m_key_verification_cancel_event}} + + +.. _`SAS Verification`: + +Short Authentication String (SAS) verification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +SAS verification is a user-friendly key verification process built off the common +framework outlined above. SAS verification is intended to be a highly interactive +process for users, and as such exposes verfiication methods which are easier for +users to use. + +The verification process is heavily inspired by Phil Zimmerman's ZRTP key agreement +handshake. A key part of key agreement in ZRTP is the hash commitment: the party that +begins the Diffie-Hellman key sharing sends a hash of their part of the Diffie-Hellman +exchange, and does not send their part of the Diffie-Hellman exchange until they have +received the other party's part. Thus an attacker essentially only has one attempt to +attack the Diffie-Hellman exchange, and hence we can verify fewer bits while still +achieving a high degree of security: if we verify n bits, then an attacker has a 1 in +2\ :sup:`n` chance of success. For example, if we verify 40 bits, then an attacker has +a 1 in 1,099,511,627,776 chance (or less than 1 in 1012 chance) of success. A failed +attack would result in a mismatched Short Authentication String, alerting users to the +attack. + +The verification process takes place over `to-device`_ messages in two phases: + +1. Key agreement phase (based on `ZRTP key agreement `_). +#. Key verification phase (based on HMAC). + +The process between Alice and Bob verifying each other would be: + +.. |AlicePublicKey| replace:: :math:`K_{A}^{public}` +.. |AlicePrivateKey| replace:: :math:`K_{A}^{private}` +.. |AliceCurve25519| replace:: :math:`K_{A}^{private},K_{A}^{public}` +.. |BobPublicKey| replace:: :math:`K_{B}^{public}` +.. |BobPrivateKey| replace:: :math:`K_{B}^{private}` +.. |BobCurve25519| replace:: :math:`K_{B}^{private},K_{B}^{public}` +.. |BobAliceCurve25519| replace:: :math:`K_{B}^{private}K_{A}^{public}` +.. |AliceBobECDH| replace:: :math:`ECDH(K_{A}^{private},K_{B}^{public})` + +1. Alice and Bob establish a secure out-of-band connection, such as meeting + in-person or a video call. "Secure" here means that either party cannot be + impersonated, not explicit secrecy. +#. Alice and Bob communicate which devices they'd like to verify with each other. +#. Alice selects Bob's device from the device list and begins verification. +#. Alice's client ensures it has a copy of Bob's device key. +#. Alice's device sends Bob's device a ``m.key.verification.start`` message. +#. Bob's device receives the message and selects a key agreement protocol, hash + algorithm, message authentication code, and SAS method supported by Alice's + device. +#. Bob's device ensures it has a copy of Alice's device key. +#. Bob's device creates an ephemeral Curve25519 key pair (|BobCurve25519|), and + calculates the hash (using the chosen algorithm) of the public key |BobPublicKey|. +#. Bob's device replies to Alice's device with a ``m.key.verification.accept`` message. +#. Alice's device receives Bob's message and stores the commitment hash for later use. +#. Alice's device creates an ephemeral Curve25519 key pair (|AliceCurve25519|) and + replies to Bob's device with a ``m.key.verification.key``, sending only the public + key |AlicePublicKey|. +#. Bob's device receives Alice's message and replies with its own ``m.key.verification.key`` + message containing its public key |BobPublicKey|. +#. Alice's device receives Bob's message and verifies the commitment hash from earlier + matches the hash of the key Bob's device just sent and the content of Alice's + ``m.key.verification.start`` message. +#. Both Alice and Bob's devices perform an Elliptic-curve Diffie-Hellman (|AliceBobECDH|), + using the result as the shared secret. +#. Both Alice and Bob's devices display a SAS to their users, which is derived + from the shared key using one of the methods in this section. If multiple SAS + methods are available, clients should allow the users to select a method. +#. Alice and Bob compare the strings shown by their devices, and tell their devices if + they match or not. +#. Assuming they match, Alice and Bob's devices calculate the HMAC of their own device keys + and a comma-separated sorted list of of the key IDs that they wish the other user + to verify, using SHA-256 as the hash function. HMAC is defined in [RFC 2104](https://tools.ietf.org/html/rfc2104). + The key for the HMAC is different for each item and is calculated by generating + 32 bytes (256 bits) using `the key verification HKDF <#SAS-HKDF>`_. +#. Alice's device sends Bob's device a ``m.key.verification.mac`` message containing the + MAC of Alice's device keys and the MAC of her key IDs to be verified. Bob's device does + the same for Bob's device keys and key IDs concurrently with Alice. +#. When the other device receives the ``m.key.verification.mac`` message, the device + calculates the HMAC of its copies of the other device's keys given in the message, + as well as the HMAC of the comma-seperated, sorted, list of key IDs in the message. + The device compares these with the HMAC values given in the message, and if everything + matches then the device keys are verified. + +The wire protocol looks like the following between Alice and Bob's devices:: + + +-------------+ +-----------+ + | AliceDevice | | BobDevice | + +-------------+ +-----------+ + | | + | m.key.verification.start | + |-------------------------------->| + | | + | m.key.verification.accept | + |<--------------------------------| + | | + | m.key.verification.key | + |-------------------------------->| + | | + | m.key.verification.key | + |<--------------------------------| + | | + | m.key.verification.mac | + |-------------------------------->| + | | + | m.key.verification.mac | + |<--------------------------------| + | | + +Error and exception handling +<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +At any point the interactive verfication can go wrong. The following describes what +to do when an error happens: + +* Alice or Bob can cancel the verification at any time. A ``m.key.verification.cancel`` + message must be sent to signify the cancellation. +* The verification can time out. Clients should time out a verification that does not + complete within 10 minutes. Additionally, clients should expire a ``transaction_id`` + which goes unused for 10 minutes after having last sent/received it. The client should + inform the user that the verification timed out, and send an appropriate + ``m.key.verification.cancel`` message to the other device. +* When the same device attempts to intiate multiple verification attempts, the receipient + should cancel all attempts with that device. +* When a device receives an unknown ``transaction_id``, it should send an appropriate + ``m.key.verfication.cancel`` message to the other device indicating as such. This + does not apply for inbound ``m.key.verification.start`` or ``m.key.verification.cancel`` + messages. +* If the two devices do not share a common key share, hash, HMAC, or SAS method then + the device should notify the other device with an appropriate ``m.key.verification.cancel`` + message. +* If the user claims the Short Authentication Strings do not match, the device should + send an appropriate ``m.key.verification.cancel`` message to the other device. +* If the device receives a message out of sequence or that it was not expecting, it should + notify the other device with an appropriate ``m.key.verification.cancel`` message. + + +Verification messages specific to SAS +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +Building off the common framework, the following events are involved in SAS verification. + +The ``m.key.verification.cancel`` event is unchanged, however the following error codes +are used in addition to those already specified: + +* ``m.unknown_method``: The devices are unable to agree on the key agreement, hash, MAC, + or SAS method. +* ``m.mismatched_commitment``: The hash commitment did not match. +* ``m.mismatched_sas``: The SAS did not match. + + +{{m_key_verification_start_m_sas_v1_event}} + +{{m_key_verification_accept_event}} + +{{m_key_verification_key_event}} + +{{m_key_verification_mac_event}} + + +.. _`SAS-HKDF`: + +HKDF calculation +<<<<<<<<<<<<<<<< + +In all of the SAS methods, HKDF is as defined in [RFC 5869](https://tools.ietf.org/html/rfc5869) +and uses the previously agreed-upon hash function for the hash function. The shared +secret is supplied as the input keying material. No salt is used, and the input +parameter is the concatenation of: + + * The string ``MATRIX_KEY_VERIFICATION_SAS``. + * The Matrix ID of the user who sent the ``m.key.verification.start`` message. + * The Device ID of the device which sent the ``m.key.verification.start`` message. + * The Matrix ID of the user who sent the ``m.key.verification.accept`` message. + * The Device ID of the device which sent the ``m.key.verification.accept`` message. + * The ``transaction_id`` being used. + +.. admonition:: Rationale + + HKDF is used over the plain shared secret as it results in a harder attack + as well as more uniform data to work with. + +For verification of each party's device keys, HKDF is as defined in RFC 5869 and +uses SHA-256 as the hash function. The shared secret is supplied as the input keying +material. No salt is used, and in the input parameter is the concatenation of: + + * The string ``MATRIX_KEY_VERIFICATION_MAC``. + * The Matrix ID of the user whose key is being MAC-ed. + * The Device ID of the device sending the MAC. + * The Matrix ID of the other user. + * The Device ID of the device receiving the MAC. + * The ``transaction_id`` being used. + * The Key ID of the key being MAC-ed, or the string ``KEY_IDS`` if the item + being MAC-ed is the list of key IDs. + +SAS method: ``decimal`` +<<<<<<<<<<<<<<<<<<<<<<< + +Generate 5 bytes using `HKDF <#SAS-HKDF>`_ then take sequences of 13 bits to +convert to decimal numbers (resulting in 3 numbers between 0 and 8191 inclusive +each). Add 1000 to each calculated number. + +The bitwise operations to get the numbers given the 5 bytes +:math:`B_{0}, B_{1}, B_{2}, B_{3}, B_{4}` would be: + +* First: :math:`(B_{0} \ll 5 | B_{1} \gg 3) + 1000` +* Second: :math:`((B_{1} \& 0x7) \ll 10 | B_{2} \ll 2 | B_{3} \gg 6) + 1000` +* Third: :math:`((B_{3} \& 0x3F) \ll 7 | B_{4} \gg 1) + 1000` + +The digits are displayed to the user either with an appropriate separator, +such as dashes, or with the numbers on individual lines. + +SAS method: ``emoji`` +<<<<<<<<<<<<<<<<<<<<< + +Generate 6 bytes using `HKDF <#SAS-HKDF>`_ then split the first 42 bits into +7 groups of 6 bits, similar to how one would base64 encode something. Convert +each group of 6 bits to a number and use the following table to get the corresponding +emoji: + +{{sas_emoji_table}} + +.. Note:: + This table is available as JSON at + https://github.com/matrix-org/matrix-doc/blob/master/data-definitions/sas-emoji.json + +.. admonition:: Rationale + + The emoji above were chosen to: + + * Be recognisable without colour. + * Be recognisable at a small size. + * Be recognisable by most cultures. + * Be distinguishable from each other. + * Easily described by a few words. + * Avoid symbols with negative connotations. + * Be likely similar across multiple platforms. + +Clients SHOULD show the emoji with the descriptions from the table, or appropriate +translation of those descriptions. Client authors SHOULD collaborate to create a +common set of translations for all languages. + .. section name changed, so make sure that old links keep working .. _key-sharing: @@ -442,8 +756,8 @@ sending `m.room_key_request`_ to-device messages to other devices with device, it can forward the keys to the first device by sending an encrypted `m.forwarded_room_key`_ to-device message. The first device should then send an `m.room_key_request`_ to-device message with ``action`` set to -``cancel_request`` to the other devices that it had originally sent the key -request to; a device that receives a ``cancel_request`` should disregard any +``request_cancellation`` to the other devices that it had originally sent the key +request to; a device that receives a ``request_cancellation`` should disregard any previously-received ``request`` message with the same ``request_id`` and ``requesting_device_id``. @@ -460,7 +774,7 @@ Keys can be manually exported from one device to an encrypted file, copied to another device, and imported. The file is encrypted using a user-supplied passphrase, and is created as follows: -1. Encode the sessions a JSON object, formatted as described in `Key export +1. Encode the sessions as a JSON object, formatted as described in `Key export format`_. 2. Generate a 512-bit key from the user-entered passphrase by computing `PBKDF2`_\(HMAC-SHA-512, passphrase, S, N, 512), where S is a 128-bit @@ -658,10 +972,38 @@ part of the ed25519 key it claims to have in the Olm payload. This is crucial when the ed25519 key corresponds to a verified device. If a client has multiple sessions established with another device, it should -use the session from which it last received a message. A client may expire old -sessions by defining a maximum number of olm sessions that it will maintain for -each device, and expiring sessions on a Least Recently Used basis. The maximum -number of olm sessions maintained per device should be at least 4. +use the session from which it last received and successfully decrypted a +message. For these purposes, a session that has not received any messages +should use its creation time as the time that it last received a message. +A client may expire old sessions by defining a maximum number of olm sessions +that it will maintain for each device, and expiring sessions on a Least Recently +Used basis. The maximum number of olm sessions maintained per device should +be at least 4. + +Recovering from undecryptable messages +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +Occasionally messages may be undecryptable by clients due to a variety of reasons. +When this happens to an Olm-encrypted message, the client should assume that the Olm +session has become corrupted and create a new one to replace it. + +.. Note:: + Megolm-encrypted messages generally do not have the same problem. Usually the key + for an undecryptable Megolm-encrypted message will come later, allowing the client + to decrypt it successfully. Olm does not have a way to recover from the failure, + making this session replacement process required. + +To establish a new session, the client sends a `m.dummy <#m-dummy>`_ to-device event +to the other party to notify them of the new session details. + +Clients should rate-limit the number of sessions it creates per device that it receives +a message from. Clients should not create a new session with another device if it has +already created one for that given device in the past 1 hour. + +Clients should attempt to mitigate loss of the undecryptable messages. For example, +Megolm sessions that were sent using the old session would have been lost. The client +can attempt to retrieve the lost sessions through ``m.room_key_request`` messages. + ``m.megolm.v1.aes-sha2`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -740,6 +1082,8 @@ Events {{m_forwarded_room_key_event}} +{{m_dummy_event}} + Key management API ~~~~~~~~~~~~~~~~~~ diff --git a/specification/modules/instant_messaging.rst b/specification/modules/instant_messaging.rst index 86daa700..679eabdc 100644 --- a/specification/modules/instant_messaging.rst +++ b/specification/modules/instant_messaging.rst @@ -278,70 +278,42 @@ choose a name: #. If the room has an `m.room.canonical_alias`_ state event with a non-empty ``alias`` field, use the alias given by that field as the name. -#. If neither of the above conditions are met, a name should be composed based +#. If neither of the above conditions are met, the client can optionally guess + an alias from the ``m.room.alias`` events in the room. This is a temporary + measure while clients do not promote canonical aliases as prominently. This + step may be removed in a future version of the specification. + +#. If none of the above conditions are met, a name should be composed based on the members of the room. Clients should consider `m.room.member`_ events - for users other than the logged-in user, with ``membership: join`` or - ``membership: invite``. + for users other than the logged-in user, as defined below. - .. _active_members: + i. If the number of ``m.heroes`` for the room are greater or equal to + ``m.joined_member_count + m.invited_member_count - 1``, then use the + membership events for the heroes to calculate display names for the + users (`disambiguating them if required`_) and concatenating them. For + example, the client may choose to show "Alice, Bob, and Charlie + (@charlie:example.org)" as the room name. The client may optionally + limit the number of users it uses to generate a room name. - i. If there is only one such event, the display name for the room should be - the `disambiguated display name`_ of the corresponding user. + #. If there are fewer heroes than ``m.joined_member_count + m.invited_member_count + - 1``, and ``m.joined_member_count + m.invited_member_count`` is greater + than 1, the client should use the heroes to calculate display names for + the users (`disambiguating them if required`_) and concatenating them + alongside a count of the remaining users. For example, "Alice, Bob, and + 1234 others". - #. If there are two such events, they should be lexicographically sorted by - their ``state_key`` (i.e. the corresponding user IDs), and the display - name for the room should be the `disambiguated display name`_ of both - users: " and ", or a localised variant thereof. + #. If ``m.joined_member_count + m.invited_member_count`` is less than or + equal to 1 (indicating the member is alone), the client should use the + rules above to indicate that the room was empty. For example, "Empty + Room (was Alice)", "Empty Room (was Alice and 1234 others)", or + "Empty Room" if there are no heroes. - #. If there are three or more such events, the display name for the room - should be based on the disambiguated display name of the user - corresponding to the first such event, under a lexicographical sorting - according to their ``state_key``. The display name should be in the - format " and others" (or a localised variant thereof), where N - is the number of `m.room.member`_ events with ``membership: join`` or - ``membership: invite``, excluding the logged-in user and "user1". - - For example, if Alice joins a room, where Bob (whose user id is - ``@superuser:example.com``), Carol (user id ``@carol:example.com``) and - Dan (user id ``@dan:matrix.org``) are in conversation, Alice's - client should show the room name as "Carol and 2 others". - - .. TODO-spec - Sorting by user_id certainly isn't ideal, as IDs at the start of the - alphabet will end up dominating room names: they will all be called - "Arathorn and 15 others". Furthermore - user_ids are not necessarily - ASCII, which means we need to either specify a collation order, or specify - how to choose one. - - Ideally we might sort by the time when the user was first invited to, or - first joined the room. But we don't have this information. - - See https://matrix.org/jira/browse/SPEC-267 for further discussion. - -#. If the room has no valid ``m.room.name`` or ``m.room.canonical_alias`` - event, and no active members other than the current user, clients should - consider ``m.room.member`` events with ``membership: leave``. If such events - exist, a display name such as "Empty room (was and others)" (or - a localised variant thereof) should be used, following similar rules as for - active members (see `above `_). - -#. A complete absence of room name, canonical alias, and room members is likely - to indicate a problem with creating the room or synchronising the state - table; however clients should still handle this situation. A display name - such as "Empty room" (or a localised variant thereof) should be used in this - situation. - -.. _`disambiguated display name`: `Calculating the display name for a user`_ - -Clients SHOULD NOT use `m.room.aliases`_ events as a source for room names, as -it is difficult for clients to agree on the best alias to use, and aliases can -change unexpectedly. - -.. TODO-spec - How can we make this less painful for clients to implement, without forcing - an English-language implementation on them all? See - https://matrix.org/jira/browse/SPEC-425. +Clients SHOULD internationalise the room name to the user's language when using +the ``m.heroes`` to calculate the name. Clients SHOULD use minimum 5 heroes to +calculate room names where possible, but may use more or less to fit better with +their user experience. +.. _`disambiguating them if required`: `Calculating the display name for a user`_ Forming relationships between events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/specification/modules/push.rst b/specification/modules/push.rst index 9830729a..c9489987 100644 --- a/specification/modules/push.rst +++ b/specification/modules/push.rst @@ -1,4 +1,5 @@ .. Copyright 2016 OpenMarket Ltd +.. Copyright 2019 The Matrix.org Foundation C.I.C. .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -116,6 +117,16 @@ have received. {{notifications_cs_http_api}} +Receiving notifications +~~~~~~~~~~~~~~~~~~~~~~~ + +Servers MUST include the number of unread notifications in a client's ``/sync`` +stream, and MUST update it as it changes. Notifications are determined by the +push rules which apply to an event. + +When the user updates their read receipt (either by using the API or by sending an +event), notifications prior to and including that event MUST be marked as read. + Push Rules ~~~~~~~~~~ A push rule is a single rule that states under what *conditions* an event should @@ -177,7 +188,7 @@ notification is delivered for a matching event. The following actions are define This prevents each matching event from generating a notification ``coalesce`` This enables notifications for matching events but activates homeserver - specific behaviour to intelligently coalesce multiple events into a single + specific behaviour to intelligently coalesce multiple events into a single notification. Not all homeservers may support this. Those that do not support it should treat it as the ``notify`` action. ``set_tweak`` @@ -369,6 +380,42 @@ Definition: } +``.m.rule.tombstone`` +````````````````````` +Matches any state event whose type is ``m.room.tombstone``. This is intended +to notify users of a room when it is upgraded, similar to what an +``@room`` notification would accomplish. + +Definition: + +.. code:: json + + { + "rule_id": ".m.rule.tombstone", + "default": true, + "enabled": true, + "conditions": [ + { + "kind": "event_match", + "key": "type", + "pattern": "m.room.tombstone" + }, + { + "kind": "event_match", + "key": "state_key", + "pattern": "" + } + ], + "actions": [ + "notify", + { + "set_tweak": "highlight", + "value": true + } + ] + } + + ``.m.rule.roomnotif`` ````````````````````` Matches any message whose content is unencrypted and contains the @@ -521,6 +568,11 @@ Definition: { "kind": "room_member_count", "is": "2" + }, + { + "kind": "event_match", + "key": "type", + "pattern": "m.room.message" } ], "actions": [ @@ -599,7 +651,7 @@ Definition: Conditions ++++++++++ -Override, Underride and Default Rules MAY have a list of 'conditions'. +Override, Underride and Default Rules MAY have a list of 'conditions'. All conditions must hold true for an event in order to apply the ``action`` for the event. A rule with no conditions always matches. Room, Sender, User and Content rules do not have conditions in the same way, but instead have diff --git a/specification/modules/receipts.rst b/specification/modules/receipts.rst index faba7b62..4630091f 100644 --- a/specification/modules/receipts.rst +++ b/specification/modules/receipts.rst @@ -26,7 +26,8 @@ to a homeserver. To prevent this from becoming a problem, receipts are implement using "up to" markers. This marker indicates that the acknowledgement applies to all events "up to and including" the event specified. For example, marking an event as "read" would indicate that the user had read all events *up to* the -referenced event. +referenced event. See the `Receiving notifications <#receiving-notifications>`_ +section for more information on how read receipts affect notification counts. Events ------ @@ -60,7 +61,8 @@ Clients should send read receipts when there is some certainty that the event in question has been **displayed** to the user. Simply receiving an event does not provide enough certainty that the user has seen the event. The user SHOULD need to *take some action* such as viewing the room that the event was sent to or -dismissing a notification in order for the event to count as "read". +dismissing a notification in order for the event to count as "read". Clients +SHOULD NOT send read receipts for events sent by their own user. A client can update the markers for its user by interacting with the following HTTP APIs. @@ -94,4 +96,3 @@ Security considerations As receipts are sent outside the context of the event graph, there are no integrity checks performed on the contents of ``m.receipt`` events. - diff --git a/specification/modules/send_to_device.rst b/specification/modules/send_to_device.rst index 86288546..7ab622bc 100644 --- a/specification/modules/send_to_device.rst +++ b/specification/modules/send_to_device.rst @@ -63,7 +63,7 @@ If the client sends messages to users on remote domains, those messages should be sent on to the remote servers via `federation`_. -.. _`federation`: ../server_server/latest.html#send-to-device-messaging +.. _`federation`: ../server_server/%SERVER_RELEASE_LABEL%.html#send-to-device-messaging .. TODO-spec: @@ -108,7 +108,7 @@ to_device ToDevice Optional. Information on the send-to-device messages ========= ========= ============================================= Parameter Type Description ========= ========= ============================================= -events [Event] List of send-to-device messages +events [Event] List of send-to-device messages. ========= ========= ============================================= ``Event`` diff --git a/specification/modules/server_notices.rst b/specification/modules/server_notices.rst new file mode 100644 index 00000000..63b7bfc5 --- /dev/null +++ b/specification/modules/server_notices.rst @@ -0,0 +1,78 @@ +.. Copyright 2019 The Matrix.org Foundation C.I.C. +.. +.. Licensed under the Apache License, Version 2.0 (the "License"); +.. you may not use this file except in compliance with the License. +.. You may obtain a copy of the License at +.. +.. http://www.apache.org/licenses/LICENSE-2.0 +.. +.. Unless required by applicable law or agreed to in writing, software +.. distributed under the License is distributed on an "AS IS" BASIS, +.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +.. See the License for the specific language governing permissions and +.. limitations under the License. + +Server Notices +============== + +.. _module:server-notices: + +Homeserver hosts often want to send messages to users in an official capacity, +or have resource limits which affect a user's ability to use the homeserver. +For example, the homeserver may be limited to a certain number of active users +per month and has exceeded that limit. To communicate this failure to users, +the homeserver would use the Server Notices room. + +The aesthetics of the room (name, topic, avatar, etc) are left as an implementation +detail. It is recommended that the homeserver decorate the room such that it looks +like an official room to users. + +Events +------ +Notices are sent to the client as normal ``m.room.message`` events with a +``msgtype`` of ``m.server_notice`` in the server notices room. Events with +a ``m.server_notice`` ``msgtype`` outside of the server notice room must +be ignored by clients. + +The specified values for ``server_notice_type`` are: + +:``m.server_notice.usage_limit_reached``: + The server has exceeded some limit which requires the server administrator + to intervene. The ``limit_type`` describes the kind of limit reached. + The specified values for ``limit_type`` are: + + :``monthly_active_user``: + The server's number of active users in the last 30 days has exceeded the + maximum. New connections are being refused by the server. What defines + "active" is left as an implementation detail, however servers are encouraged + to treat syncing users as "active". + + +{{m_room_message_m_server_notice_event}} + +Client behaviour +---------------- +Clients can identify the server notices room by the ``m.server_notice`` tag +on the room. Active notices are represented by the `pinned events <#m-room-pinned-events>`_ +in the server notices room. Server notice events pinned in that room should +be shown to the user through special UI and not through the normal pinned +events interface in the client. For example, clients may show warning banners +or bring up dialogs to get the user's attention. Events which are not server +notice events and are pinned in the server notices room should be shown just +like any other pinned event in a room. + +The client must not expect to be able to reject an invite to join the server +notices room. Attempting to reject the invite must result in a +``M_CANNOT_LEAVE_SERVER_NOTICE_ROOM`` error. Servers should not prevent the user +leaving the room after joining the server notices room, however the same error +code must be used if the server will prevent leaving the room. + +Server behaviour +---------------- +Servers should manage exactly 1 server notices room per user. Servers must +identify this room to clients with the ``m.server_notice`` tag. Servers should +invite the target user rather than automatically join them to the server notice +room. + +How servers send notices to clients, and which user they use to send the events, +is left as an implementation detail for the server. diff --git a/specification/modules/tags.rst b/specification/modules/tags.rst index a4b0becf..9d0cd554 100644 --- a/specification/modules/tags.rst +++ b/specification/modules/tags.rst @@ -34,9 +34,9 @@ The JSON object associated with each tag gives information about the tag, e.g ho to order the rooms with a given tag. Ordering information is given under the ``order`` key as a number between 0 and -1. The numbers are compared such that 0 is displayed first. Therefore a room -with an ``order`` of ``0.2`` would be displayed before a room with an ``order`` -of ``0.7``. If a room has a tag without an ``order`` key then it should appear +1. The numbers are compared such that 0 is displayed first. Therefore a room +with an ``order`` of ``0.2`` would be displayed before a room with an ``order`` +of ``0.7``. If a room has a tag without an ``order`` key then it should appear after the rooms with that tag that have an ``order`` key. The name of a tag MUST NOT exceed 255 bytes. @@ -60,6 +60,7 @@ The following tags are defined in the ``m.*`` namespace: * ``m.favourite``: The user's favourite rooms. These should be shown with higher precedence than other rooms. * ``m.lowpriority``: These should be shown with lower precedence than others. +* ``m.server_notice``: Used to identify `Server Notice Rooms <#module-server-notices>`_. {{m_tag_event}} diff --git a/specification/modules/third_party_invites.rst b/specification/modules/third_party_invites.rst index 3e11d929..282b06f4 100644 --- a/specification/modules/third_party_invites.rst +++ b/specification/modules/third_party_invites.rst @@ -38,7 +38,8 @@ When the invitee's homeserver receives the notification of the binding, it should insert an ``m.room.member`` event into the room's graph for that user, with ``content.membership`` = ``invite``, as well as a ``content.third_party_invite`` property which contains proof that the invitee -does indeed own that third party identifier. +does indeed own that third party identifier. See the `m.room.member <#m-room-member>`_ +schema for more information. Events @@ -254,4 +255,4 @@ these is left to the implementer's discretion. -.. _`identity server /isvalid`: ../identity_service/unstable.html#get-matrix-identity-api-v1-pubkey-isvalid +.. _`identity server /isvalid`: ../identity_service/%IDENTITY_RELEASE_LABEL%.html#get-matrix-identity-api-v1-pubkey-isvalid diff --git a/specification/proposals.rst b/specification/proposals.rst index 371850ab..94878f80 100644 --- a/specification/proposals.rst +++ b/specification/proposals.rst @@ -1,6 +1,6 @@ Tables of Tracked Proposals --------------------------- -This file is autogenerated by a jenkins build process +This file is generated by an automated process on our build server. -View the current live version `at https://matrix.org/docs/spec/proposals `_ +View the current live version `at https://matrix.org/docs/spec/proposals `_. diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst index d95128b7..de65ba27 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -13,16 +13,20 @@ Proposals for Spec Changes to Matrix If you are interested in submitting a change to the Matrix Specification, please take note of the following guidelines. -All changes to Specification content require a formal proposal process. This -involves writing a proposal, having it reviewed by everyone, having the -proposal being accepted, then actually having your ideas implemented as -committed changes to the `Specification repository +Most changes to the Specification require a formal proposal. Bug fixes, typos, +and clarifications to existing behaviour do not need proposals - see the +`contributing guide `_ +for more information on what does and does not need a proposal. + +The proposal process involves some technical writing, having it reviewed by +everyone, having the proposal being accepted, then actually having your ideas +implemented as committed changes to the `Specification repository `_. Meet the `members of the Core Team -`_, a group of +`_, a group of individuals tasked with ensuring the spec process is as smooth and painless as -possible. Members of the Core Team will do their best to participate in +possible. Members of the Spec Core Team will do their best to participate in discussion, summarise when things become long-winded, and generally try to act towards the benefit of everyone. As a majority, team members have the ability to change the state of a proposal, and individually have the final say in @@ -33,14 +37,15 @@ Guiding Principles Proposals **must** act to the greater benefit of the entire Matrix ecosystem, rather than benefiting or privileging any single player or subset of players - -and must not contain any patent encumbered intellectual property. Members of the Core Team pledge to act as -a neutral custodian for Matrix on behalf of the whole ecosystem. +and must not contain any patent encumbered intellectual property. Members of +the Core Team pledge to act as a neutral custodian for Matrix on behalf of the +whole ecosystem. For clarity: the Matrix ecosystem is anyone who uses the Matrix protocol. That includes client users, server admins, client developers, bot developers, -bridge and application service developers, users and admins who are indirectly using Matrix via -3rd party networks which happen to be bridged, server developers, room -moderators and admins, companies/projects building products or services on +bridge and application service developers, users and admins who are indirectly +using Matrix via 3rd party networks which happen to be bridged, server developers, +room moderators and admins, companies/projects building products or services on Matrix, spec contributors, translators, and those who created it in the first place. @@ -52,13 +57,21 @@ the first place. * the number of online servers in the open federation * the number of developers building on Matrix * the number of independent implementations which use Matrix -* the quality and utility of the Matrix spec +* the number of bridged end-users reachable on the open Matrix network +* the signal-to-noise ratio of the content on the open Matrix network (i.e. minimising spam) +* the ability for users to discover content on their terms (empowering them to select what to see and what not to see) +* the quality and utility of the Matrix spec (as defined by ease and ability + with which a developer can implement spec-compliant clients, servers, bots, + bridges, and other integrations without needing to refer to any other + external material) In addition, proposal authors are expected to uphold the following values in their proposed changes to the Matrix protocol: * Supporting the whole long-term ecosystem rather than individual stakeholder gain -* Openness rather than proprietariness +* Openness rather than proprietary lock-in +* Interoperability rather than fragmentation +* Cross-platform rather than platform-specific * Collaboration rather than competition * Accessibility rather than elitism * Transparency rather than stealth @@ -66,6 +79,89 @@ their proposed changes to the Matrix protocol: * Pragmatism rather than perfection * Proof rather than conjecture +Please see `MSC1779 `_ +for full details of the project's Guiding Principles. + +Technical notes +--------------- + +Proposals **must** develop Matrix as a layered protocol: with new features +building on layers of shared abstractions rather than introducing tight vertical +coupling within the stack. This ensures that new features can evolve rapidly by +building on existing layers and swapping out old features without impacting the +rest of the stack or requiring substantial upgrades to the whole ecosystem. +This is critical for Matrix to rapidly evolve and compete effectively with +centralised systems, despite being a federated protocol. + +For instance, new features should be implemented using the highest layer +abstractions possible (e.g. new event types, which layer on top of the existing +room semantics, and so don't even require any API changes). Failing that, the +next recourse would be backwards-compatible changes to the next layer down (e.g. +room APIs); failing that, considering changes to the format of events or the +DAG; etc. It would be a very unusual feature which doesn't build on the +existing infrastructure provided by the spec and instead created new primitives +or low level APIs. + +Backwards compatibility is very important for Matrix, but not at the expense of +hindering the protocol's evolution. Backwards incompatible changes to endpoints +are allowed when no other alternative exists, and must be versioned under a new +major release of the API. Backwards incompatible changes to the room algorithm +are also allowed when no other alternative exists, and must be versioned under a +new version of the room algorithm. + +There is sometimes a dilemma over where to include higher level features: for +instance, should video conferencing be formalised in the spec, or should it be +implemented via widgets? Should reputation systems be specified? Should search +engine behaviour be specified? + +There is no universal answer to this, but the following guidelines should be +applied: + +1. If the feature would benefit the whole Matrix ecosystem and is aligned with + the guiding principles above, then it should be supported by the spec. +2. If the spec already makes the feature possible without changing any of the + implementations and spec, then it may not need to be added to the spec. +3. However, if the best user experience for a feature does require custom + implementation behaviour then the behaviour should be defined in the spec + such that all implementations may implement it. +4. However, the spec must never add dependencies on unspecified/nonstandardised + 3rd party behaviour. + +As a worked example: + +1. Video conferencing is clearly a feature which would benefit + the whole ecosystem, and so the spec should find a way to make it happen. +2. Video conferencing can be achieved by widgets without requiring any + compulsory changes to changes to clients nor servers to work, and so could be + omitted from the spec. +3. A better experience could be achieved by embedding Jitsi natively into clients + rather than using a widget... +4. ...except that would add a dependency on unspecified/nonstandardised 3rd party + behaviour, so must not be added to the spec. + +Therefore, our two options in the specific case of video conferencing are +either to spec SFU conferencing semantics for WebRTC (or refer to an existing spec +for doing so), or to keep it as a widget-based approach (optionally with widget +extensions specific for more deeply integrating video conferencing use cases). + +As an alternative example: it's very unlikely that "how to visualise Magnetic +Resonsance Imaging data over Matrix" would ever be added to the Matrix spec +(other than perhaps a custom event type in a wider standardised Matrix event +registry) given that the spec's existing primitives of file transfer and +extensible events (MSC1767) give excellent tools for transfering and +visualising arbitrary rich data. + +Supporting public search engines are likely to not require custom spec features +(other than possibly better bulk access APIs), given they can be implemented as +clients using the existing CS API. An exception could be API features required +by decentralised search infrastructure (avoiding centralisation of power by +a centralised search engine). + +Features such as reactions, threaded messages, editable messages, +spam/abuse/content filtering (and reputation systems), are all features which +would clearly benefit the whole Matrix ecosystem, and cannot be implemented in an +interoperable way using the current spec; so they necessitate a spec change. + Process ------- @@ -124,25 +220,25 @@ follows: viewpoints and get consensus, but this can sometimes be time-consuming (or the author may be biased), in which case an impartial 'shepherd' can be assigned to help guide the proposal through this process instead. A shepherd is - typically a neutral party from the Core Team or an experienced member of + typically a neutral party from the Spec Core Team or an experienced member of the community. There is no formal process for assignment. Simply ask for a shepherd to help get your proposal through and one will be assigned based on availability. Having a shepherd is not a requirement for proposal acceptance. -- Members of the Core Team and community will review and discuss the PR in the +- Members of the Spec Core Team and community will review and discuss the PR in the comments and in relevant rooms on Matrix. Discussion outside of GitHub should be summarised in a comment on the PR. -- When a member of the Core Team believes that no new discussion points are +- When a member of the Spec Core Team believes that no new discussion points are being made, they will propose a motion for a final comment period (FCP), along with a *disposition* of either merge, close or postpone. This FCP is provided to allow a short period of time for any invested party to provide a final objection before a major decision is made. If sufficient reasoning is given, an FCP can be cancelled. It is often preceded by a comment summarising the current state of the discussion, along with reasoning for its occurrence. -- A concern can be raised by a Core Team member at any time, which will block - an FCP from beginning. An FCP will only begin when a **75% majority** of core - team members agree on its outcome, and all existing concerns have been +- A concern can be raised by a Spec Core Team member at any time, which will block + an FCP from beginning. An FCP will only begin when 75% of the members of the + Spec Core Team team agree on its outcome, and all existing concerns have been resolved. - The FCP will then begin and last for 5 days, giving anyone else some time to speak up before it concludes. On its conclusion, the disposition of the FCP @@ -232,7 +328,7 @@ Lifetime States 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 Core Team and community +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 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 @@ -242,6 +338,7 @@ Spec PR Merged merged A proposal with Postponed proposal-postponed A proposal that is temporarily blocked or a feature that may not be useful currently but perhaps sometime in the future Closed proposal-closed A proposal which has been reviewed and deemed unsuitable for acceptance +Obsolete obsolete A proposal which has been made obsolete by another proposal or decision elsewhere. =============================== ============================= ==================================== @@ -253,7 +350,7 @@ pull request trackers of the `matrix-doc `_ repo. We use labels and some metadata in MSC PR descriptions to generate this page. -Labels are assigned by the Core Team whilst triaging the proposals based on those +Labels are assigned by the Spec Core Team whilst triaging the proposals based on those which exist in the `matrix-doc `_ repo already. diff --git a/specification/rooms/v1.rst b/specification/rooms/v1.rst index 1c7a56c4..a720b41a 100644 --- a/specification/rooms/v1.rst +++ b/specification/rooms/v1.rst @@ -109,6 +109,12 @@ The types of state events that affect authorization are: - ``m.room.power_levels`` - ``m.room.third_party_invite`` +.. NOTE:: + + Power levels are inferred from defaults when not explicitly supplied. + For example, mentions of the ``sender``'s power level can also refer + to the default power level for users in the room. + The rules are as follows: 1. If type is ``m.room.create``: @@ -243,15 +249,24 @@ The rules are as follows: #. 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: + #. For the keys ``users_default``, ``events_default``, + ``state_default``, ``ban``, ``redact``, ``kick``, ``invite`` check if they + were added, changed or removed. For each found alteration: 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 added, changed or removed in both the ``events`` and + ``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: @@ -293,5 +308,5 @@ 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 +.. _`auth events selection`: ../server_server/%SERVER_RELEASE_LABEL%.html#auth-events-selection +.. _`Signing Events`: ../server_server/%SERVER_RELEASE_LABEL%.html#signing-events diff --git a/specification/rooms/v2.rst b/specification/rooms/v2.rst index 247f6909..4e8365bf 100644 --- a/specification/rooms/v2.rst +++ b/specification/rooms/v2.rst @@ -161,7 +161,7 @@ The *resolution* of a set of states is obtained as follows: resolved state. -.. _`authorization rules`: ../server_server/r0.1.1.html#authorization-rules +.. _`authorization rules`: ../server_server/%SERVER_RELEASE_LABEL%.html#authorization-rules Rejected events +++++++++++++++ diff --git a/specification/rooms/v3.rst b/specification/rooms/v3.rst index 733c6984..8ef52acc 100644 --- a/specification/rooms/v3.rst +++ b/specification/rooms/v3.rst @@ -120,5 +120,5 @@ The remaining rules are the same as `room version 1 `_ using a different encoding for +event IDs. + +.. contents:: Table of Contents +.. sectnum:: + + +Client considerations +--------------------- + +This room version changes the format form event IDs sent to clients. Clients should +already be treating event IDs as opaque identifiers, and should not be concerned with +the format of them. Clients should still encode the event ID when including it in a +request path. + +Clients should expect to see event IDs changed from the format of ``$randomstring:example.org`` +to something like ``$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg`` (note the lack of domain). + + +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 4 uses the same algorithms defined in `room version 3 `_, however +using URL-safe base64 to generate the event ID. + +Event IDs +~~~~~~~~~ + +.. admonition:: Rationale + + Room version 3 generated event IDs that were difficult for client implementations + which were not encoding the event ID to function in those rooms. It additionally + raised concern due to the ``/`` character being interpretted differently by some + reverse proxy software, and generally made administration harder. + +The event ID is the `reference hash`_ of the event encoded using a variation of +`Unpadded Base64`_ which replaces the 62nd and 63rd characters with ``-`` and ``_`` +instead of using ``+`` and ``/``. This matches `RFC4648's definition of URL-safe base64 +`_. Event IDs are still prefixed +with ``$`` and may result in looking like ``$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg``. + +Just like in room version 3, 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. Room version 3 also changes the format +of ``auth_events`` and ``prev_events`` in a PDU. + +{{definition_ss_pdu_v4}} + +.. _`Unpadded Base64`: ../appendices.html#unpadded-base64 +.. _`Canonical JSON`: ../appendices.html#canonical-json +.. _`Signing Events`: ../server_server/%SERVER_RELEASE_LABEL%.html#signing-events +.. _`reference hash`: ../server_server/%SERVER_RELEASE_LABEL%.html#reference-hashes diff --git a/specification/rooms/v5.rst b/specification/rooms/v5.rst new file mode 100644 index 00000000..6d34ec93 --- /dev/null +++ b/specification/rooms/v5.rst @@ -0,0 +1,59 @@ +.. Copyright 2019 The Matrix.org Foundation C.I.C. +.. +.. Licensed under the Apache License, Version 2.0 (the "License"); +.. you may not use this file except in compliance with the License. +.. You may obtain a copy of the License at +.. +.. http://www.apache.org/licenses/LICENSE-2.0 +.. +.. Unless required by applicable law or agreed to in writing, software +.. distributed under the License is distributed on an "AS IS" BASIS, +.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +.. See the License for the specific language governing permissions and +.. limitations under the License. + +Room Version 5 +============== + +This room version builds on `version 4 `_ while enforcing signing +key validity periods for events. + +.. contents:: Table of Contents +.. sectnum:: + + +Client considerations +--------------------- + +There are no specific requirements for clients in this room version. Clients should +be aware of event ID changes in `room version 4 `_, however. + + +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 5 uses the same algorithms defined in `room version 4 `_, ensuring +that signing key validity is respected. + +Signing key validity period +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When validating event signatures, servers MUST enforce the ``valid_until_ts`` property +from a key request is at least as large as the ``origin_server_ts`` for the event being +validated. Servers missing a copy of the signing key MUST try to obtain one via the +`GET /_matrix/key/v2/server <../server_server/%SERVER_RELEASE_LABEL%.html#get-matrix-key-v2-server-keyid>`_ +or `POST /_matrix/key/v2/query <../server_server/%SERVER_RELEASE_LABEL%.html#post-matrix-key-v2-query>`_ +APIs. When using the ``/query`` endpoint, servers MUST set the ``minimum_valid_until_ts`` +property to prompt the notary server to attempt to refresh the key if appropriate. + +Servers MUST use the lesser of ``valid_until_ts`` and 7 days into the future when +determining if a key is valid. This is to avoid a situation where an attacker +publishes a key which is valid for a significant amount of time without a way for +the homeserver owner to revoke it. diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index b765e36a..62f6916a 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -80,9 +80,21 @@ 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.2 `_ - `r0.1.1 `_ - `r0.1.0 `_ + +API standards +------------- + +The mandatory baseline for client-server communication in Matrix is exchanging +JSON objects over HTTP APIs. More efficient optional transports will in future +be supported as optional extensions - e.g. a packed binary encoding over +stream-cipher encrypted TCP socket for low-bandwidth/low-roundtrip mobile usage. +For the default HTTP transport, all API calls use a Content-Type of +``application/json``. In addition, all strings MUST be encoded as UTF-8. + Server discovery ---------------- @@ -421,9 +433,8 @@ must ensure that the event: Further details of these checks, and how to handle failures, are described below. -.. TODO: - Flesh this out a bit more, and probably change the doc to group the various - checks in one place, rather than have them spread out. +The `Signing Events <#signing-events>`_ section has more information on which hashes +and signatures are expected on events, and how to calculate them. Definitions @@ -1099,6 +1110,15 @@ originating server, following the algorithm described in `Checking for a signatu Note that this step should succeed whether we have been sent the full event or a redacted copy. +The signatures expected on an event are: + +* The ``sender``'s server, unless the invite was created as a result of 3rd party invite. + The sender must already match the 3rd party invite, and the server which actually + sends the event may be a different server. +* For room versions 1 and 2, the server which created the ``event_id``. Other room + versions do not track the ``event_id`` over federation and therefore do not need + a signature from those servers. + If the signature is found to be valid, the expected content hash is calculated as described below. The content hash in the ``hashes`` property of the received event is base64-decoded, and the two are compared for equality. @@ -1114,7 +1134,9 @@ Calculating the reference hash for an event ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The *reference hash* of an event covers the essential fields of an event, -including content hashes. It is calculated as follows. +including content hashes. It is used for event identifiers in some room versions. +See the `room version specification`_ for more information. It is calculated as +follows. 1. The event is put through the redaction algorithm. diff --git a/specification/targets.yaml b/specification/targets.yaml index 830449ae..2cd911ba 100644 --- a/specification/targets.yaml +++ b/specification/targets.yaml @@ -5,7 +5,6 @@ targets: client_server: files: - client_server_api.rst - - { 1: events.rst } - { 1: modules.rst } - { 2: feature_profiles.rst } - { 2: "group:modules" } # reference a group of files @@ -38,6 +37,14 @@ targets: files: - rooms/v3.rst version_label: v3 + rooms@v4: # this is translated to be rooms/v4.html + files: + - rooms/v4.rst + version_label: v4 + rooms@v5: # this is translated to be rooms/v5.html + files: + - rooms/v5.rst + version_label: v5 appendices: files: - appendices.rst @@ -83,6 +90,7 @@ groups: # reusable blobs of files when prefixed with 'group:' - modules/server_acls.rst - modules/mentions.rst - modules/room_upgrades.rst + - modules/server_notices.rst title_styles: ["=", "-", "~", "+", "^", "`", "@", ":"]