diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..3a0b6032 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,64 @@ +gendoc: &gendoc + name: Generate the docs + command: | + source /env/bin/activate + scripts/gendoc.py + +genswagger: &genswagger + name: Generate the swagger + command: | + source /env/bin/activate + scripts/dump-swagger.py + +buildswaggerui: &buildswaggerui + name: Build Swagger UI + command: | + ls scripts/ + mkdir -p api/client-server + git clone https://github.com/matrix-org/swagger-ui swagger-ui + cp -r swagger-ui/dist/* api/client-server/ + mkdir -p api/client-server/json + cp scripts/swagger/api-docs.json api/client-server/json/ + wget https://raw.githubusercontent.com/matrix-org/matrix.org/master/content/swagger.css -O api/client-server/swagger.css + wget https://raw.githubusercontent.com/matrix-org/matrix.org/master/scripts/swagger-ui.patch + patch api/client-server/index.html swagger-ui.patch + + +version: 2 +jobs: + build-docs: + docker: + - image: uhoreg/matrix-doc-build + steps: + - checkout + - run: *gendoc + - store_artifacts: + path: scripts/gen + - run: + name: "Doc build is available at:" + command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/scripts/gen/index.html"; echo $DOCS_URL + + build-swagger: + docker: + - image: uhoreg/matrix-doc-build + steps: + - checkout + - run: *genswagger + - run: *buildswaggerui + - store_artifacts: + path: api/client-server/ + - run: + name: "Swagger UI is available at:" + command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/api/client-server/index.html"; echo $DOCS_URL + +workflows: + version: 2 + + build-spec: + jobs: + - build-docs + - build-swagger + +notify: + webhooks: + - url: https://giles.cadair.com/circleci diff --git a/.gitignore b/.gitignore index 84ac4951..a850d2fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ /api/node_modules /assets /assets.tar.gz -/env +/env* /scripts/gen /scripts/continuserv/continuserv /scripts/speculator/speculator @@ -10,3 +10,4 @@ /templating/out *.pyc *.swp +_rendered.rst diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7d84f237..00000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go -go: - - 1.8 - -sudo: false - -# we only need a single git commit -git: - depth: 1 - -# test-and-build does the installation, so tell travis to skip the -# installation step -install: true - -script: - - ./scripts/test-and-build.sh diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2a7416b1..c592cf02 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -69,6 +69,41 @@ For such changes, please do just open a `pull request`_. .. _pull request: https://help.github.com/articles/about-pull-requests + +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. + +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 +under ``changelogs/client_server/newsfragments``. + +To create a changelog entry, create a file named in the format ``prNumber.type`` in +the ``newsfragments`` directory. The ``type`` can be one of the following: + +* ``new`` - Used when adding new endpoints. Please have the file contents be the + method and route being added, surrounded in RST code tags. For example: ``POST + /accounts/whoami`` + +* ``feature`` - Used when adding backwards-compatible changes to the API. + +* ``clarification`` - Used when an area of the spec is being improved upon and does + not change or introduce any functionality. + +* ``breaking`` - Used when the change is not backwards compatible. + +* ``deprecation`` - Used when deprecating something + +All news fragments must have a brief summary explaining the change in the contents +of the file. + +Changes that do not change the spec, such as changes to the build script, formatting, +CSS, etc should not get a news fragment. + Sign off -------- diff --git a/README.rst b/README.rst index 6c87201c..b8847bfb 100644 --- a/README.rst +++ b/README.rst @@ -41,9 +41,9 @@ specs and event schemas in this repository. Preparation ----------- -To use the scripts, it is best to create a Python 2.x virtualenv as follows:: +To use the scripts, it is best to create a Python 3.4+ virtualenv as follows:: - virtualenv env + virtualenv -p python3 env env/bin/pip install -r scripts/requirements.txt (Benjamin Synders has contributed a script for `Nix`_ users, which can be diff --git a/api/application-service/application_service.yaml b/api/application-service/application_service.yaml index a63774a2..d8f43ed6 100644 --- a/api/application-service/application_service.yaml +++ b/api/application-service/application_service.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,8 +30,12 @@ paths: put: summary: Send some events to the application service. description: |- - This API is called by the HS when the HS wants to push an event (or - batch of events) to the AS. + This API is called by the homeserver when it wants to push an event + (or batch of events) to the application service. + + Note that the application service should distinguish state events + from message events via the presence of a ``state_key``, rather than + via the event type. operationId: sendTransaction parameters: - in: path @@ -47,33 +52,33 @@ paths: schema: type: object example: { - "events": [ - { - "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", - "user_id": "@bob:localhost" + "events": [ + { + "age": 32, + "content": { + "body": "incoming message", + "msgtype": "m.text" }, - { - "age": 1984, - "content": { - "body": "another incoming message", - "msgtype": "m.text" - }, - "event_id": "$1228055551ffsef:localhost", - "origin_server_ts": 1432804485886, - "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", - "type": "m.room.message", - "user_id": "@bob:localhost" - } - ] - } + "event_id": "$14328055551tzaee:localhost", + "origin_server_ts": 1432804485886, + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "type": "m.room.message", + "user_id": "@bob:localhost" + }, + { + "age": 1984, + "content": { + "body": "another incoming message", + "msgtype": "m.text" + }, + "event_id": "$1228055551ffsef:localhost", + "origin_server_ts": 1432804485886, + "room_id": "!TmaZBKYIFrIPVGoUYp:localhost", + "type": "m.room.message", + "user_id": "@bob:localhost" + } + ] + } description: "Transaction informations" properties: events: @@ -88,10 +93,9 @@ paths: description: The transaction was processed successfully. examples: application/json: { - } + } schema: type: object - "/rooms/{roomAlias}": get: summary: Query if a room alias should exist on the application service. @@ -119,7 +123,7 @@ paths: before responding. examples: application/json: { - } + } schema: type: object 401: @@ -128,29 +132,29 @@ paths: Optional error information can be included in the body of this response. examples: application/json: { - "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" - } + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } schema: - type: object + $ref: ../client-server/definitions/errors/error.yaml 403: description: |- The credentials supplied by the homeserver were rejected. examples: application/json: { - "errcode": "M_FORBIDDEN" - } + "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN" + } schema: - type: object + $ref: ../client-server/definitions/errors/error.yaml 404: description: |- The application service indicates that this room alias does not exist. Optional error information can be included in the body of this response. examples: application/json: { - "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" - } + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } schema: - type: object + $ref: ../client-server/definitions/errors/error.yaml "/users/{userId}": get: summary: Query if a user should exist on the application service. @@ -175,7 +179,7 @@ paths: service MUST create the user using the client-server API. examples: application/json: { - } + } schema: type: object 401: @@ -184,26 +188,266 @@ paths: Optional error information can be included in the body of this response. examples: application/json: { - "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" - } + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } schema: - type: object + $ref: ../client-server/definitions/errors/error.yaml 403: description: |- The credentials supplied by the homeserver were rejected. examples: application/json: { - "errcode": "M_FORBIDDEN" - } + "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN" + } schema: - type: object + $ref: ../client-server/definitions/errors/error.yaml 404: description: |- The application service indicates that this user does not exist. Optional error information can be included in the body of this response. examples: application/json: { - "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" - } + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } schema: - type: object + $ref: ../client-server/definitions/errors/error.yaml + "/_matrix/app/unstable/thirdparty/protocol/{protocol}": + get: + summary: Retrieve metadata about a specific protocol that the application service supports. + description: |- + This API is called by the homeserver when it wants to present clients + with specific information about the various third party networks that + an application service supports. + operationId: getProtocolMetadata + parameters: + - in: path + name: protocol + type: string + description: The protocol ID. + required: true + x-example: "irc" + responses: + 200: + description: The protocol was found and metadata returned. + schema: + $ref: definitions/protocol_metadata.yaml + 401: + description: |- + The homeserver has not supplied credentials to the application service. + Optional error information can be included in the body of this response. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 403: + description: |- + The credentials supplied by the homeserver were rejected. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 404: + description: No protocol was found with the given path. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + "/_matrix/app/unstable/thirdparty/user/{protocol}": + get: + summary: Retrieve the Matrix User ID of a corresponding third party user. + description: |- + This API is called by the homeserver in order to retrieve a Matrix + User ID linked to a user on the third party network, given a set of + user parameters. + operationId: queryUserByProtocol + parameters: + - in: path + name: protocol + type: string + description: The protocol ID. + required: true + x-example: irc + - in: query + name: fields... + type: string + description: |- + One or more custom fields that are passed to the application + service to help identify the user. + responses: + 200: + description: The Matrix User IDs found with the given parameters. + schema: + $ref: definitions/user_batch.yaml + 401: + description: |- + The homeserver has not supplied credentials to the application service. + Optional error information can be included in the body of this response. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 403: + description: |- + The credentials supplied by the homeserver were rejected. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 404: + description: No users were found with the given parameters. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + "/_matrix/app/unstable/thirdparty/location/{protocol}": + get: + summary: Retreive Matrix-side portal rooms leading to a third party location. + description: |- + Retrieve a list of Matrix portal rooms that lead to the matched third party location. + operationId: queryLocationByProtocol + parameters: + - in: path + name: protocol + type: string + description: The protocol ID. + required: true + x-example: irc + - in: query + name: fields... + type: string + description: |- + One or more custom fields that are passed to the application + service to help identify the third party location. + responses: + 200: + description: At least one portal room was found. + schema: + $ref: definitions/location_batch.yaml + 401: + description: |- + The homeserver has not supplied credentials to the application service. + Optional error information can be included in the body of this response. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 403: + description: |- + The credentials supplied by the homeserver were rejected. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 404: + description: No mappings were found with the given parameters. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + "/_matrix/app/unstable/thirdparty/location": + get: + summary: Reverse-lookup third party locations given a Matrix room alias. + description: |- + Retreive an array of third party network locations from a Matrix room + alias. + operationId: queryLocationByAlias + parameters: + - in: query + name: alias + type: string + description: The Matrix room alias to look up. + responses: + 200: + description: |- + All found third party locations. + schema: + $ref: definitions/location_batch.yaml + 401: + description: |- + The homeserver has not supplied credentials to the application service. + Optional error information can be included in the body of this response. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 403: + description: |- + The credentials supplied by the homeserver were rejected. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 404: + description: No mappings were found with the given parameters. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + "/_matrix/app/unstable/thirdparty/user": + get: + summary: Reverse-lookup third party users given a Matrix User ID. + description: |- + Retreive an array of third party users from a Matrix User ID. + operationId: queryUserByID + parameters: + - in: query + name: userid + type: string + description: The Matrix User ID to look up. + responses: + 200: + description: |- + An array of third party users. + schema: + $ref: definitions/user_batch.yaml + 401: + description: |- + The homeserver has not supplied credentials to the application service. + Optional error information can be included in the body of this response. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 403: + description: |- + The credentials supplied by the homeserver were rejected. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml + 404: + description: No mappings were found with the given parameters. + examples: + application/json: { + "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND" + } + schema: + $ref: ../client-server/definitions/errors/error.yaml diff --git a/api/application-service/definitions/location.yaml b/api/application-service/definitions/location.yaml new file mode 100644 index 00000000..4967ef61 --- /dev/null +++ b/api/application-service/definitions/location.yaml @@ -0,0 +1,30 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +properties: + alias: + description: An alias for a matrix room. + type: string + example: "#freenode_#matrix:matrix.org" + protocol: + description: The protocol ID that the third party location is a part of. + type: string + example: irc + fields: + description: Information used to identify this third party location. + type: object + example: + "network": "freenode" + "channel": "#matrix" +title: Location +type: object \ No newline at end of file diff --git a/api/application-service/definitions/location_batch.yaml b/api/application-service/definitions/location_batch.yaml new file mode 100644 index 00000000..3f6de9df --- /dev/null +++ b/api/application-service/definitions/location_batch.yaml @@ -0,0 +1,17 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: array +description: List of matched third party locations. +items: + $ref: location.yaml diff --git a/api/application-service/definitions/protocol.yaml b/api/application-service/definitions/protocol.yaml new file mode 100644 index 00000000..231e8288 --- /dev/null +++ b/api/application-service/definitions/protocol.yaml @@ -0,0 +1,79 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +properties: + user_fields: + description: Fields used to identify a third party user. + type: array + items: + type: string + description: Field used to identify a third party user. + example: ["network", "nickname"] + location_fields: + description: Fields used to identify a third party location. + type: array + items: + type: string + description: Field used to identify a third party location. + example: ["network", "channel"] + icon: + description: An icon representing the third party protocol. + type: string + example: "mxc://example.org/aBcDeFgH" + field_types: + title: Field Types + description: All location or user fields should have an entry here. + type: object + properties: + fieldname: + title: Field Type + description: Definition of valid values for a field. + type: object + properties: + regexp: + description: A regular expression for validation of a field's value. + type: string + placeholder: + description: An placeholder serving as a valid example of the field value. + type: string + example: { + "network": { + "regexp": "([a-z0-9]+\\.)*[a-z0-9]+", + "placeholder": "irc.example.org" + }, + "nickname": { + "regexp": "[^\\s#]+", + "placeholder": "username" + }, + "channel": { + "regexp": "#[^\\s]+", + "placeholder": "#foobar" + } + } + instances: + description: |- + A list of objects representing independent instances of configuration. + For instance multiple networks on IRC if multiple are bridged by the + same bridge. + type: array + items: + type: object + example: { + "desc": "Freenode", + "icon": "mxc://example.org/JkLmNoPq", + "fields": { + "network": "freenode.net", + } + } +title: Protocol +type: object \ No newline at end of file diff --git a/api/application-service/definitions/protocol_metadata.yaml b/api/application-service/definitions/protocol_metadata.yaml new file mode 100644 index 00000000..2b2c8f4e --- /dev/null +++ b/api/application-service/definitions/protocol_metadata.yaml @@ -0,0 +1,68 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +description: Dictionary of supported third party protocols. +additionalProperties: + $ref: protocol.yaml +example: { + "irc": { + "user_fields": ["network", "nickname"], + "location_fields": ["network", "channel"], + "icon": "mxc://example.org/aBcDeFgH", + "field_types": { + "network": { + "regexp": "([a-z0-9]+\\.)*[a-z0-9]+", + "placeholder": "irc.example.org" + }, + "nickname": { + "regexp": "[^\\s]+", + "placeholder": "username" + }, + "channel": { + "regexp": "#[^\\s]+", + "placeholder": "#foobar" + } + }, + "instances": [ + { + "desc": "Freenode", + "icon": "mxc://example.org/JkLmNoPq", + "fields": { + "network": "freenode.net", + } + } + ] + }, + "gitter": { + "user_fields": ["username"], + "location_fields": ["room"], + "field_types": { + "username": { + "regexp": "@[^\\s]+", + "placeholder": "@username" + }, + "room": { + "regexp": "[^\\s]+\\/[^\\s]+", + "placeholder": "matrix-org/matrix-doc" + } + }, + "instances": [ + { + "desc": "Gitter", + "icon": "mxc://example.org/zXyWvUt", + "fields": {} + } + ] + } +} \ No newline at end of file diff --git a/api/application-service/definitions/user.yaml b/api/application-service/definitions/user.yaml new file mode 100644 index 00000000..a7b2287e --- /dev/null +++ b/api/application-service/definitions/user.yaml @@ -0,0 +1,31 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO: Change userid to user_id as a breaking change +properties: + userid: + description: A Matrix User ID represting a third party user. + type: string + example: "@_gitter_jim:matrix.org" + protocol: + description: The protocol ID that the third party location is a part of. + type: string + example: gitter + fields: + description: Information used to identify this third party location. + type: object + example: + "user": "jim" +title: User +type: object \ No newline at end of file diff --git a/api/application-service/definitions/user_batch.yaml b/api/application-service/definitions/user_batch.yaml new file mode 100644 index 00000000..3653feb4 --- /dev/null +++ b/api/application-service/definitions/user_batch.yaml @@ -0,0 +1,17 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: array +description: List of matched third party users. +items: + $ref: user.yaml diff --git a/api/check_examples.py b/api/check_examples.py index be0676bb..0fb275b1 100755 --- a/api/check_examples.py +++ b/api/check_examples.py @@ -43,21 +43,23 @@ except ImportError as e: raise +def check_schema(filepath, example, schema): + example = resolve_references(filepath, example) + schema = resolve_references(filepath, schema) + resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_file}) + jsonschema.validate(example, schema, resolver=resolver) + + def check_parameter(filepath, request, parameter): schema = parameter.get("schema") example = schema.get('example') - fileurl = "file://" + os.path.abspath(filepath) if example and schema: try: - print ("Checking request schema for: %r %r" % ( + print("Checking request schema for: %r %r" % ( filepath, request )) - # Setting the 'id' tells jsonschema where the file is so that it - # can correctly resolve relative $ref references in the schema - schema['id'] = fileurl - resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml}) - jsonschema.validate(example, schema, resolver=resolver) + check_schema(filepath, example, schema) except Exception as e: raise ValueError("Error validating JSON schema for %r" % ( request @@ -67,17 +69,12 @@ def check_parameter(filepath, request, parameter): def check_response(filepath, request, code, response): example = response.get('examples', {}).get('application/json') schema = response.get('schema') - fileurl = "file://" + os.path.abspath(filepath) if example and schema: try: print ("Checking response schema for: %r %r %r" % ( filepath, request, code )) - # Setting the 'id' tells jsonschema where the file is so that it - # can correctly resolve relative $ref references in the schema - schema['id'] = fileurl - resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml}) - jsonschema.validate(example, schema, resolver=resolver) + check_schema(filepath, example, schema) except Exception as e: raise ValueError("Error validating JSON schema for %r %r" % ( request, code @@ -104,12 +101,39 @@ def check_swagger_file(filepath): check_response(filepath, request, code, response) -def load_yaml(path): - if not path.startswith("file:///"): +def resolve_references(path, schema): + if isinstance(schema, dict): + # do $ref first + if '$ref' in schema: + value = schema['$ref'] + path = os.path.abspath(os.path.join(os.path.dirname(path), value)) + ref = load_file("file://" + path) + result = resolve_references(path, ref) + del schema['$ref'] + else: + result = {} + + for key, value in schema.items(): + result[key] = resolve_references(path, value) + return result + elif isinstance(schema, list): + return [resolve_references(path, value) for value in schema] + else: + return schema + + +def load_file(path): + print("Loading reference: %s" % path) + if not path.startswith("file://"): raise Exception("Bad ref: %s" % (path,)) path = path[len("file://"):] with open(path, "r") as f: - return yaml.load(f) + if path.endswith(".json"): + return json.load(f) + else: + # We have to assume it's YAML because some of the YAML examples + # do not have file extensions. + return yaml.load(f) if __name__ == '__main__': diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml index e7381a55..1cf66fe1 100644 --- a/api/client-server/administrative_contact.yaml +++ b/api/client-server/administrative_contact.yaml @@ -66,7 +66,7 @@ paths: medium: type: string description: The medium of the third party identifier. - enum: ["email"] + enum: ["email", "msisdn"] address: type: string description: The third party identifier address. @@ -129,6 +129,8 @@ paths: "errcode": "M_THREEPID_AUTH_FAILED", "error": "The third party credentials could not be verified by the identity server." } + schema: + "$ref": "definitions/errors/error.yaml" tags: - User data "/account/3pid/email/requestToken": @@ -141,7 +143,21 @@ paths: validation tokens when adding an email address to an account. This API's parameters and response is identical to that of the HS API |/register/email/requestToken|_ endpoint. - operationId: requestTokenTo3PID + operationId: requestTokenTo3PIDEmail responses: 200: - description: An email was sent to the given address + description: An email was sent to the given address. + "/account/3pid/msisdn/requestToken": + post: + summary: Requests a validation token be sent to the given email address for the purpose of adding a phone number to an account. + description: |- + Proxies the identity server API ``validate/msisdn/requestToken``, but + first checks that the given phone number is **not** already associated + with an account on this Home Server. This API should be used to request + validation tokens when adding a phone number to an account. This API's + parameters and response is identical to that of the HS API + |/register/msisdn/requestToken|_ endpoint. + operationId: requestTokenTo3PIDMSISDN + responses: + 200: + description: An SMS message was sent to the given phone number. diff --git a/api/client-server/banning.yaml b/api/client-server/banning.yaml index 3877f0e9..6ef430df 100644 --- a/api/client-server/banning.yaml +++ b/api/client-server/banning.yaml @@ -61,7 +61,8 @@ paths: description: The fully qualified user ID of the user being banned. reason: type: string - description: The reason the user has been banned. + description: The reason the user has been banned. This will be supplied as the + ``reason`` on the target's updated `m.room.member`_ event. required: ["user_id"] responses: 200: @@ -82,6 +83,8 @@ paths: "errcode": "M_FORBIDDEN", "error": "You do not have a high enough power level to ban from this room." } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room membership "/rooms/{roomId}/unban": @@ -133,5 +136,7 @@ paths: "errcode": "M_FORBIDDEN", "error": "You do not have a high enough power level to unban from this room." } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room membership diff --git a/api/client-server/content-repo.yaml b/api/client-server/content-repo.yaml index f76b2fb9..b3e9517b 100644 --- a/api/client-server/content-repo.yaml +++ b/api/client-server/content-repo.yaml @@ -72,7 +72,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Media "/download/{serverName}/{mediaId}": @@ -100,6 +100,7 @@ paths: name: allow_remote x-example: false required: false + 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 @@ -119,7 +120,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Media "/download/{serverName}/{mediaId}/{fileName}": @@ -154,6 +155,7 @@ paths: name: allow_remote x-example: false required: false + 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 @@ -173,7 +175,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Media "/thumbnail/{serverName}/{mediaId}": @@ -221,6 +223,7 @@ paths: name: allow_remote x-example: false required: false + 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 @@ -238,7 +241,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Media "/preview_url": @@ -293,6 +296,47 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - Media + "/config": + get: + summary: Get the configuration for the content repository. + description: |- + This endpoint allows clients to retrieve the configuration of the content + repository, such as upload limitations. + Clients SHOULD use this as a guide when using content repository endpoints. + All values are intentionally left optional. Clients SHOULD follow + the advice given in the field description when the field is not available. + + **NOTE:** Both clients and server administrators should be aware that proxies + between the client and the server may affect the apparent behaviour of content + repository APIs, for example, proxies may enforce a lower upload size limit + than is advertised by the server on this endpoint. + operationId: getConfig + produces: ["application/json"] + security: + - accessToken: [] + responses: + 200: + description: The public content repository configuration for the matrix server. + schema: + type: object + properties: + m.upload.size: + type: number + 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. + examples: + application/json: { + "m.upload.size": 50000000 + } + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/error.yaml" + tags: - Media diff --git a/api/client-server/create_room.yaml b/api/client-server/create_room.yaml index bf632c1a..66b5578a 100644 --- a/api/client-server/create_room.yaml +++ b/api/client-server/create_room.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -40,7 +41,7 @@ paths: 0. A default ``m.room.power_levels`` event, giving the room creator (and not other members) permission to send state events. - 1. Events set by ``presets``. + 1. Events set by the ``preset``. 2. Events listed in ``initial_state``, in the order that they are listed. @@ -49,6 +50,16 @@ paths: 4. Invite events implied by ``invite`` and ``invite_3pid``. + The available presets do the following with respect to room state: + + ======================== ============== ====================== ================ ========= + Preset ``join_rules`` ``history_visibility`` ``guest_access`` Other + ======================== ============== ====================== ================ ========= + ``private_chat`` ``invite`` ``shared`` ``can_join`` + ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All invitees are given the same power level as the room creator. + ``public_chat`` ``public`` ``shared`` ``forbidden`` + ======================== ============== ====================== ================ ========= + operationId: createRoom security: - accessToken: [] @@ -142,7 +153,7 @@ paths: room. The expected format of the state events are an object with type, state_key and content keys set. - Takes precedence over events set by ``presets``, but gets + Takes precedence over events set by ``preset``, but gets overriden by ``name`` and ``topic`` keys. items: type: object @@ -150,29 +161,20 @@ paths: properties: type: type: string + description: The type of event to send. state_key: type: string + description: The state_key of the state event. Defaults to an empty string. content: type: object + description: The content of the event. + required: ["type", "content"] preset: type: string enum: ["private_chat", "public_chat", "trusted_private_chat"] description: |- Convenience parameter for setting various default state events - based on a preset. Must be either: - - ``private_chat`` => - ``join_rules`` is set to ``invite``. - ``history_visibility`` is set to ``shared``. - - ``trusted_private_chat`` => - ``join_rules`` is set to ``invite``. - ``history_visibility`` is set to ``shared``. - All invitees are given the same power level as the room creator. - - ``public_chat``: => - ``join_rules`` is set to ``public``. - ``history_visibility`` is set to ``shared``. + based on a preset. is_direct: type: boolean description: |- @@ -214,6 +216,8 @@ paths: invalid: for example, the user's ``power_level`` is set below that necessary to set the room name (``errcode`` set to ``M_INVALID_ROOM_STATE``). + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room creation diff --git a/api/client-server/definitions/error.yaml b/api/client-server/definitions/errors/error.yaml similarity index 92% rename from api/client-server/definitions/error.yaml rename to api/client-server/definitions/errors/error.yaml index fa5cada7..7471da6f 100644 --- a/api/client-server/definitions/error.yaml +++ b/api/client-server/definitions/errors/error.yaml @@ -17,7 +17,9 @@ properties: errcode: type: string description: An error code. + example: M_UNKNOWN error: type: string description: A human-readable error message. + example: An unknown error occurred required: ["errcode"] \ No newline at end of file diff --git a/api/client-server/definitions/errors/rate_limited.yaml b/api/client-server/definitions/errors/rate_limited.yaml new file mode 100644 index 00000000..aca82ce7 --- /dev/null +++ b/api/client-server/definitions/errors/rate_limited.yaml @@ -0,0 +1,32 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +$ref: error.yaml +type: object +description: The rate limit was reached for this request +properties: + errcode: + type: string + description: The M_LIMIT_EXCEEDED error code + example: M_LIMIT_EXCEEDED + error: + type: string + description: A human-readable error message. + example: Too many requests + retry_after_ms: + type: integer + description: |- + The amount of time in milliseconds the client should wait + before trying the request again. + example: 2000 +required: ["errcode"] \ No newline at end of file diff --git a/api/client-server/definitions/public_rooms_response.yaml b/api/client-server/definitions/public_rooms_response.yaml new file mode 100644 index 00000000..fc6ccb44 --- /dev/null +++ b/api/client-server/definitions/public_rooms_response.yaml @@ -0,0 +1,105 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +description: A list of the rooms on the server. +required: ["chunk"] +properties: + chunk: + title: "PublicRoomsChunks" + type: array + description: |- + A paginated chunk of public rooms. + items: + type: object + title: "PublicRoomsChunk" + required: + - room_id + - num_joined_members + - world_readable + - guest_can_join + properties: + aliases: + type: array + description: |- + Aliases of the room. May be empty. + items: + type: string + canonical_alias: + type: string + description: |- + The canonical alias of the room, if any. + name: + type: string + description: |- + The name of the room, if any. + num_joined_members: + type: number + description: |- + The number of members joined to the room. + room_id: + type: string + description: |- + The ID of the room. + topic: + type: string + description: |- + The topic of the room, if any. + world_readable: + type: boolean + description: |- + Whether the room may be viewed by guest users without joining. + guest_can_join: + type: boolean + description: |- + Whether guest users may join the room and participate in it. + If they can, they will be subject to ordinary power level + rules like any other user. + avatar_url: + type: string + description: The URL for the room's avatar, if one is set. + next_batch: + type: string + description: |- + A pagination token for the response. The absence of this token + means there are no more results to fetch and the client should + stop paginating. + prev_batch: + type: string + description: |- + A pagination token that allows fetching previous results. The + absence of this token means there are no results before this + batch, i.e. this is the first batch. + total_room_count_estimate: + type: number + description: |- + An estimate on the total number of public rooms, if the + server has an estimate. +example: { + "chunk": [ + { + "aliases": ["#murrays:cheese.bar"], + "avatar_url": "mxc://bleeker.street/CHEDDARandBRIE", + "guest_can_join": false, + "name": "CHEESE", + "num_joined_members": 37, + "room_id": "!ol19s:bleecker.street", + "topic": "Tasty tasty cheese", + "world_readable": true + } + ], + "next_batch": "p190q", + "prev_batch": "p1902", + "total_room_count_estimate": 115 +} \ No newline at end of file diff --git a/api/client-server/definitions/user_identifier.yaml b/api/client-server/definitions/user_identifier.yaml new file mode 100644 index 00000000..ce65053d --- /dev/null +++ b/api/client-server/definitions/user_identifier.yaml @@ -0,0 +1,24 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +title: User identifier +description: |- + Identification information for a user +type: object +properties: + type: + type: string + description: The type of identification. See `Identifier types`_ for supported values and additional property descriptions. +required: + - type +additionalProperties: true diff --git a/api/client-server/directory.yaml b/api/client-server/directory.yaml index a50bab62..ee42cf84 100644 --- a/api/client-server/directory.yaml +++ b/api/client-server/directory.yaml @@ -68,6 +68,8 @@ paths: "errcode": "M_UNKNOWN", "error": "Room alias #monkeys:matrix.org already exists." } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room directory get: @@ -118,6 +120,8 @@ paths: "errcode": "M_NOT_FOUND", "error": "Room alias #monkeys:matrix.org not found." } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room directory delete: diff --git a/api/client-server/inviting.yaml b/api/client-server/inviting.yaml index 47f51bd4..f312d5ce 100644 --- a/api/client-server/inviting.yaml +++ b/api/client-server/inviting.yaml @@ -93,9 +93,11 @@ paths: examples: application/json: { "errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"} + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room membership diff --git a/api/client-server/joining.yaml b/api/client-server/joining.yaml index da4e4337..7dc1e0a4 100644 --- a/api/client-server/joining.yaml +++ b/api/client-server/joining.yaml @@ -110,10 +110,12 @@ paths: examples: application/json: { "errcode": "M_FORBIDDEN", "error": "You are not invited to this room."} + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room membership "/join/{roomIdOrAlias}": @@ -143,6 +145,15 @@ paths: description: The room identifier or alias to join. required: true x-example: "#monkeys:matrix.org" + - in: query + type: array + items: + type: string + name: server_name + description: |- + The servers to attempt to join the room through. One of the servers + must be participating in the room. + x-example: ["matrix.org", "elsewhere.ca"] - in: body name: third_party_signed schema: @@ -206,9 +217,11 @@ paths: examples: application/json: { "errcode": "M_FORBIDDEN", "error": "You are not invited to this room."} + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room membership diff --git a/api/client-server/keys.yaml b/api/client-server/keys.yaml index 5fb6318e..55f8a5a5 100644 --- a/api/client-server/keys.yaml +++ b/api/client-server/keys.yaml @@ -194,7 +194,7 @@ paths: "user_id": "@alice:example.com", "device_id": "JLAFKJWSCS", "algorithms": [ - "m.olm.curve25519-aes-sha256", + "m.olm.v1.curve25519-aes-sha256", "m.megolm.v1.aes-sha" ], "keys": { @@ -247,7 +247,7 @@ paths: description: algorithm example: "signed_curve25519" example: - "@alice:example.com": { "JLAFKJWSCS": "curve25519" } + "@alice:example.com": { "JLAFKJWSCS": "signed_curve25519" } required: - one_time_keys responses: @@ -274,7 +274,7 @@ paths: type: object description: |- One-time keys for the queried devices. A map from user ID, to a - map from ``:`` to the key object. + map from devices to a map from ``:`` to the key object. additionalProperties: type: object additionalProperties: diff --git a/api/client-server/kicking.yaml b/api/client-server/kicking.yaml index ef43c0ee..7fbee38b 100644 --- a/api/client-server/kicking.yaml +++ b/api/client-server/kicking.yaml @@ -34,6 +34,10 @@ paths: Kick a user from the room. The caller must have the required power level in order to perform this operation. + + Kicking a user adjusts the target member's membership state to be ``leave`` with an + optional ``reason``. Like with other membership changes, a user can directly adjust + the target member's state by making a request to ``/rooms//state/m.room.member/``. operationId: kick security: - accessToken: [] @@ -59,7 +63,9 @@ paths: description: The fully qualified user ID of the user being kicked. reason: type: string - description: The reason the user has been kicked. + description: |- + The reason the user has been kicked. This will be supplied as the + ``reason`` on the target's updated `m.room.member`_ event. required: ["user_id"] responses: 200: @@ -81,5 +87,7 @@ paths: "errcode": "M_FORBIDDEN", "error": "You do not have a high enough power level to kick from this room." } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Room membership diff --git a/api/client-server/leaving.yaml b/api/client-server/leaving.yaml index 36351fd4..513b5b4d 100644 --- a/api/client-server/leaving.yaml +++ b/api/client-server/leaving.yaml @@ -64,7 +64,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room membership "/rooms/{roomId}/forget": @@ -78,8 +78,8 @@ paths: for this room. If all users on a homeserver forget a room, the room is eligible for deletion from that homeserver. - If the user is currently joined to the room, they will implicitly leave - the room as part of this API call. + If the user is currently joined to the room, they must leave the room + before calling this API. operationId: forgetRoom security: - accessToken: [] @@ -99,9 +99,18 @@ paths: } schema: type: object + 400: + description: The user has not left the room + examples: + application/json: { + "errcode": "M_UNKNOWN", + "error": "User @example:matrix.org is in room !au1ba7o:matrix.org" + } + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room membership diff --git a/api/client-server/list_public_rooms.yaml b/api/client-server/list_public_rooms.yaml index 334d528c..72a12060 100644 --- a/api/client-server/list_public_rooms.yaml +++ b/api/client-server/list_public_rooms.yaml @@ -59,6 +59,8 @@ paths: "errcode": "M_NOT_FOUND", "error": "Room not found" } + schema: + "$ref": "definitions/errors/error.yaml" put: summary: Sets the visibility of a room in the room directory description: |- @@ -107,6 +109,8 @@ paths: "errcode": "M_NOT_FOUND", "error": "Room not found" } + schema: + "$ref": "definitions/errors/error.yaml" "/publicRooms": get: summary: Lists the public rooms on the server. @@ -140,98 +144,7 @@ paths: 200: description: A list of the rooms on the server. schema: - type: object - description: A list of the rooms on the server. - required: ["chunk"] - properties: - chunk: - title: "PublicRoomsChunks" - type: array - description: |- - A paginated chunk of public rooms. - items: - type: object - title: "PublicRoomsChunk" - required: - - room_id - - num_joined_members - - world_readable - - guest_can_join - properties: - aliases: - type: array - description: |- - Aliases of the room. May be empty. - items: - type: string - canonical_alias: - type: string - description: |- - The canonical alias of the room, if any. - name: - type: string - description: |- - The name of the room, if any. - num_joined_members: - type: number - description: |- - The number of members joined to the room. - room_id: - type: string - description: |- - The ID of the room. - topic: - type: string - description: |- - The topic of the room, if any. - world_readable: - type: boolean - description: |- - Whether the room may be viewed by guest users without joining. - guest_can_join: - type: boolean - description: |- - Whether guest users may join the room and participate in it. - If they can, they will be subject to ordinary power level - rules like any other user. - avatar_url: - type: string - description: The URL for the room's avatar, if one is set. - next_batch: - type: string - description: |- - A pagination token for the response. The absence of this token - means there are no more results to fetch and the client should - stop paginating. - prev_batch: - type: string - description: |- - A pagination token that allows fetching previous results. The - absence of this token means there are no results before this - batch, i.e. this is the first batch. - total_room_count_estimate: - type: number - description: |- - An estimate on the total number of public rooms, if the - server has an estimate. - examples: - application/json: { - "chunk": [ - { - "aliases": ["#murrays:cheese.bar"], - "avatar_url": "mxc://bleeker.street/CHEDDARandBRIE", - "guest_can_join": false, - "name": "CHEESE", - "num_joined_members": 37, - "room_id": "!ol19s:bleecker.street", - "topic": "Tasty tasty cheese", - "world_readable": true - } - ], - "next_batch": "p190q", - "prev_batch": "p1902", - "total_room_count_estimate": 115 - } + $ref: "definitions/public_rooms_response.yaml" tags: - Room discovery post: diff --git a/api/client-server/login.yaml b/api/client-server/login.yaml index a6e21a38..43aae5df 100644 --- a/api/client-server/login.yaml +++ b/api/client-server/login.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -28,6 +29,42 @@ securityDefinitions: $ref: definitions/security.yaml paths: "/login": + get: + summary: Get the supported login types to authenticate users + description: |- + Gets the homeserver's supported login types to authenticate users. Clients + should pick one of these and supply it as the ``type`` when logging in. + operationId: getLoginFlows + responses: + 200: + description: The login types the homeserver supports + examples: + application/json: { + "flows": [ + {"type": "m.login.password"} + ] + } + schema: + type: object + properties: + flows: + type: array + description: The homeserver's supported login types + items: + type: object + title: LoginFlow + properties: + type: + description: |- + The login type. This is supplied as the ``type`` when + logging in. + type: string + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - Session management post: summary: Authenticates the user. description: |- @@ -49,7 +86,10 @@ paths: type: object example: { "type": "m.login.password", - "user": "cheeky_monkey", + "identifier": { + "type": "m.id.user", + "user": "cheeky_monkey" + }, "password": "ilovebananas", "initial_device_display_name": "Jungle Phone" } @@ -58,15 +98,18 @@ paths: type: string enum: ["m.login.password", "m.login.token"] description: The login type being used. + identifier: + description: Identification information for the user. + "$ref": "definitions/user_identifier.yaml" user: type: string - description: The fully qualified user ID or just local part of the user ID, to log in. + description: The fully qualified user ID or just local part of the user ID, to log in. Deprecated in favour of ``identifier``. medium: type: string - description: When logging in using a third party identifier, the medium of the identifier. Must be 'email'. + description: When logging in using a third party identifier, the medium of the identifier. Must be 'email'. Deprecated in favour of ``identifier``. address: type: string - description: Third party identifier for the user. + description: Third party identifier for the user. Deprecated in favour of ``identifier``. password: type: string description: |- @@ -131,15 +174,19 @@ paths: "errcode": "M_UNKNOWN", "error": "Bad login type." } + schema: + "$ref": "definitions/errors/error.yaml" 403: description: |- The login attempt failed. For example, the password may have been incorrect. examples: application/json: { "errcode": "M_FORBIDDEN"} + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Session management diff --git a/api/client-server/message_pagination.yaml b/api/client-server/message_pagination.yaml index 71406653..941e61fb 100644 --- a/api/client-server/message_pagination.yaml +++ b/api/client-server/message_pagination.yaml @@ -107,6 +107,7 @@ paths: items: type: object title: RoomEvent + "$ref": "definitions/event-schemas/schema/core-event-schema/room_event.yaml" examples: application/json: { "start": "t47429-4392820_219380_26003_2265", diff --git a/api/client-server/openid.yaml b/api/client-server/openid.yaml new file mode 100644 index 00000000..4b89232e --- /dev/null +++ b/api/client-server/openid.yaml @@ -0,0 +1,103 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Client-Server OpenID API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/%CLIENT_MAJOR_VERSION% +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/user/{userId}/openid/request_token": + post: + summary: Get an OpenID token object to verify the requester's identity. + description: |- + Gets an OpenID token object that the requester may supply to another + service to verify their identity in Matrix. The generated token is only + valid for exchanging for user information from the federation API for + OpenID. + + The access token generated is only valid for the OpenID API. It cannot + be used to request another OpenID access token or call ``/sync``, for + example. + operationId: requestOpenIdToken + security: + - accessToken: [] + parameters: + - in: path + type: string + name: userId + description: |- + The user to request and OpenID token for. Should be the user who + is authenticated for the request. + required: true + x-example: "@alice:example.com" + - in: body + name: body + description: An empty object. Reserved for future expansion. + required: true + schema: + type: object + example: {} + responses: + 200: + description: |- + OpenID token information. This response is nearly compatible with the + response documented in the `OpenID 1.0 Specification `_ + with the only difference being the lack of an ``id_token``. Instead, + the Matrix homeserver's name is provided. + examples: + application/json: { + "access_token": "SomeT0kenHere", + "token_type": "Bearer", + "matrix_server_name": "example.com", + "expires_in": 3600, + } + schema: + type: object + properties: + access_token: + type: string + description: |- + An access token the consumer may use to verify the identity of + the person who generated the token. This is given to the federation + API ``GET /openid/userinfo``. + token_type: + type: string + description: The string ``Bearer``. + matrix_server_name: + type: string + description: |- + The homeserver domain the consumer should use when attempting to + verify the user's identity. + expires_in: + type: int + description: |- + The number of seconds before this token expires and a new one must + be generated. + required: ['access_token', 'token_type', 'matrix_server_name', 'expires_in'] + 429: + description: This request was rate-limited. + schema: + "$ref": "definitions/errors/rate_limited.yaml" + tags: + - OpenID diff --git a/api/client-server/presence.yaml b/api/client-server/presence.yaml index ba202c2d..91b75c6a 100644 --- a/api/client-server/presence.yaml +++ b/api/client-server/presence.yaml @@ -75,7 +75,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Presence get: @@ -83,6 +83,8 @@ paths: description: |- Get the given user's presence state. operationId: getPresence + security: + - accessToken: [] parameters: - in: path type: string @@ -121,6 +123,17 @@ paths: description: |- There is no presence state for this user. This user may not exist or isn't exposing presence information to you. + schema: + "$ref": "definitions/errors/error.yaml" + 403: + description: You are not allowed to see this user's presence status. + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "You are not allowed to see their presence" + } + schema: + "$ref": "definitions/errors/error.yaml" tags: - Presence "/presence/list/{userId}": @@ -176,7 +189,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Presence get: diff --git a/api/client-server/profile.yaml b/api/client-server/profile.yaml index 0cada0ca..c8dc4056 100644 --- a/api/client-server/profile.yaml +++ b/api/client-server/profile.yaml @@ -67,7 +67,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data get: @@ -141,7 +141,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data get: diff --git a/api/client-server/pusher.yaml b/api/client-server/pusher.yaml index 9cf40a06..d232baf9 100644 --- a/api/client-server/pusher.yaml +++ b/api/client-server/pusher.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,30 +32,30 @@ paths: get: summary: Gets the current pushers for the authenticated user description: |- - Gets all currently active pushers for the authenticated user + Gets all currently active pushers for the authenticated user. operationId: getPushers security: - accessToken: [] responses: 200: - description: The pushers for this user + description: The pushers for this user. examples: application/json: { - "pushers": [ - { - "pushkey": "Xp/MzCt8/9DcSNE9cuiaoT5Ac55job3TdLSSmtmYl4A=", - "kind": "http", - "app_id": "face.mcapp.appy.prod", - "app_display_name": "Appy McAppface", - "device_display_name": "Alice's Phone", - "profile_tag": "xyz", - "lang": "en-US", - "data": { - "url": "https://example.com/_matrix/push/v1/notify" - } + "pushers": [ + { + "pushkey": "Xp/MzCt8/9DcSNE9cuiaoT5Ac55job3TdLSSmtmYl4A=", + "kind": "http", + "app_id": "face.mcapp.appy.prod", + "app_display_name": "Appy McAppface", + "device_display_name": "Alice's Phone", + "profile_tag": "xyz", + "lang": "en-US", + "data": { + "url": "https://example.com/_matrix/push/v1/notify" } - ] - } + } + ] + } schema: type: object properties: @@ -70,7 +71,7 @@ paths: pushkey: type: string description: |- - This is a unique identifier for this pusher. See `/set` for + This is a unique identifier for this pusher. See ``/set`` for more detail. Max length, 512 bytes. kind: @@ -115,6 +116,19 @@ paths: description: |- Required if ``kind`` is ``http``. The URL to use to send notifications to. + format: + type: string + description: |- + The format to use when sending notifications to the Push + Gateway. + required: + - pushkey + - app_id + - kind + - app_display_name + - device_display_name + - lang + - data tags: - Push notifications "/pushers/set": @@ -130,23 +144,24 @@ paths: parameters: - in: body name: pusher - description: The pusher information + description: The pusher information. required: true schema: type: object example: { - "lang": "en", - "kind": "http", - "app_display_name": "Mat Rix", - "device_display_name": "iPhone 9", - "profile_tag": "xxyyzz", - "app_id": "com.example.app.ios", - "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ", - "data": { - "url": "https://push-gateway.location.here" - }, - "append": false - } + "lang": "en", + "kind": "http", + "app_display_name": "Mat Rix", + "device_display_name": "iPhone 9", + "profile_tag": "xxyyzz", + "app_id": "com.example.app.ios", + "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ", + "data": { + "url": "https://push-gateway.location.here/_matrix/push/v1/notify", + "format": "event_id_only" + }, + "append": false + } properties: pushkey: type: string @@ -157,11 +172,15 @@ paths: for APNS or the Registration ID for GCM. If your notification client has no such concept, use any unique identifier. Max length, 512 bytes. + + If the ``kind`` is ``"email"``, this is the email address to + send notifications to. kind: type: string description: |- The kind of pusher to configure. ``"http"`` makes a pusher that - sends HTTP pokes. ``null`` deletes the pusher. + sends HTTP pokes. ``"email"`` makes a pusher that emails the + user with unread notifications. ``null`` deletes the pusher. app_id: type: string description: |- @@ -169,6 +188,8 @@ paths: It is recommended that this end with the platform, such that different platform versions get different app identifiers. Max length, 64 chars. + + If the ``kind`` is ``"email"``, this is ``"m.email"``. app_display_name: type: string description: |- @@ -188,7 +209,7 @@ paths: type: string description: |- The preferred language for receiving notifications (e.g. 'en' - or 'en-US') + or 'en-US'). data: type: object description: |- @@ -201,7 +222,17 @@ paths: type: string description: |- Required if ``kind`` is ``http``. The URL to use to send - notifications to. + notifications to. MUST be an HTTPS URL with a path of + ``/_matrix/push/v1/notify``. + example: "https://push-gateway.location.here/_matrix/push/v1/notify" + format: + type: string + description: |- + The format to send notifications in to Push Gateways if the + ``kind`` is ``http``. The details about what fields the + homeserver should send to the push gateway are defined in the + `Push Gateway Specification`_. Currently the only format + available is 'event_id_only'. append: type: boolean description: |- @@ -216,22 +247,22 @@ paths: 200: description: The pusher was set. examples: - application/json: { - } + application/json: {} schema: - type: object # empty json object + type: object + description: An empty object. 400: description: One or more of the pusher values were invalid. examples: application/json: { - "error": "Missing parameters: lang, data", - "errcode": "M_MISSING_PARAM" - } + "error": "Missing parameters: lang, data", + "errcode": "M_MISSING_PARAM" + } schema: - type: object + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Push notifications diff --git a/api/client-server/pushrules.yaml b/api/client-server/pushrules.yaml index 801349ef..ceb9954b 100644 --- a/api/client-server/pushrules.yaml +++ b/api/client-server/pushrules.yaml @@ -438,11 +438,11 @@ paths: "errcode": "M_UNKNOWN" } schema: - type: object + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Push notifications "/pushrules/{scope}/{kind}/{ruleId}/enabled": diff --git a/api/client-server/receipts.yaml b/api/client-server/receipts.yaml index e46359a9..a3e9789e 100644 --- a/api/client-server/receipts.yaml +++ b/api/client-server/receipts.yaml @@ -76,6 +76,6 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room participation diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index 1c544afe..56da9add 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -177,6 +177,8 @@ paths: "errcode": "M_USER_IN_USE", "error": "Desired user ID is already taken." } + schema: + "$ref": "definitions/errors/error.yaml" 401: description: |- The homeserver requires additional authentication information. @@ -185,7 +187,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data "/register/email/requestToken": @@ -194,11 +196,9 @@ paths: description: |- Proxies the identity server API ``validate/email/requestToken``, but first checks that the given email address is not already associated - with an account on this Home Server. Note that, for consistency, - this API takes JSON objects, though the Identity Server API takes - ``x-www-form-urlencoded`` parameters. See the Identity Server API for + with an account on this Home Server. See the Identity Server API for further information. - operationId: requestTokenToRegister + operationId: requestTokenToRegisterEmail parameters: - in: body name: body @@ -248,8 +248,73 @@ paths: "errcode": "M_THREEPID_IN_USE", "error": "The specified address is already in use" } + schema: + "$ref": "definitions/errors/error.yaml" + "/register/msisdn/requestToken": + post: + summary: Requests a validation token be sent to the given phone number for the purpose of registering an account + description: |- + Proxies the identity server API ``validate/msisdn/requestToken``, but + first checks that the given phone number is not already associated + with an account on this Home Server. See the Identity Server API for + further information. + operationId: requestTokenToRegisterMSISDN + parameters: + - in: body + name: body schema: type: object + properties: + id_server: + type: string + description: The ID server to send the onward request to as a hostname with an appended colon and port number if the port is not the default. + example: "id.matrix.org" + client_secret: + type: string + description: Client-generated secret string used to protect this session. + example: "this_is_my_secret_string" + country: + type: string + description: |- + The two-letter uppercase ISO country code that the number in + ``phone_number`` should be parsed as if it were dialled from. + phone_number: + type: string + description: The phone number. + example: "example@example.com" + send_attempt: + type: number + description: Used to distinguish protocol level retries from requests to re-send the SMS message. + example: 1 + required: ["client_secret", "country", "phone_number", "send_attempt"] + 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. + examples: + application/json: {} + schema: + type: object + 400: + description: |- + Part of the request was invalid. This may include one of the following error codes: + + * ``M_THREEPID_IN_USE`` : The phone number is already registered to an account on this server. + However, if the home server has the ability to send SMS message, it is recommended that the server + instead send an SMS message to the user with instructions on how to reset their password. + This prevents malicious parties from being able to determine if a given phone number + has an account on the Home Server in question. + * ``M_SERVER_NOT_TRUSTED`` : The ``id_server`` parameter refers to an ID server + that is not trusted by this Home Server. + examples: + application/json: { + "errcode": "M_THREEPID_IN_USE", + "error": "The specified address is already in use" + } + schema: + "$ref": "definitions/errors/error.yaml" "/account/password": post: summary: "Changes a user's password." @@ -296,7 +361,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data "/account/password/email/requestToken": @@ -317,10 +382,32 @@ paths: .. |/register/email/requestToken| replace:: ``/register/email/requestToken`` .. _/register/email/requestToken: #post-matrix-client-%CLIENT_MAJOR_VERSION%-register-email-requesttoken - operationId: requestTokenToResetPassword + operationId: requestTokenToResetPasswordEmail responses: 200: description: An email was sent to the given address + "/account/password/msisdn/requestToken": + 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 server API ``validate/msisdn/requestToken``, but + first checks that the given phone number **is** associated with an account + on this Home Server. 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 email address 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. + + .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken`` + + .. _/register/msisdn/requestToken: #post-matrix-client-%CLIENT_MAJOR_VERSION%-register-email-requesttoken + operationId: requestTokenToResetPasswordMSISDN + responses: + 200: + description: An SMS message was sent to the given phone number. "/account/deactivate": post: summary: "Deactivate a user's account." @@ -363,7 +450,7 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data "/register/available": @@ -420,9 +507,11 @@ paths: "errcode": "M_USER_IN_USE", "error": "Desired user ID is already taken." } + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data diff --git a/api/client-server/search.yaml b/api/client-server/search.yaml index e5f6a872..e4118c32 100644 --- a/api/client-server/search.yaml +++ b/api/client-server/search.yaml @@ -74,7 +74,7 @@ paths: properties: room_events: type: object - title: "Room Events" + title: Room Events Criteria description: Mapping of category name to search criteria. properties: search_term: @@ -103,7 +103,7 @@ paths: The order in which to search for results. By default, this is ``"rank"``. event_context: - title: "Event Context" + title: Include Event Context type: object description: |- Configures whether any context for the events @@ -169,13 +169,13 @@ paths: properties: search_categories: type: object - title: Categories + title: Result Categories description: Describes which categories to search in and their criteria. properties: room_events: type: object - title: Room Event Results + title: Result Room Events description: Mapping of category name to search criteria. properties: count: @@ -227,6 +227,9 @@ paths: description: |- The historic profile information of the users that sent the events returned. + + The ``string`` key is the user ID for which + the profile belongs to. additionalProperties: type: object title: User Profile @@ -260,15 +263,24 @@ paths: The current state for every room in the results. This is included if the request had the ``include_state`` key set with a value of ``true``. + + The ``string`` key is the room ID for which the ``State + Event`` array belongs to. additionalProperties: type: array title: Room State items: + type: object "$ref": "definitions/event-schemas/schema/core-event-schema/state_event.yaml" groups: type: object title: Groups - description: Any groups that were requested. + description: |- + 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: + a room's ID or a user's ID). additionalProperties: type: object title: Group Key @@ -355,6 +367,6 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Search diff --git a/api/client-server/sync.yaml b/api/client-server/sync.yaml index 34659dd0..4b44c20e 100644 --- a/api/client-server/sync.yaml +++ b/api/client-server/sync.yaml @@ -253,6 +253,14 @@ paths: description: |- Information on end-to-end device updates, as specified in |device_lists_sync|_. + device_one_time_keys_count: + title: One-time keys count + type: object + additionalProperties: + type: integer + description: |- + Information on end-to-end encryption keys, as specified + in |device_lists_sync|_. examples: application/json: { "next_batch": "s72595_4483_1934", diff --git a/api/client-server/tags.yaml b/api/client-server/tags.yaml index 9310b25f..b7bafab6 100644 --- a/api/client-server/tags.yaml +++ b/api/client-server/tags.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -64,8 +65,9 @@ paths: examples: application/json: { "tags": { - "work": {"order": "1"}, - "pinned": {} + "m.favourite": {}, + "u.Work": {"order": "1"}, + "u.Customers": {} } } tags: diff --git a/api/client-server/third_party_lookup.yaml b/api/client-server/third_party_lookup.yaml new file mode 100644 index 00000000..cba9ce22 --- /dev/null +++ b/api/client-server/third_party_lookup.yaml @@ -0,0 +1,208 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Client-Server Third Party Lookup API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/%CLIENT_MAJOR_VERSION% +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/thirdparty/protocols": + get: + summary: Retrieve metadata about all protocols that a homeserver supports. + description: |- + Fetches the overall metadata about protocols supported by the + homeserver. Includes both the available protocols and all fields + required for queries against each protocol. + operationId: getProtocols + security: + - accessToken: [] + responses: + 200: + description: The protocols supported by the homeserver. + schema: + $ref: ../application-service/definitions/protocol_metadata.yaml + "/thirdparty/protocol/{protocol}": + get: + summary: Retrieve metadata about a specific protocol that the homeserver supports. + description: |- + Fetches the metadata from the homeserver about a particular third party protocol. + operationId: getProtocolMetadata + security: + - accessToken: [] + parameters: + - in: path + name: protocol + type: string + description: |- + The name of the protocol. + required: true + x-example: "irc" + responses: + 200: + description: The protocol was found and metadata returned. + schema: + $ref: ../application-service/definitions/protocol.yaml + 404: + description: The protocol is unknown. + examples: + application/json: { + "errcode": "M_NOT_FOUND" + } + schema: + $ref: definitions/errors/error.yaml + "/thirdparty/location/{protocol}": + get: + summary: Retreive Matrix-side portals rooms leading to a third party location. + description: |- + Requesting this endpoint with a valid protocol name results in a list + of successful mapping results in a JSON array. Each result contains + objects to represent the Matrix room or rooms that represent a portal + to this third party network. Each has the Matrix room alias string, + an identifier for the particular third party network protocol, and an + object containing the network-specific fields that comprise this + identifier. It should attempt to canonicalise the identifier as much + as reasonably possible given the network type. + operationId: queryLocationByProtocol + security: + - accessToken: [] + parameters: + - in: path + name: protocol + type: string + description: The protocol used to communicate to the third party network. + required: true + x-example: irc + - in: query + name: searchFields + type: string + description: |- + One or more custom fields to help identify the third party + location. + responses: + 200: + description: At least one portal room was found. + schema: + $ref: ../application-service/definitions/location_batch.yaml + 404: + description: No portal rooms were found. + examples: + application/json: { + "errcode": "M_NOT_FOUND" + } + schema: + $ref: definitions/errors/error.yaml + "/thirdparty/user/{protocol}": + get: + summary: Retrieve the Matrix User ID of a corresponding third party user. + description: |- + Retrieve a Matrix User ID linked to a user on the third party service, given + a set of user parameters. + operationId: queryUserByProtocol + security: + - accessToken: [] + parameters: + - in: path + name: protocol + type: string + description: |- + The name of the protocol. + required: true + x-example: irc + - in: query + name: fields... + type: string + description: |- + One or more custom fields that are passed to the AS to help identify the user. + responses: + 200: + description: The Matrix User IDs found with the given parameters. + schema: + $ref: ../application-service/definitions/user_batch.yaml + 404: + description: The Matrix User ID was not found + examples: + application/json: { + "errcode": "M_NOT_FOUND" + } + schema: + $ref: definitions/errors/error.yaml + "/thirdparty/location": + get: + summary: Reverse-lookup third party locations given a Matrix room alias. + description: |- + Retreive an array of third party network locations from a Matrix room + alias. + operationId: queryLocationByAlias + security: + - accessToken: [] + parameters: + - in: query + name: alias + type: string + description: The Matrix room alias to look up. + required: true + x-example: "#matrix:matrix.org" + responses: + 200: + description: |- + All found third party locations. + schema: + $ref: ../application-service/definitions/location_batch.yaml + 404: + description: The Matrix room alias was not found + examples: + application/json: { + "errcode": "M_NOT_FOUND" + } + schema: + $ref: definitions/errors/error.yaml + "/thirdparty/user": + get: + summary: Reverse-lookup third party users given a Matrix User ID. + description: |- + Retreive an array of third party users from a Matrix User ID. + operationId: queryUserByID + security: + - accessToken: [] + parameters: + - in: query + name: userid + type: string + description: The Matrix User ID to look up. + required: true + x-example: "@bob:matrix.org" + responses: + 200: + description: |- + An array of third party users. + schema: + $ref: ../application-service/definitions/user_batch.yaml + 404: + description: The Matrix User ID was not found + examples: + application/json: { + "errcode": "M_NOT_FOUND" + } + schema: + $ref: definitions/errors/error.yaml diff --git a/api/client-server/third_party_membership.yaml b/api/client-server/third_party_membership.yaml index 612b22d0..66c14c4d 100644 --- a/api/client-server/third_party_membership.yaml +++ b/api/client-server/third_party_membership.yaml @@ -126,9 +126,11 @@ paths: examples: application/json: { "errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"} + schema: + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room membership diff --git a/api/client-server/typing.yaml b/api/client-server/typing.yaml index e2a8f9bd..e7cbe2d7 100644 --- a/api/client-server/typing.yaml +++ b/api/client-server/typing.yaml @@ -82,6 +82,6 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - Room participation diff --git a/api/client-server/users.yaml b/api/client-server/users.yaml index 1734e3bb..a682b435 100644 --- a/api/client-server/users.yaml +++ b/api/client-server/users.yaml @@ -95,6 +95,6 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data diff --git a/api/client-server/voip.yaml b/api/client-server/voip.yaml index 6d6136a5..75ace4c3 100644 --- a/api/client-server/voip.yaml +++ b/api/client-server/voip.yaml @@ -73,6 +73,6 @@ paths: 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - VOIP diff --git a/api/client-server/whoami.yaml b/api/client-server/whoami.yaml index 8f5abdf7..ad40eb86 100644 --- a/api/client-server/whoami.yaml +++ b/api/client-server/whoami.yaml @@ -65,7 +65,7 @@ paths: "error": "Unrecognised access token." } schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/error.yaml" 403: description: The appservice cannot masquerade as the user or has not registered them. @@ -75,10 +75,10 @@ paths: "error": "Application service has not registered this user." } schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/error.yaml" 429: description: This request was rate-limited. schema: - "$ref": "definitions/error.yaml" + "$ref": "definitions/errors/rate_limited.yaml" tags: - User data diff --git a/api/identity/associations.yaml b/api/identity/associations.yaml new file mode 100644 index 00000000..784bb5d6 --- /dev/null +++ b/api/identity/associations.yaml @@ -0,0 +1,179 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Establishing Associations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https + - http +basePath: /_matrix/identity/api/v1 +produces: + - application/json +paths: + "/3pid/getValidated3pid": + get: + summary: Check whether ownership of a 3pid was validated. + description: A client can check whether ownership of a 3pid was validated + operationId: getValidated3pid + 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 indicating the time that the 3pid was validated. + 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" + } + 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" + } + "/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 older versions of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: bind + 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 services which show that the association should be trusted, if you trust the verifying identity services. + 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" + } + 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" + } diff --git a/api/identity/email_associations.yaml b/api/identity/email_associations.yaml new file mode 100644 index 00000000..8431c9e8 --- /dev/null +++ b/api/identity/email_associations.yaml @@ -0,0 +1,197 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Email Associations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https + - http +basePath: /_matrix/identity/api/v1 +produces: + - application/json +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 service will send an email containing a token. If that + token is presented to the identity service 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 Home Servers 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 older versions of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: emailRequestToken + parameters: + - in: body + name: body + schema: + type: object + example: { + "client_secret": "monkeys_are_GREAT", + "email": "foo@example.com", + "send_attempt": 1 + } + properties: + client_secret: + type: string + description: A unique string used to identify the validation attempt + email: + type: string + description: The email address to validate. + send_attempt: + type: integer + description: |- + Optional. If specified, the server will only send an email if + the ``send_attempt`` is a number greater than the most recent + one which it has seen (or if it has never seen one), scoped + to that ``email`` + ``client_secret`` pair. This is to avoid + repeatedly sending the same email in the case of request + retries between the POSTing user and the identity + service. The client should increment this value if they + desire a new email (e.g. a reminder) to be sent. + next_link: + type: string + description: |- + Optional. When the validation is completed, the identity + service will redirect the user to this URL. + required: ["client_secret", "email"] + responses: + 200: + description: + Session created. + examples: + application/json: { + "sid": "1234" + } + schema: + type: object + properties: + sid: + type: string + description: The session ID. + 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. + "/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. + + Note: for backwards compatibility with older versions of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: emailSubmitTokenPost + 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. + 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: emailSubmitTokenGet + 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/invitation_signing.yaml b/api/identity/invitation_signing.yaml new file mode 100644 index 00000000..982dbff7 --- /dev/null +++ b/api/identity/invitation_signing.yaml @@ -0,0 +1,90 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Ephemeral Invitation Signing API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https + - http +basePath: /_matrix/identity/api/v1 +produces: + - application/json +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: blindlySignStuff + 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: 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 signedjson 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. + token: + type: string + description: The token for the invitation. + examples: + application/json: { + "mxid": "@foo:bar.com", + "sender": "@baz:bar.com", + "signatures": { + "my.id.server": { + "ed25519:0": "def987" + } + }, + "token": "abc123" + } + 404: + description: Token was not found. + example: { + "errcode": "M_UNRECOGNIZED", + "error": "Didn't recognize token" + } diff --git a/api/identity/phone_associations.yaml b/api/identity/phone_associations.yaml new file mode 100644 index 00000000..c2cc6cfe --- /dev/null +++ b/api/identity/phone_associations.yaml @@ -0,0 +1,203 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Phone Number Associations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https + - http +basePath: /_matrix/identity/api/v1 +produces: + - application/json +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 service will send an SMS message containing a token. If + that token is presented to the identity service 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 Home Servers 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 older versions of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: msisdnRequestToken + parameters: + - in: body + name: body + schema: + type: object + example: { + "client_secret": "monkeys_are_GREAT", + "country": "GB", + "phone_number": "07700900001", + "send_attempt": 1 + } + properties: + client_secret: + type: string + description: A unique string used to identify the validation attempt. + country: + type: string + description: |- + The two-letter uppercase ISO country code that the number in + ``phone_number`` should be parsed as if it were dialled from. + phone_number: + type: string + description: The phone number to validate. + send_attempt: + type: integer + description: |- + Optional. If specified, the server will only send an SMS if + the ``send_attempt`` is a number greater than the most recent + one which it has seen (or if it has never seen one), scoped + to that ``country`` + ``phone_number`` + ``client_secret`` + triple. This is to avoid repeatedly sending the same SMS in + the case of request retries between the POSTing user and the + identity service. The client should increment this value if + they desire a new SMS (e.g. a reminder) to be sent. + next_link: + type: string + description: |- + Optional. When the validation is completed, the identity + service will redirect the user to this URL. + required: ["client_secret", "country", "phone_number"] + responses: + 200: + description: + Session created. + examples: + application/json: { + "sid": "1234" + } + schema: + type: object + properties: + sid: + type: string + description: The session ID. + 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. + "/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. + + Note: for backwards compatibility with older versions of this + specification, the parameters may also be specified as + ``application/x-form-www-urlencoded`` data. However, this usage is + deprecated. + operationId: msisdnSubmitTokenPost + 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. + 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: msisdnSubmitTokenGet + 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/store_invite.yaml b/api/identity/store_invite.yaml new file mode 100644 index 00000000..6b847b5b --- /dev/null +++ b/api/identity/store_invite.yaml @@ -0,0 +1,114 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +swagger: '2.0' +info: + title: "Matrix Identity Service Store Invitations API" + version: "1.0.0" +host: localhost:8090 +schemes: + - https + - http +basePath: /_matrix/identity/api/v1 +produces: + - application/json +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/api/v1/pubkey/ephemeral/isvalid``. + operationId: storeInvite + 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``. + address: + type: string + description: The email address of the invited user. + room_id: + type: string + description: The Matrix room ID to which the user is invited + sender: + type: string + description: The Matrix user ID of the inviting user + required: ["medium", "address", "room_id", "sender"] + responses: + 200: + description: The invitation was stored. + schema: + type: object + properties: + token: + type: string + description: The generated token. + 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. + 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": mxid + } diff --git a/api/openapi_extensions.md b/api/openapi_extensions.md new file mode 100644 index 00000000..339452ba --- /dev/null +++ b/api/openapi_extensions.md @@ -0,0 +1,45 @@ +# OpenAPI Extensions + +For some functionality that is not directly provided by the OpenAPI v2 +specification, some extensions have been added that are to be consistent +across the specification. The defined extensions are listed below. Extensions +should not break parsers, however if extra functionality is required, aware +parsers should be able to take advantage of the added syntax. + +## Extensible Query Parameters + + + +If a unknown amount of query parameters can be added to a request, the `name` +must be `fields...`, with the trailing ellipses representing the possibility +of more fields. + +Example: + +``` + - in: query + name: fields... + type: string +``` + +## Using oneOf to provide type alternatives + + + +`oneOf` (available in JSON Schema and Swagger/OpenAPI v3 but not in v2) +is used in cases when a simpler type specification as a list of types +doesn't work, as in the following example: +``` + properties: + old: # compliant with old Swagger + type: + - string + - object # Cannot specify a schema here + new: # uses oneOf extension + oneOf: + - type: string + - type: object + title: CustomSchemaForTheWin + properties: + ... +``` diff --git a/api/push-gateway/push_notifier.yaml b/api/push-gateway/push_notifier.yaml index 9b6e78d3..4a6cb8f7 100644 --- a/api/push-gateway/push_notifier.yaml +++ b/api/push-gateway/push_notifier.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +20,7 @@ host: localhost:8008 schemes: - https - http -basePath: /_matrix/push/%CLIENT_MAJOR_VERSION% +basePath: /_matrix/push/v1 consumes: - application/json produces: @@ -38,14 +39,14 @@ paths: Notifications about a particular event will normally cause the user to be alerted in some way. It is therefore necessary to perform duplicate - suppression for such notifications using the `event_id` field to avoid + suppression for such notifications using the ``event_id`` field to avoid retries of this HTTP API causing duplicate alerts. The operation of updating counts of unread notifications should be idempotent and therefore do not require duplicate suppression. - Notifications are sent to the URL configured when the pusher is - created. This means that the HTTP path may be different depending on the - push gateway. + Notifications are sent to the URL configured when the pusher is created. + This means that the HTTP path may be different depending on the push + gateway. operationId: notify parameters: - in: body @@ -55,36 +56,36 @@ paths: schema: type: object example: { - "notification": { - "id": "$3957tyerfgewrf384", - "room_id": "!slw48wfj34rtnrf:example.com", - "type": "m.room.message", - "sender": "@exampleuser:matrix.org", - "sender_display_name": "Major Tom", - "room_name": "Mission Control", - "room_alias": "#exampleroom:matrix.org", - "prio": "high", - "content": { - "msgtype": "m.text", - "body": "I'm floating in a most peculiar way." - }, - "counts": { - "unread" : 2, - "missed_calls": 1 - }, - "devices": [ - { - "app_id": "org.matrix.matrixConsole.ios", - "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", - "pushkey_ts": 12345678, - "data" : {}, - "tweaks": { - "sound": "bing" - } + "notification": { + "id": "$3957tyerfgewrf384", + "room_id": "!slw48wfj34rtnrf:example.com", + "type": "m.room.message", + "sender": "@exampleuser:matrix.org", + "sender_display_name": "Major Tom", + "room_name": "Mission Control", + "room_alias": "#exampleroom:matrix.org", + "prio": "high", + "content": { + "msgtype": "m.text", + "body": "I'm floating in a most peculiar way." + }, + "counts": { + "unread" : 2, + "missed_calls": 1 + }, + "devices": [ + { + "app_id": "org.matrix.matrixConsole.ios", + "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/", + "pushkey_ts": 12345678, + "data" : {}, + "tweaks": { + "sound": "bing" } - ] - } + } + ] } + } required: ["notification"] properties: notification: @@ -111,14 +112,10 @@ paths: type: string description: |- The type of the event as in the event's ``type`` field. - Required if the notification relates to a specific - Matrix event. sender: type: string description: |- The sender of the event as in the corresponding event field. - Required if the notification relates to a specific - Matrix event. sender_display_name: type: string description: |- @@ -148,15 +145,16 @@ paths: type: object title: EventContent description: |- - The ``content`` field from the event, if present. If the - event had no content field, this field is omitted. + The ``content`` field from the event, if present. The pusher + may omit this if the event had no content or for any other + reason. counts: type: object title: Counts description: |- This is a dictionary of the current number of unacknowledged communications for the recipient user. Counts whose value is - zero are omitted. + zero should be omitted. properties: unread: type: integer @@ -180,10 +178,10 @@ paths: app_id: type: string description: |- - The app_id given when the pusher was created. + The ``app_id`` given when the pusher was created. pushkey: type: string - description: The pushkey given when the pusher was created. + description: The ``pushkey`` given when the pusher was created. pushkey_ts: type: integer description: |- @@ -202,13 +200,14 @@ paths: description: |- A dictionary of customisations made to the way this notification is to be presented. These are added by push rules. + required: ['app_id', 'pushkey'] responses: 200: description: A list of rejected push keys. examples: application/json: { - "rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ] - } + "rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ] + } schema: type: object # empty json object properties: @@ -222,7 +221,8 @@ paths: pushkeys and remove the associated pushers. It may not necessarily be the notification in the request that failed: it could be that a previous notification to the same pushkey - failed. + failed. May be empty. items: type: string - description: A pushkey + description: A pushkey that has been rejected. + required: ['rejected'] diff --git a/api/server-server/backfill.yaml b/api/server-server/backfill.yaml new file mode 100644 index 00000000..6b3cfaef --- /dev/null +++ b/api/server-server/backfill.yaml @@ -0,0 +1,148 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Events API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/backfill/{roomId}": + get: + summary: Retrieves the events which precede the given event + description: |- + Retrieves a sliding-window history of previous PDUs that occurred in the given room. + Starting from the PDU ID(s) given in the ``v`` argument, the PDUs that preceded it + are retrieved, up to the total number given by the ``limit``. + operationId: backfillRoom + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID to backfill. + required: true + x-example: "!SomeRoom:matrix.org" + - in: query + name: v + type: array + items: + type: string + description: The event IDs to backfill from. + required: true + x-example: ["$abc123:matrix.org"] + - in: query + name: limit + type: integer + description: The maximum number of PDUs to retrieve, including the given events. + required: true + x-example: 2 + responses: + 200: + description: |- + 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" + # Override the example to show the response of the request a bit better + examples: + application/json: { + "$ref": "examples/transaction.json", + "pdus": [ + { + "$ref": "pdu.json", + "room_id": "!SomeRoom:matrix.org", + "event_id": "$abc123:matrix.org" + }, + { + "$ref": "pdu.json", + "room_id": "!SomeRoom:matrix.org" + }, + ] + } + "/get_missing_events/{roomId}": + post: + summary: Retrieves events that the sender is missing + description: |- + Retrieves previous events that the sender is missing. This is done by doing a breadth-first + walk of the ``prev_events`` for the ``latest_events``, ignoring any events in ``earliest_events`` + and stopping at the ``limit``. + operationId: getMissingPreviousEvents + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID to search in. + required: true + x-example: "!SomeRoom:matrix.org" + - in: body + name: body + schema: + type: object + properties: + limit: + type: integer + description: The maximum number of events to retrieve. Defaults to 10. + example: 10 + min_depth: + type: integer + description: The minimum depth of events to retrieve. Defaults to 0. + example: 0 + earliest_events: + type: array + description: |- + The latest events that the sender already has. These are skipped when retrieving + the previous events of ``latest_events``. + items: + type: string + example: ["$missing_event:domain.com"] + latest_events: + type: array + description: The events to retrieve the previous events for. + items: + type: string + example: ["$event_that_has_the_missing_event_as_a_previous_event:domain.com"] + required: ['earliest_events', 'latest_events'] + responses: + 200: + description: |- + The previous events for ``latest_events``, excluding any ``earliest_events``, up to the + provided ``limit``. + schema: + type: object + properties: + events: + type: array + description: The missing events. + items: + $ref: definitions/pdu.yaml + required: ['events'] + examples: + application/json: { + "events": [ + {"$ref": "examples/pdu.json"} + ] + } diff --git a/api/server-server/definitions/edu.yaml b/api/server-server/definitions/edu.yaml new file mode 100644 index 00000000..0e4edcc6 --- /dev/null +++ b/api/server-server/definitions/edu.yaml @@ -0,0 +1,28 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: Ephemeral Data Unit +description: An ephemeral data unit. +example: + $ref: "../examples/edu.json" +properties: + edu_type: + type: string + description: The type of ephemeral message. + example: "m.presence" + content: + type: object + description: The content of the ephemeral message. +required: ['edu_type', 'content'] \ No newline at end of file diff --git a/api/server-server/definitions/event-schemas/m.receipt.yaml b/api/server-server/definitions/event-schemas/m.receipt.yaml new file mode 100644 index 00000000..210988f9 --- /dev/null +++ b/api/server-server/definitions/event-schemas/m.receipt.yaml @@ -0,0 +1,82 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: Receipt EDU +description: |- + An EDU representing receipt updates for users of the sending homeserver. + When receiving receipts, the server should only update entries that are + listed in the EDU. Receipts previously received that do not appear in the + EDU should not be removed or otherwise manipulated. +allOf: + - $ref: ../edu.yaml + - type: object + properties: + edu_type: + type: enum + enum: ['m.receipt'] + description: The string ``m.receipt`` + example: "m.receipt" + content: + type: object + description: |- + Receipts for a particular room. The string key is the room ID for + which the receipts under it belong. + additionalProperties: + type: object + title: Room Receipts + properties: + # We strongly define the receipt type to help spec future ones later + # on. At that point, m.read can become optional (maybe). + "m.read": + type: object + description: Read receipts for users in the room. + title: User Read Receipt + properties: + event_ids: + type: array + description: |- + The extremity event IDs that the user has read up to. + minItems: 1 + maxItems: 1 + items: + type: string + example: ['$read_this_event:matrix.org'] + data: + type: object + description: Metadata for the read receipt. + title: Read Receipt Metadata + properties: + ts: + type: integer + format: int64 + description: |- + A POSIX timestamp in milliseconds for when the user read + the event specified in the read receipt. + example: 1533358089009 + required: ['ts'] + required: ['event_ids', 'data'] + required: ['m.read'] + example: { + "!some_room:domain.com": { + "m.read": { + "@john:matrix.org": { + "event_ids": ["$read_this_event:matrix.org"], + "data": { + "ts": 1533358089009 + } + } + } + } + } diff --git a/api/server-server/definitions/event-schemas/m.typing.yaml b/api/server-server/definitions/event-schemas/m.typing.yaml new file mode 100644 index 00000000..d4fa2f81 --- /dev/null +++ b/api/server-server/definitions/event-schemas/m.typing.yaml @@ -0,0 +1,45 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: object +title: Typing Notification EDU +description: A typing notification EDU for a user in a room. +allOf: + - $ref: ../edu.yaml + - type: object + properties: + edu_type: + type: string + description: The string ``m.typing`` + example: "m.typing" + content: + type: object + description: The typing notification. + title: Typing Notification + properties: + room_id: + type: string + description: |- + The room where the user's typing status has been updated. + example: "!somewhere:matrix.org" + user_id: + type: string + description: |- + The user ID that has had their typing status changed. + example: "@john:matrix.org" + typing: + type: boolean + description: Whether the user is typing in the room or not. + example: true + required: ['room_id', 'user_id', 'typing'] diff --git a/api/server-server/definitions/invite_event.yaml b/api/server-server/definitions/invite_event.yaml new file mode 100644 index 00000000..d196339a --- /dev/null +++ b/api/server-server/definitions/invite_event.yaml @@ -0,0 +1,87 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Invite Event +description: An invite event +allOf: + - $ref: "pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + # TODO: Verify/clarify this - it doesn't seem right, given this is a 'regular' invite + description: |- + The matrix ID of the user who sent the original ``m.room.third_party_invite``. + example: "@someone:example.org" + origin: + type: string + description: The name of the inviting homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the inviting homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the invited member. + example: "@joe:elsewhere.com" + content: + type: object + title: Membership Event Content + description: |- + The content of the event, matching what is available in the + `Client-Server API`_. Must include a ``membership`` of ``invite``. + example: {"membership": "invite"} + properties: + membership: + type: string + description: The value ``invite``. + example: "invite" + required: ['membership'] + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to be invited to the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key diff --git a/api/server-server/definitions/keys.yaml b/api/server-server/definitions/keys.yaml new file mode 100644 index 00000000..738e9e46 --- /dev/null +++ b/api/server-server/definitions/keys.yaml @@ -0,0 +1,110 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Server Keys +description: Server keys +example: + $ref: "../examples/server_key.json" +properties: + server_name: + type: string + description: DNS name of the homeserver. + required: true + example: "example.org" + verify_keys: + type: object + description: |- + Public keys of the homeserver for verifying digital signatures. + + The object's key is the algorithm and version combined (``ed25519`` being the + algorithm and ``abc123`` being the version in the example below). Together, + this forms the Key ID. The version must have characters matching the regular + expression ``[a-zA-Z0-9_]``. + required: true + additionalProperties: + type: object + title: Verify Key + example: { + "ed25519:abc123": { + "key": "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA" + } + } + properties: + key: + type: string + description: The `Unpadded Base64`_ encoded key. + required: true + example: "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA" + old_verify_keys: + type: object + description: |- + The public keys that the server used to use and when it stopped using them. + + The object's key is the algorithm and version combined (``ed25519`` being the + algorithm and ``0ldK3y`` being the version in the example below). Together, + this forms the Key ID. The version must have characters matching the regular + expression ``[a-zA-Z0-9_]``. + additionalProperties: + type: object + title: Old Verify Key + example: { + "ed25519:0ldK3y": { + "expired_ts": 1532645052628, + "key": "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg" + } + } + properties: + expired_ts: + type: integer + format: int64 + description: POSIX timestamp in milliseconds for when this key expired. + required: true + example: 1532645052628 + key: + type: string + description: The `Unpadded Base64`_ encoded key. + required: true + example: "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg" + signatures: + type: object + description: Digital signatures for this object signed using the ``verify_keys``. + additionalProperties: + type: object + title: Signed Server + example: { + "example.org": { + "ad25519:abc123": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU" + } + } + additionalProperties: + type: string + name: Encoded Signature Verification Key + tls_fingerprints: + type: array + description: Hashes of X.509 TLS certificates used by this server. + items: + type: object + title: TLS Fingerprint + properties: + sha256: + type: string + description: The `Unpadded Base64`_ encoded fingerprint. + example: "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw" + valid_until_ts: + type: integer + format: int64 + description: |- + POSIX timestamp when the list of valid keys should be refreshed. Keys used beyond this + timestamp are no longer valid. + example: 1052262000000 diff --git a/api/server-server/definitions/keys_query_response.yaml b/api/server-server/definitions/keys_query_response.yaml new file mode 100644 index 00000000..52ad506c --- /dev/null +++ b/api/server-server/definitions/keys_query_response.yaml @@ -0,0 +1,27 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +description: Server keys +example: { + "server_keys": [{ + $ref: "../examples/server_key_notary_signed.json" + }] +} +properties: + server_keys: + type: array + title: Server Keys + description: The queried server's keys, signed by the notary server. + items: + $ref: "keys.yaml" diff --git a/api/server-server/definitions/pdu.yaml b/api/server-server/definitions/pdu.yaml new file mode 100644 index 00000000..bb14ede2 --- /dev/null +++ b/api/server-server/definitions/pdu.yaml @@ -0,0 +1,52 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Persistent Data Unit +description: A persistent data unit (event) +example: + $ref: "../examples/pdu.json" +allOf: + - $ref: "unsigned_pdu.yaml" + - type: object + properties: + hashes: + type: object + title: Event Hash + description: Hashes of the PDU, following the algorithm specified in `Signing Events`_. + example: { + "sha256": "thishashcoversallfieldsincasethisisredacted" + } + properties: + sha256: + type: string + description: The hash. + example: thishashcoversallfieldsincasthisisredacted + required: ['sha256'] + signatures: + type: object + description: |- + Signatures for the PDU, following the algorithm specified in `Signing Events`_. + example: { + "example.com": { + "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus" + } + } + additionalProperties: + type: object + title: Server Signatures + additionalProperties: + type: string + required: + - hashes + - signatures diff --git a/api/server-server/definitions/security.yaml b/api/server-server/definitions/security.yaml new file mode 100644 index 00000000..6c9cc808 --- /dev/null +++ b/api/server-server/definitions/security.yaml @@ -0,0 +1,19 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +signedRequest: + type: apiKey + description: |- + The ``Authorization`` header defined in the `Authentication`_ section. + name: Authorization + in: header diff --git a/api/server-server/definitions/transaction.yaml b/api/server-server/definitions/transaction.yaml new file mode 100644 index 00000000..7df8b646 --- /dev/null +++ b/api/server-server/definitions/transaction.yaml @@ -0,0 +1,37 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Transaction +description: Transaction +example: + $ref: "../examples/transaction.json" +properties: + origin: + type: string + description: |- + The ``server_name`` of the homeserver sending this transaction. + example: "example.org" + origin_server_ts: + type: integer + format: int64 + description: |- + POSIX timestamp in milliseconds on originating homeserver when this + transaction started. + example: 1532991320875 + pdus: + type: array + description: List of persistent updates to rooms. + items: + $ref: "pdu.yaml" +required: ['origin', 'origin_server_ts', 'pdus'] diff --git a/api/server-server/definitions/unsigned_pdu.yaml b/api/server-server/definitions/unsigned_pdu.yaml new file mode 100644 index 00000000..ab281224 --- /dev/null +++ b/api/server-server/definitions/unsigned_pdu.yaml @@ -0,0 +1,152 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +type: object +title: Unsigned Persistent Data Unit +description: An unsigned persistent data unit (event) +example: + $ref: "../examples/unsigned_pdu.json" +properties: + event_id: + type: string + description: The event ID for the PDU. + example: "$a4ecee13e2accdadf56c1025:example.com" + room_id: + type: string + description: Room identifier. + example: "!abc123:matrix.org" + sender: + type: string + description: The ID of the user sending the event. + example: "@someone:matrix.org" + origin: + type: string + description: The ``server_name`` of the homeserver that created this event. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: Timestamp in milliseconds on origin homeserver when this event was created. + example: 1234567890 + type: + type: string + description: Event type + example: "m.room.message" + state_key: + type: string + description: |- + If this key is present, the event is a state event, and it will replace previous events + with the same ``type`` and ``state_key`` in the room state. + example: "my_key" + content: + type: object + description: The content of the event. + example: {"key": "value"} + prev_events: + type: array + description: |- + Event IDs and hashes of the most recent events in the room that the homeserver was aware + of when it made this event. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + depth: + type: integer + description: The maximum depth of the ``prev_events``, plus one. + example: 12 + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow this event to be in the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: For redaction events, the ID of the event being redacted. + example: "$def456:matrix.org" + unsigned: + type: object + title: Example Unsigned Data + description: |- + Additional data added by the origin server but not covered by the ``signatures``. More + keys than those defined here may be used. + example: {"key": "value"} + properties: + age: + type: integer + description: The number of milliseconds that have passed since this message was sent. + example: 4612 + replaces_state: + type: string + description: The event ID of the state event this event replaces. + example: "$state_event:domain.com" + prev_sender: + type: string + description: The sender of the replaced state event. + example: "@someone:domain.com" + prev_content: + type: object + description: The content of the replaced state event. + example: { + "membership": "join", + "displayname": "Bob" + } + redacted_because: + type: string + description: A reason for why the event was redacted. + example: "Inappropriate content" +required: + - event_id + - room_id + - sender + - origin + - origin_server_ts + - type + - content + - prev_events + - depth + - auth_events diff --git a/api/server-server/directory.yaml b/api/server-server/directory.yaml deleted file mode 100644 index c4a06231..00000000 --- a/api/server-server/directory.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2017 Kamax.io -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -swagger: '2.0' -info: - title: "Matrix Federation Query Directory API" - version: "1.0.0" -host: localhost:8448 -schemes: - - https -basePath: /_matrix/federation/v1 -produces: - - application/json -paths: - "/query/directory": - get: - summary: Retrieve the room ID and list of resident homeservers for a room - alias. - description: Retrieve the room ID and list of resident homeservers for a Room - alias. - parameters: - - in: query - name: room_alias - type: string - description: Room alias - required: true - x-example: "#room_alias:example.org" - responses: - 200: - description: The corresponding room ID and list of known resident - homeservers for the room. - schema: - type: object - properties: - room_id: - type: string - description: The room ID mapped to the queried room alias. - x-example: "!roomid1234:example.org" - servers: - type: array - description: An array of server names that are likely to hold - then given room. This list may or may not include the server - answering the query. - items: - type: string - required: - - "room_id" - - "servers" - examples: - application/json: { - "room_id": "!roomid1234:example.org", - "servers": [ - "example.org", - "example.com", - "another.example.com:8449", - ] - } \ No newline at end of file diff --git a/api/server-server/event_auth.yaml b/api/server-server/event_auth.yaml new file mode 100644 index 00000000..8857131f --- /dev/null +++ b/api/server-server/event_auth.yaml @@ -0,0 +1,180 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Event Authorization API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/event_auth/{roomId}/{eventId}": + get: + summary: Get the auth chain for a given event + description: |- + Retrieves the complete auth chain for a given event. + operationId: getEventAuth + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID to get the auth chain for. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID to get the auth chain of. + required: true + x-example: "$helloworld:domain.com" + responses: + 200: + description: The auth chain for the event. + schema: + type: object + properties: + auth_chain: + type: array + description: |- + The full set of authorization events that make up the state of + the room, and their authorization events, recursively. + items: + $ref: "definitions/pdu.yaml" + example: [{"$ref": "examples/pdu.json"}] + required: ['auth_chain'] + "/query_auth/{roomId}/{eventId}": + post: + summary: Compare auth chains with the receiving server + description: |- + Compares the auth chain provided with what the receiving server has for the + room ID and event ID combination. + + The auth difference can be calculated in two parts, where the "remote auth" + is the auth chain provided by the sending server and the "local auth" is the + auth chain the receiving server has. With those lists, the algorithm works + bottom-up after sorting each chain by depth then by event ID. The differences + are then discovered and returned as the response to this API call. + operationId: compareEventAuth + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID to compare the auth chain in. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID to compare the auth chain of. + required: true + x-example: "$helloworld:domain.com" + - in: body + name: body + schema: + type: object + properties: + auth_chain: + type: array + description: The auth chain (the "remote auth"). + items: + $ref: "definitions/pdu.yaml" + example: [{"$ref": "examples/pdu.json"}] + missing: + type: array + description: |- + A list of event IDs that the sender thinks the receiver is missing. + items: + type: string + example: [] + rejects: + type: object + description: |- + The set of events that the sending server has rejected from the provided + auth chain. + + The ``string`` key is the event ID that was rejected. + additionalProperties: + type: object + title: Rejection Reason + properties: + reason: + type: enum + enum: ['auth_error', 'replaced', 'not_ancestor'] + description: |- + The reason for the event being rejected. + required: ['reason'] + example: { + "$some_event:domain.com": { + "reason": "auth_error" + } + } + required: ['auth_chain'] + responses: + 200: + description: The auth chain differences, as determined by the receiver. + schema: + type: object + properties: + auth_chain: + type: array + description: |- + The auth chain the receiver has, and used to determine the auth + chain differences (the "local auth"). + items: + $ref: "definitions/pdu.yaml" + example: [{"$ref": "examples/pdu.json"}] + missing: + type: array + description: |- + The list of event IDs that the receiver believes it is missing, + after comparing the "remote auth" and "local auth" chains. + items: + type: string + example: ["$a_missing_event:domain.com"] + rejects: + type: object + description: |- + The set of events that the receiving server has rejected from the + auth chain, not including events that the sending server is missing + as determined from the difference algorithm. + + The ``string`` key is the event ID that was rejected. + additionalProperties: + type: object + title: Rejection Reason + properties: + reason: + type: enum + enum: ['auth_error', 'replaced', 'not_ancestor'] + description: |- + The reason for the event being rejected. + required: ['reason'] + example: { + "$some_event:domain.com": { + "reason": "auth_error" + } + } + required: ['auth_chain', 'missing', 'rejects'] diff --git a/api/server-server/events.yaml b/api/server-server/events.yaml new file mode 100644 index 00000000..cf3988a2 --- /dev/null +++ b/api/server-server/events.yaml @@ -0,0 +1,139 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Events API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/state/{roomId}": + get: + summary: Get all the state of a given room + description: |- + Retrieves a snapshot of a room's state at a given event. + operationId: getRoomState + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID to get state for. + required: true + x-example: "!abc123:matrix.org" + - in: query + name: event_id + type: string + description: An event ID in the room to retrieve the state at. + required: true + x-example: "$helloworld:matrix.org" + responses: + 200: + description: |- + The fully resolved state for the room, including the authorization + chain for the events. + schema: + type: object + properties: + auth_chain: + type: array + description: |- + The full set of authorization events that make up the state + of the room, and their authorization events, recursively. + items: + $ref: "definitions/pdu.yaml" + example: [{"$ref": "examples/pdu.json"}] + pdus: + type: array + description: |- + The fully resolved state of the room at the given event. + items: + $ref: "definitions/pdu.yaml" + example: [{"$ref": "examples/pdu.json"}] + required: ['auth_chain', 'pdus'] + "/state_ids/{roomId}": + get: + summary: Get all the state event IDs of a given room + description: |- + Retrieves a snapshot of a room's state at a given event, in the form of + event IDs. This performs the same function as calling ``/state/{roomId}``, + however this returns just the event IDs rather than the full events. + operationId: getRoomStateIds + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID to get state for. + required: true + x-example: "!abc123:matrix.org" + - in: query + name: event_id + type: string + description: An event ID in the room to retrieve the state at. + required: true + x-example: "$helloworld:matrix.org" + responses: + 200: + description: |- + The fully resolved state for the room, including the authorization + chain for the events. + schema: + type: object + properties: + auth_chain_ids: + type: array + description: |- + The full set of authorization events that make up the state + of the room, and their authorization events, recursively. + items: + type: string + example: ["$an_event:domain.com"] + pdu_ids: + type: array + description: |- + The fully resolved state of the room at the given event. + items: + type: string + example: ["$an_event:domain.com"] + required: ['auth_chain_ids', 'pdu_ids'] + "/event/{eventId}": + get: + summary: Get a single event + description: |- + Retrieves a single event. + operationId: getEvent + security: + - signedRequest: [] + parameters: + - in: path + name: eventId + type: string + description: The event ID to get. + required: true + x-example: "$abc123:matrix.org" + responses: + 200: + description: A transaction containing a single PDU which is the event requested. + schema: + $ref: "definitions/transaction.yaml" diff --git a/api/server-server/examples/edu.json b/api/server-server/examples/edu.json new file mode 100644 index 00000000..f5a58e21 --- /dev/null +++ b/api/server-server/examples/edu.json @@ -0,0 +1,6 @@ +{ + "edu_type": "m.presence", + "content": { + "key": "value" + } +} \ No newline at end of file diff --git a/api/server-server/examples/pdu.json b/api/server-server/examples/pdu.json new file mode 100644 index 00000000..81981b23 --- /dev/null +++ b/api/server-server/examples/pdu.json @@ -0,0 +1,11 @@ +{ + "$ref": "unsigned_pdu.json", + "hashes": { + "sha256": "thishashcoversallfieldsincasethisisredacted" + }, + "signatures": { + "example.com": { + "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus" + } + } +} \ No newline at end of file diff --git a/api/server-server/examples/server_key.json b/api/server-server/examples/server_key.json new file mode 100644 index 00000000..bebd8445 --- /dev/null +++ b/api/server-server/examples/server_key.json @@ -0,0 +1,23 @@ +{ + "server_name": "example.org", + "verify_keys": { + "ed25519:abc123": { + "key": "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA" + } + }, + "old_verify_keys": { + "ed25519:0ldk3y": { + "expired_ts": 1532645052628, + "key": "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg" + } + }, + "signatures": { + "example.org": { + "ed25519:auto2": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU" + } + }, + "tls_fingerprints": [{ + "sha256": "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw" + }], + "valid_until_ts": 1652262000000 +} \ No newline at end of file diff --git a/api/server-server/examples/server_key_notary_signed.json b/api/server-server/examples/server_key_notary_signed.json new file mode 100644 index 00000000..d3a461ba --- /dev/null +++ b/api/server-server/examples/server_key_notary_signed.json @@ -0,0 +1,11 @@ +{ + "$ref": "server_key.json", + "signatures": { + "example.org": { + "ed25519:abc123": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU" + }, + "notary.server.com": { + "ed25519:010203": "VGhpcyBpcyBhbm90aGVyIHNpZ25hdHVyZQ" + } + } +} \ No newline at end of file diff --git a/api/server-server/examples/transaction.json b/api/server-server/examples/transaction.json new file mode 100644 index 00000000..bd8ac3dc --- /dev/null +++ b/api/server-server/examples/transaction.json @@ -0,0 +1,5 @@ +{ + "origin": "matrix.org", + "origin_server_ts": 1234567890, + "pdus": [{"$ref": "pdu.json"}] +} \ No newline at end of file diff --git a/api/server-server/examples/unsigned_pdu.json b/api/server-server/examples/unsigned_pdu.json new file mode 100644 index 00000000..f4d2e749 --- /dev/null +++ b/api/server-server/examples/unsigned_pdu.json @@ -0,0 +1,27 @@ +{ + "room_id": "!UcYsUzyxTGDxLBEvLy:example.org", + "sender": "@alice:example.com", + "origin": "example.com", + "event_id": "$a4ecee13e2accdadf56c1025:example.com", + "origin_server_ts": 1404838188000, + "depth": 12, + "auth_events": [ + [ + "$af232176:example.org", + {"sha256": "abase64encodedsha256hashshouldbe43byteslong"} + ] + ], + "type": "m.room.message", + "prev_events": [ + [ + "$af232176:example.org", + {"sha256": "abase64encodedsha256hashshouldbe43byteslong"} + ] + ], + "content": { + "key": "value" + }, + "unsigned": { + "age": 4612 + } +} \ No newline at end of file diff --git a/api/server-server/invites.yaml b/api/server-server/invites.yaml new file mode 100644 index 00000000..6d905e17 --- /dev/null +++ b/api/server-server/invites.yaml @@ -0,0 +1,213 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Invite User To Room API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/invite/{roomId}/{eventId}": + put: + summary: Invites a remote user to a room + description: |- + Invites a remote user to a room. Once the event has been signed by both the inviting + homeserver and the invited homeserver, it can be sent to all of the servers in the + room by the inviting homeserver. + operationId: sendInvite + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID that the user is being invited to. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID for the invite event, generated by the inviting server. + required: true + x-example: "$abc123:example.org" + - in: body + name: body + type: object + required: true + schema: + allOf: + - $ref: "definitions/invite_event.yaml" + - type: object + properties: + unsigned: + type: object + title: Unsigned Event Content + description: |- + Information included alongside the event that is not signed. May include more + than what is listed here. + properties: + invite_room_state: + type: array + description: |- + An optional list of simplified events to help the receiver of the invite + identify the room. The recommended events to include are the join rules, + canonical alias, avatar, and name of the room. + items: + type: object + title: Invite Room State Event + properties: + type: + type: string + description: The type of event. + example: "m.room.join_rules" + state_key: + type: string + description: The state key for the event. May be an empty string. + example: "" + content: + type: object + description: The content for the event. + sender: + type: string + description: The sender of the event. + example: "@someone:matrix.org" + required: ['type', 'state_key', 'content', 'sender'] + example: [ + { + "type": "m.room.join_rules", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "join_rule": "public" + } + } + ] + example: { + "$ref": "examples/pdu.json", + "type": "m.room.member", + "state_key": "@joe:elsewhere.com", + "unsigned": { + "invite_room_state": [ + { + "type": "m.room.join_rules", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "join_rule": "public" + } + }, + { + "type": "m.room.name", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "name": "Cool New Room" + } + } + ] + }, + "content": { + "membership": "invite" + }, + "signatures": { + "example.com": { + "ed25519:key_version": "SomeSignatureHere" + }, + } + } + responses: + 200: + description: |- + The event with the invited server's signature added. All other fields of the events + should remain untouched. + schema: + type: array + minItems: 2 + maxItems: 2 + items: + - type: integer + description: The value ``200``. + example: 200 + - type: object + description: An object containing the signed invite event. + title: Event Container + properties: + event: + $ref: "definitions/invite_event.yaml" + required: ['event'] + examples: + application/json: [ + 200, + { + "event": { + "$ref": "examples/pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "unsigned": { + "invite_room_state": [ + { + "type": "m.room.join_rules", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "join_rule": "public" + } + }, + { + "type": "m.room.name", + "sender": "@someone:matrix.org", + "state_key": "", + "content": { + "name": "Cool New Room" + } + } + ] + }, + "content": { + "membership": "invite" + }, + "signatures": { + "example.com": { + "ed25519:key_version": "SomeSignatureHere" + }, + "elsewhere.com": { + "ed25519:k3y_versi0n": "SomeOtherSignatureHere" + } + } + } + } + ] + 403: + description: |- + The invite is not allowed. This could be for a number of reasons, including: + + * The sender is not allowed to send invites to the target user/homeserver. + * The homeserver does not permit anyone to invite its users. + * The homeserver refuses to participate in the room. + schema: + $ref: "../client-server/definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "User cannot invite the target user." + } diff --git a/api/server-server/joins.yaml b/api/server-server/joins.yaml new file mode 100644 index 00000000..e76a0aa6 --- /dev/null +++ b/api/server-server/joins.yaml @@ -0,0 +1,293 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Join Room API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/make_join/{roomId}/{userId}": + get: + summary: Get information required to make a join event for a room + description: |- + Asks the receiving server to return information that the sending + server will need to prepare a join event to get into the room. + operationId: makeJoin + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID that is about to be joined. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: userId + type: string + description: The user ID the join event will be for. + required: true + x-example: "@someone:example.org" + responses: + 200: + description: |- + An unsigned event that the server may now use as a template + for the rest of the `Joining Rooms`_ handshake. + schema: + allOf: + - $ref: "definitions/unsigned_pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + origin: + type: string + description: The name of the resident homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the resident homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "join"} + properties: + membership: + type: string + description: The value ``join``. + example: "join" + required: ['membership'] + depth: + type: integer + description: This field must be present but is ignored; it may be 0. + example: 12 + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to join the room. This should normally be the + ``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules`` + events. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key + examples: + application/json: { + "$ref": "examples/unsigned_pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "join" + }, + "auth_events": [ + ["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], + ["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], + ["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}] + ] + } + "/send_join/{roomId}/{eventId}": + put: + summary: Submit a signed join event to a resident server + description: |- + Submits a signed join event to the resident server for it + to accept it into the room's graph. + operationId: sendJoin + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID that is about to be joined. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID for the join event. + required: true + x-example: "$abc123:example.org" + - in: body + name: body + type: object + required: true + schema: + allOf: + - $ref: "definitions/pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + origin: + type: string + description: The name of the joining homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the joining homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the joining member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "join"} + properties: + membership: + type: string + description: The value ``join``. + example: "join" + required: ['membership'] + depth: + type: integer + description: This field must be present but is ignored; it may be 0. + example: 12 + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to join the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key + example: { + "$ref": "examples/pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "join" + } + } + responses: + 200: + description: |- + The full state for the room, having accepted the join event. + schema: + type: array + minItems: 2 + maxItems: 2 + items: + - type: integer + description: The value ``200``. + example: 200 + - type: object + title: Room State + description: The state for the room. + properties: + origin: + type: string + description: The resident server's DNS name. + auth_chain: + type: array + description: The auth chain. + items: + type: object + schema: + $ref: "definitions/pdu.yaml" + state: + type: array + description: The room state. + items: + type: object + schema: + $ref: "definitions/pdu.yaml" + required: ["auth_chain", "state", "origin"] + examples: + application/json: [ + 200, + { + "origin": "matrix.org", + "auth_chain": [{"$ref": "examples/pdu.json"}], + "state": [{"$ref": "examples/pdu.json"}] + } + ] diff --git a/api/server-server/keys_query.yaml b/api/server-server/keys_query.yaml new file mode 100644 index 00000000..e616915b --- /dev/null +++ b/api/server-server/keys_query.yaml @@ -0,0 +1,131 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Key Exchange API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/key/v2 +consumes: + - application/json +produces: + - application/json +paths: + "/query/{serverName}/{keyId}": + get: + summary: Query for another server's keys + description: |- + Query for another server's keys. The receiving (notary) server must + sign the keys returned by the queried server. + operationId: perspectivesKeyQuery + parameters: + - in: path + name: serverName + type: string + description: The server's DNS name to query + required: true + x-example: matrix.org + - in: path + name: keyId + 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 + look up. + required: false + x-example: "ed25519:abc123" + - in: query + name: minimum_valid_until_ts + type: integer + format: int64 + description: |- + 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. + required: false + x-example: 1234567890 + responses: + 200: + description: |- + The keys for the server, or an empty array if the server could not be reached + and no cached keys were available. + schema: + $ref: "definitions/keys_query_response.yaml" + "/query": + post: + summary: Query for several server's keys + description: |- + Query for keys from multiple servers in a batch format. The receiving (notary) + server must sign the keys returned by the queried servers. + operationId: bulkPerspectivesKeyQuery + parameters: + - in: body + name: body + schema: + type: object + example: { + "server_keys": { + "example.org": { + "ed25519:abc123": { + "minimum_valid_until_ts": 1234567890 + } + } + } + } + properties: + server_keys: + type: object + description: |- + The query criteria. The outer ``string`` key on the object is the + server name (eg: ``matrix.org``). The inner ``string`` key is the + Key ID to query for the particular server. If no key IDs are given + to be queried, the notary server should query for all keys. If no + servers are given, the notary server must return an empty ``server_keys`` + array in the response. + + The notary server may return multiple keys regardless of the Key IDs + given. + additionalProperties: + type: object + name: ServerName + description: The server names to query. + additionalProperties: + type: object + title: Query Criteria + description: The server key IDs to query. + properties: + minimum_valid_until_ts: + type: integer + format: int64 + description: |- + 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. + example: 1234567890 + required: ['server_keys'] + responses: + 200: + description: |- + The keys for the queried servers, signed by the notary server. Servers which + are offline and have no cached keys will not be included in the result. This + may result in an empty array. + schema: + $ref: "definitions/keys_query_response.yaml" diff --git a/api/server-server/keys_server.yaml b/api/server-server/keys_server.yaml new file mode 100644 index 00000000..8734f2ed --- /dev/null +++ b/api/server-server/keys_server.yaml @@ -0,0 +1,61 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Key Exchange API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/key/v2 +produces: + - application/json +paths: + "/server/{keyId}": + get: + summary: Get the homeserver's public key(s) + description: |- + Gets the homeserver's published TLS fingerprints and signing keys. + The homeserver may have any number of active keys and may have a + number of old keys. + + Intermediate notary servers should cache a response for half of its + lifetime to avoid serving a stale response. Originating servers should + avoid returning responses that expire in less than an hour to avoid + repeated reqests for a certificate that is about to expire. Requesting + servers should limit how frequently they query for certificates to + avoid flooding a server with requests. + + If the server fails to respond to this request, intermediate notary + servers should continue to return the last response they received + from the server so that the signatures of old events can still be + checked. + operationId: getServerKey + parameters: + - in: path + name: keyId + 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 + look up. + required: false + x-example: "ed25519:abc123" + deprecated: true + responses: + 200: + description: The homeserver's keys + schema: + $ref: "definitions/keys.yaml" diff --git a/api/server-server/leaving.yaml b/api/server-server/leaving.yaml new file mode 100644 index 00000000..be08acba --- /dev/null +++ b/api/server-server/leaving.yaml @@ -0,0 +1,274 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Leave Room API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/make_leave/{roomId}/{userId}": + get: + summary: Get information required to make a leave event for a room + description: |- + Asks the receiving server to return information that the sending + server will need to prepare a leave event to get out of the room. + operationId: makeLeave + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID that is about to be left. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: userId + type: string + description: The user ID the leave event will be for. + required: true + x-example: "@someone:example.org" + responses: + 200: + description: |- + An unsigned event that the sending server may use as a template + for when it calls ``/send_leave``. + schema: + allOf: + - $ref: "definitions/unsigned_pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + origin: + type: string + description: The name of the resident homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the resident homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "leave"} + properties: + membership: + type: string + description: The value ``leave``. + example: "leave" + required: ['membership'] + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to leave the room. This should normally be the + ``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules`` + events. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key + examples: + application/json: { + "$ref": "examples/unsigned_pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "leave" + }, + "auth_events": [ + ["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], + ["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}], + ["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}] + ] + } + 403: + description: |- + The request is not authorized. This could mean that the user is not in the room. + schema: + $ref: "../client-server/definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "User is not in the room." + } + "/send_leave/{roomId}/{eventId}": + put: + summary: Submit a signed leave event to a resident server + description: |- + Submits a signed leave event to the resident server for it + to accept it into the room's graph. + operationId: sendLeave + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID that is about to be left. + required: true + x-example: "!abc123:matrix.org" + - in: path + name: eventId + type: string + description: The event ID for the leave event. + required: true + x-example: "$abc123:example.org" + - in: body + name: body + type: object + required: true + schema: + allOf: + - $ref: "definitions/pdu.yaml" + - type: object + properties: + # Note: we override a bunch of parameters to change their descriptions + sender: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + origin: + type: string + description: The name of the leaving homeserver. + example: "matrix.org" + origin_server_ts: + type: integer + format: int64 + description: A timestamp added by the leaving homeserver. + example: 1234567890 + type: + type: string + description: The value ``m.room.member``. + example: "m.room.member" + state_key: + type: string + description: The user ID of the leaving member. + example: "@someone:example.org" + content: + type: object + title: Membership Event Content + description: The content of the event. + example: {"membership": "leave"} + properties: + membership: + type: string + description: The value ``leave``. + example: "leave" + required: ['membership'] + depth: + type: integer + description: This field must be present but is ignored; it may be 0. + example: 12 + auth_events: + type: array + description: |- + An event reference list containing the authorization events that would + allow the member to leave the room. + items: + type: array + maxItems: 2 + minItems: 2 + items: + - type: string + title: Event ID + example: "$abc123:matrix.org" + - type: object + title: Event Hash + example: { + "sha256": "abase64encodedsha256hashshouldbe43byteslong" + } + properties: + sha256: + type: string + description: The event hash. + example: abase64encodedsha256hashshouldbe43byteslong + required: ['sha256'] + redacts: + type: string + description: Not used. + required: + # Every other field is already flagged as required by the $ref + - state_key + example: { + "$ref": "examples/pdu.json", + "type": "m.room.member", + "state_key": "@someone:example.org", + "content": { + "membership": "leave" + } + } + responses: + 200: + description: |- + An empty response to indicate the event was accepted into the graph by + the receiving homeserver. + schema: + type: array + minItems: 2 + maxItems: 2 + items: + - type: integer + description: The value ``200``. + example: 200 + - type: object + title: Empty Object + description: An empty object. + examples: + application/json: [200, {}] diff --git a/api/server-server/openid.yaml b/api/server-server/openid.yaml new file mode 100644 index 00000000..0eac48c8 --- /dev/null +++ b/api/server-server/openid.yaml @@ -0,0 +1,63 @@ +# Copyright 2017 Kamax.io +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation OpenID API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +produces: + - application/json +paths: + "/openid/userinfo": + get: + summary: Exchange an OpenID token for user information + description: |- + Exchanges an OpenID access token for information about the user + who generated the token. Currently this only exposes the Matrix + User ID of the owner. + operationId: exchangeOpenIdToken + parameters: + - in: path + name: access_token + type: string + description: |- + The OpenID access token to get information about the owner for. + required: true + x-example: SomeT0kenHere + responses: + 200: + description: |- + Information about the user who generated the OpenID access token. + schema: + type: object + properties: + sub: + type: string + description: The Matrix User ID who generated the token. + example: "@alice:example.com" + required: ['sub'] + 401: + description: The token was not recognized or has expired. + schema: + $ref: "../client-server/definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_UNKNOWN_TOKEN", + "error": "Access token unknown or expired" + } diff --git a/api/server-server/public_rooms.yaml b/api/server-server/public_rooms.yaml new file mode 100644 index 00000000..d162568f --- /dev/null +++ b/api/server-server/public_rooms.yaml @@ -0,0 +1,56 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Public Rooms API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/publicRooms": + get: + summary: Get all the public rooms for a homeserver + description: |- + Gets all the public rooms for the homeserver. This should not return + rooms that are listed on another homeserver's directory, just those + listed on the receiving homeserver's directory. + operationId: getPublicRooms + security: + - signedRequest: [] + parameters: + - in: query + name: limit + type: integer + description: |- + The maximum number of rooms to return. Defaults to 0 (no limit). + x-example: 10 + - in: query + name: since + type: string + description: |- + A pagination token from a previous call to this endpoint to fetch more + rooms. + x-example: "GetMoreRoomsTokenHere" + responses: + 200: + description: The public room list for the homeserver. + schema: + $ref: "../client-server/definitions/public_rooms_response.yaml" diff --git a/api/server-server/query.yaml b/api/server-server/query.yaml new file mode 100644 index 00000000..dc14724c --- /dev/null +++ b/api/server-server/query.yaml @@ -0,0 +1,175 @@ +# Copyright 2017 Kamax.io +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Query API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/query/{queryType}": + get: + summary: Query for information + description: |- + Performs a single query request on the receiving homeserver. The query string + arguments are dependent on which type of query is being made. Known query types + are specified as their own endpoints as an extension to this definition. + operationId: queryInfo + security: + - signedRequest: [] + parameters: + - in: path + name: queryType + type: string + description: The type of query to make + required: true + x-example: profile + responses: + 200: + description: |- + The query response. The schema varies depending on the query being made. + "/query/directory": + get: + summary: Query for the room ID and resident homeservers for a room alias + description: |- + Performs a query to get the mapped room ID and list of resident homeservers in + the room for a given room alias. Homeservers should only query room aliases + that belong to the target server (identified by the DNS Name in the alias). + + Servers may wish to cache the response to this query to avoid requesting the + information too often. + operationId: queryRoomDirectory + security: + - signedRequest: [] + parameters: + - in: query + name: room_alias + type: string + description: The room alias to query. + required: true + x-example: "#room_alias:example.org" + responses: + 200: + description: |- + The corresponding room ID and list of known resident homeservers for the room. + schema: + type: object + properties: + room_id: + type: string + description: The room ID mapped to the queried room alias. + x-example: "!roomid1234:example.org" + servers: + type: array + description: |- + 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 + required: + - "room_id" + - "servers" + examples: + application/json: { + "room_id": "!roomid1234:example.org", + "servers": [ + "example.org", + "example.com", + "another.example.com:8449", + ] + } + 404: + description: The room alias was not found. + schema: + $ref: "../client-server/definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "Room alias not found." + } + "/query/profile": + get: + summary: Query for profile information about a given user + description: |- + Performs a query to get profile information, such as a display name or avatar, + for a given user. Homeservers should only query profiles for users that belong + to the target server (identified by the DNS Name in the user ID). + + Servers may wish to cache the response to this query to avoid requesting the + information too often. + operationId: queryProfile + security: + - signedRequest: [] + parameters: + - in: query + name: user_id + type: string + description: The user ID to query. + required: true + x-example: "@someone:example.org" + - in: query + name: field + type: enum + enum: ['displayname', 'avatar_url'] + description: |- + The field to query. If specified, the server will only return the given field + in the response. If not specified, the server will return the full profile for + the user. + responses: + 200: + 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 + public, such as the display name and avatar. + + If the user does not have a particular field set on their profile, the server + should exclude it from the response body or give it the value ``null``. + schema: + type: object + properties: + displayname: + type: string + description: |- + The display name of the user. May be omitted if the user does not have a + display name set. + x-example: "John Doe" + avatar_url: + type: string + description: |- + The avatar URL for the user's avatar. May be omitted if the user does not + have an avatar set. + x-example: "mxc://matrix.org/MyC00lAvatar" + examples: + application/json: { + "displayname": "John Doe", + "avatar_url": "mxc://matrix.org/MyC00lAvatar" + } + 404: + description: The user does not exist or does not have a profile. + schema: + $ref: "../client-server/definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "User does not exist." + } diff --git a/api/server-server/third_party_invite.yaml b/api/server-server/third_party_invite.yaml new file mode 100644 index 00000000..5c12247c --- /dev/null +++ b/api/server-server/third_party_invite.yaml @@ -0,0 +1,196 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Third Party Invites API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/exchange_third_party_invite/{roomId}": + put: + summary: Request a server to auth a third party invite event + description: |- + The receiving server will verify the partial ``m.room.member`` event + given in the request body. If valid, the receiving server will issue + an invite as per the `Inviting to a room`_ section before returning a + response to this request. + operationId: exchangeThirdPartyInvite + security: + - signedRequest: [] + parameters: + - in: path + name: roomId + type: string + description: The room ID to exchange a third party invite in + required: true + x-example: "!abc123:matrix.org" + - in: body + name: body + type: object + description: A partial ``m.room.member`` event + required: true + schema: + type: object + properties: + type: + type: string + description: The event type. Must be ``m.room.member`` + example: "m.room.member" + room_id: + type: string + description: |- + The room ID the event is for. Must match the ID given in + the path. + example: "!abc123:matrix.org" + sender: + type: string + description: |- + The user ID of the user who sent the original ``m.room.third_party_invite`` + event. + example: "@joe:matrix.org" + state_key: + type: string + description: The user ID of the invited user + example: "@someone:example.org" + content: + type: object + description: The event content + title: Event Content + properties: + membership: + type: string + description: The membership state. Must be ``invite`` + example: invite + third_party_invite: + type: object + description: The third party invite + properties: + display_name: + type: string + description: |- + A name which can be displayed to represent the user instead of their + third party identifier. + example: "alice" + signed: + type: object + description: |- + A block of content which has been signed, which servers can use to + verify the event. + properties: + signatures: + type: object + description: The server signatures for this event. + additionalProperties: + type: object + title: Server Signatures + additionalProperties: + type: string + example: { + "magic.forest": { + "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } + } + mxid: + type: string + description: The invited matrix user ID + example: "@alice:localhost" + token: + type: string + description: The token used to verify the event + example: abc123 + required: ['signatures', 'mxid', 'token'] + example: { + "mxid": "@alice:localhost", + "token": "abc123", + "signatures": { + "magic.forest": { + "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } + } + } + required: ['display_name', 'signed'] + example: { + "display_name": "alice", + "signed": { + "mxid": "@alice:localhost", + "token": "abc123", + "signatures": { + "magic.forest": { + "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } + } + } + } + required: ['membership', 'third_party_invite'] + example: { + "membership": "invite", + "third_party_invite": { + "display_name": "alice", + "signed": { + "mxid": "@alice:localhost", + "token": "abc123", + "signatures": { + "magic.forest": { + "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } + } + } + } + } + required: + - type + - room_id + - sender + - state_key + - content + example: { + "type": "m.room.member", + "room_id": "!abc123:matrix.org", + "sender": "@joe:matrix.org", + "state_key": "@someone:example.org", + "content": { + "membership": "invite", + "third_party_invite": { + "display_name": "alice", + "signed": { + "mxid": "@alice:localhost", + "token": "abc123", + "signatures": { + "magic.forest": { + "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg" + } + } + } + } + } + } + responses: + 200: + description: The invite has been issued successfully. + examples: + application/json: {} + schema: + type: object + description: An empty object + example: {} diff --git a/api/server-server/transactions.yaml b/api/server-server/transactions.yaml new file mode 100644 index 00000000..8d810ad5 --- /dev/null +++ b/api/server-server/transactions.yaml @@ -0,0 +1,116 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +swagger: '2.0' +info: + title: "Matrix Federation Transaction API" + version: "1.0.0" +host: localhost:8448 +schemes: + - https +basePath: /_matrix/federation/v1 +consumes: + - application/json +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/send/{txnId}": + put: + summary: Send a transaction + description: |- + Push messages representing live activity to another server. The destination name + will be set to that of the receiving server itself. Each embedded PDU in the + transaction body will be processed. + + The sending server must wait and retry for a 200 OK response before sending a + transaction with a different ``txnId`` to the receiving server. + operationId: sendTransaction + security: + - signedRequest: [] + parameters: + - in: path + name: txnId + type: string + description: |- + The transaction ID. + required: true + x-example: S0meTransacti0nId + - in: body + name: body + type: object + required: true + schema: + allOf: + - $ref: "definitions/transaction.yaml" + - type: object + properties: + edus: + type: array + description: |- + List of ephemeral messages. May be omitted if there are no ephemeral + messages to be sent. + items: + $ref: "definitions/edu.yaml" + example: { + "$ref": "examples/transaction.json", + "edus": [{"$ref": "edu.json"}] # Relative to the examples directory + } + responses: + 200: + description: |- + The result of processing the transaction. The server is to use this response even in + the event of one or more PDUs failing to be processed. + schema: + type: array + minItems: 2 + maxItems: 2 + items: + - type: integer + description: The value ``200``. + example: 200 + - type: object + title: PDU Processing Results + description: The results for the processing of each PDU in the transaction. + properties: + pdus: + type: object + description: |- + The PDUs from the original transaction. The string key represents the ID of the + PDU (event) that was processed. + additionalProperties: + type: object + title: PDU Processing Result + description: Information about how the PDU was handled. + properties: + error: + type: string + description: |- + A human readable description about what went wrong in processing this PDU. + If no error is present, the PDU can be considered successfully handled. + example: "You are not allowed to send a message to this room." + required: ['pdus'] + examples: + application/json: [ + 200, + { + "pdus": { + "$successful_event:domain.com": {}, + "$failed_event:example.org": { + "error": "You are not allowed to send a message to this room." + } + } + } + ] diff --git a/changelogs/README.md b/changelogs/README.md new file mode 100644 index 00000000..a5fb1fb7 --- /dev/null +++ b/changelogs/README.md @@ -0,0 +1,55 @@ + + +# Changelogs + +[Towncrier](https://github.com/hawkowl/towncrier) is used to manage the changelog and +keep it up to date. Because of this, updating a changelog is really easy. + +## How to update a changelog when releasing an API + +1. Ensure you're in your Python 3 virtual environment +2. `cd` your way to the API you're releasing (eg: `cd changelogs/client_server`) +3. Run `towncrier --version "r0.4.0" --name "client-server" --yes` substituting the + variables as approprite. Note that `--name` is required although the value is ignored. +4. Commit the changes and finish the release process. + +## How to prepare a changelog for a new API + +For this example, we're going to pretend that the `server_server` API doesn't exist. + +1. Create the file `changelogs/server_server.rst` +2. Create the folder `changelogs/server_server` +3. In the new folder, create a `pyproject.toml` file with these contents: + ```toml + [tool.towncrier] + filename = "../server_server.rst" + directory = "newsfragments" + issue_format = "`#{issue} `_" + title_format = "{version}" + + [[tool.towncrier.type]] + directory = "breaking" + name = "Breaking Changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "deprecation" + name = "Deprecations" + showcontent = true + + [[tool.towncrier.type]] + directory = "new" + name = "New Endpoints" + showcontent = true + + [[tool.towncrier.type]] + directory = "feature" + name = "Backwards Compatible Changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "clarification" + name = "Spec Clarifications" + showcontent = true + ``` +4. Create a `.gitignore` in `changelogs/server_server/newsfragments` with the contents `!.gitignore` diff --git a/changelogs/client_server.rst b/changelogs/client_server.rst index 24344116..d2ef7f2d 100644 --- a/changelogs/client_server.rst +++ b/changelogs/client_server.rst @@ -1,62 +1,3 @@ -Unreleased changes -================== - -- Changes to the API which will be backwards-compatible for clients: - - - New endpoints: - - - ``POST /user_directory/search`` - (`#1096 `_). - - ``GET /rooms/{roomId}/event/{eventId}`` - (`#1110 `_). - - ``POST /delete_devices`` - (`#1239 `_). - - - Sticker messages: - - Add sticker message event definition. - (`#1158 `_). - -- Spec clarifications: - - - Update ``ImageInfo`` and ``ThumbnailInfo`` dimension schema descriptions - to clarify that they relate to intended display size, as opposed to the - intrinsic size of the image file. - (`#1158 `_). - - Mark ``home_server`` return field for ``/login`` and ``/register`` - endpoints as deprecated - (`#1097 `_). - - Fix response format of ``/keys/changes`` endpoint - (`#1106 `_). - - Clarify default values for some fields on the /search API - (`#1109 `_). - - Fix the representation of ``m.presence`` events - (`#1137 `_). - - Clarify that ``m.tag`` ordering is done with numbers, not strings - (`#1139 `_). - - Clarify that ``/account/whoami`` should consider application services - (`#1152 `_). - - Mark ``GET /rooms/{roomId}/members`` as requiring authentication - (`#1245 `_). - -- Changes to the API which will be backwards-compatible for clients: - - - Add 'token' parameter to /keys/query endpoint - (`#1104 `_). - - Add the room visibility options for the room directory - (`#1141 `_). - - Add spec for ignoring users - (`#1142 `_). - - Add the ``/register/available`` endpoint for username availability - (`#1151 `_). - - Add ``allow_remote`` to the content repo to avoid routing loops - (`#1265 `_). - - Add report content API - (`#1264 `_). - - Document ``/logout/all`` endpoint - (`#1263 `_). - - Document `highlights` field in /search response - (`#1274 `_). - r0.3.0 ====== diff --git a/changelogs/client_server/newsfragments/.gitignore b/changelogs/client_server/newsfragments/.gitignore new file mode 100644 index 00000000..b722e9e1 --- /dev/null +++ b/changelogs/client_server/newsfragments/.gitignore @@ -0,0 +1 @@ +!.gitignore \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1096.new b/changelogs/client_server/newsfragments/1096.new new file mode 100644 index 00000000..50d86879 --- /dev/null +++ b/changelogs/client_server/newsfragments/1096.new @@ -0,0 +1 @@ +``POST /user_directory/search`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1097.clarification b/changelogs/client_server/newsfragments/1097.clarification new file mode 100644 index 00000000..2a7cb93a --- /dev/null +++ b/changelogs/client_server/newsfragments/1097.clarification @@ -0,0 +1 @@ +Mark ``home_server`` return field for ``/login`` and ``/register`` endpoints as deprecated \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1104.feature b/changelogs/client_server/newsfragments/1104.feature new file mode 100644 index 00000000..9b85343f --- /dev/null +++ b/changelogs/client_server/newsfragments/1104.feature @@ -0,0 +1 @@ +Add ``token`` parameter to the ``/keys/query`` endpoint \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1106.clarification b/changelogs/client_server/newsfragments/1106.clarification new file mode 100644 index 00000000..f7a1fe3e --- /dev/null +++ b/changelogs/client_server/newsfragments/1106.clarification @@ -0,0 +1 @@ +Fix response format of ``/keys/changes`` endpoint \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1109.clarification b/changelogs/client_server/newsfragments/1109.clarification new file mode 100644 index 00000000..176d9403 --- /dev/null +++ b/changelogs/client_server/newsfragments/1109.clarification @@ -0,0 +1 @@ +Clarify default values for some fields on the ``/search`` API \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1110.new b/changelogs/client_server/newsfragments/1110.new new file mode 100644 index 00000000..e1b80b8b --- /dev/null +++ b/changelogs/client_server/newsfragments/1110.new @@ -0,0 +1 @@ +``GET /rooms/{roomId}/event/{eventId}`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1137.clarification b/changelogs/client_server/newsfragments/1137.clarification new file mode 100644 index 00000000..5ad8ec34 --- /dev/null +++ b/changelogs/client_server/newsfragments/1137.clarification @@ -0,0 +1 @@ +Fix the representation of ``m.presence`` events \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1139.clarification b/changelogs/client_server/newsfragments/1139.clarification new file mode 100644 index 00000000..b5193ad3 --- /dev/null +++ b/changelogs/client_server/newsfragments/1139.clarification @@ -0,0 +1 @@ +Clarify that ``m.tag`` ordering is done with numbers, not strings \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1141.feature b/changelogs/client_server/newsfragments/1141.feature new file mode 100644 index 00000000..da041819 --- /dev/null +++ b/changelogs/client_server/newsfragments/1141.feature @@ -0,0 +1 @@ +Add the room visibility options for the room directory \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1142.feature b/changelogs/client_server/newsfragments/1142.feature new file mode 100644 index 00000000..0a0842c4 --- /dev/null +++ b/changelogs/client_server/newsfragments/1142.feature @@ -0,0 +1 @@ +Add spec for ignoring users \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1151.feature b/changelogs/client_server/newsfragments/1151.feature new file mode 100644 index 00000000..8875812b --- /dev/null +++ b/changelogs/client_server/newsfragments/1151.feature @@ -0,0 +1 @@ +Add the ``/register/available`` endpoint for username availability \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1152.clarification b/changelogs/client_server/newsfragments/1152.clarification new file mode 100644 index 00000000..bbecc9b2 --- /dev/null +++ b/changelogs/client_server/newsfragments/1152.clarification @@ -0,0 +1 @@ +Clarify that ``/account/whoami`` should consider application services \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1158.clarification b/changelogs/client_server/newsfragments/1158.clarification new file mode 100644 index 00000000..dc1f6d14 --- /dev/null +++ b/changelogs/client_server/newsfragments/1158.clarification @@ -0,0 +1,3 @@ +Update ``ImageInfo`` and ``ThumbnailInfo`` dimension schema descriptions +to clarify that they relate to intended display size, as opposed to the +intrinsic size of the image file. \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1158.feature b/changelogs/client_server/newsfragments/1158.feature new file mode 100644 index 00000000..a55df4fb --- /dev/null +++ b/changelogs/client_server/newsfragments/1158.feature @@ -0,0 +1 @@ +Add sticker messages \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1239.new b/changelogs/client_server/newsfragments/1239.new new file mode 100644 index 00000000..9bcf357d --- /dev/null +++ b/changelogs/client_server/newsfragments/1239.new @@ -0,0 +1 @@ +``POST /delete_devices`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1245.clarification b/changelogs/client_server/newsfragments/1245.clarification new file mode 100644 index 00000000..e0a63834 --- /dev/null +++ b/changelogs/client_server/newsfragments/1245.clarification @@ -0,0 +1 @@ +Mark ``GET /rooms/{roomId}/members`` as requiring authentication \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1263.feature b/changelogs/client_server/newsfragments/1263.feature new file mode 100644 index 00000000..04964a7d --- /dev/null +++ b/changelogs/client_server/newsfragments/1263.feature @@ -0,0 +1 @@ +Document ``/logout/all`` endpoint \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1264.feature b/changelogs/client_server/newsfragments/1264.feature new file mode 100644 index 00000000..9cb06f71 --- /dev/null +++ b/changelogs/client_server/newsfragments/1264.feature @@ -0,0 +1 @@ +Add report content API \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1265.feature b/changelogs/client_server/newsfragments/1265.feature new file mode 100644 index 00000000..1e270fa8 --- /dev/null +++ b/changelogs/client_server/newsfragments/1265.feature @@ -0,0 +1 @@ +Add ``allow_remote`` to the content repo to avoid routing loops \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1274.feature b/changelogs/client_server/newsfragments/1274.feature new file mode 100644 index 00000000..d4958131 --- /dev/null +++ b/changelogs/client_server/newsfragments/1274.feature @@ -0,0 +1 @@ +Document `highlights` field in /search response \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1284.clarification b/changelogs/client_server/newsfragments/1284.clarification new file mode 100644 index 00000000..7bc92f47 --- /dev/null +++ b/changelogs/client_server/newsfragments/1284.clarification @@ -0,0 +1 @@ +Clarify ``changed`` field behaviour in device tracking process diff --git a/changelogs/client_server/newsfragments/1284.feature b/changelogs/client_server/newsfragments/1284.feature new file mode 100644 index 00000000..c658142e --- /dev/null +++ b/changelogs/client_server/newsfragments/1284.feature @@ -0,0 +1,7 @@ +End-to-end encryption for group chats: + + - Olm and Megolm messaging algorithms. + - ``m.room.encrypted``, ``m.room.encryption``, ``m.room_key`` events. + - Device verification process. + - ``device_one_time_keys_count`` sync parameter. + - ``device_lists:left`` sync parameter. diff --git a/changelogs/client_server/newsfragments/1329.clarification b/changelogs/client_server/newsfragments/1329.clarification new file mode 100644 index 00000000..970d3d94 --- /dev/null +++ b/changelogs/client_server/newsfragments/1329.clarification @@ -0,0 +1 @@ +Describe ``StateEvent`` for ``/createRoom`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1353.new b/changelogs/client_server/newsfragments/1353.new new file mode 100644 index 00000000..0af0c520 --- /dev/null +++ b/changelogs/client_server/newsfragments/1353.new @@ -0,0 +1 @@ +``GET /thirdparty/*`` Endpoints diff --git a/changelogs/client_server/newsfragments/1361.feature b/changelogs/client_server/newsfragments/1361.feature new file mode 100644 index 00000000..b1d4c2f1 --- /dev/null +++ b/changelogs/client_server/newsfragments/1361.feature @@ -0,0 +1 @@ +Document the GET version of ``/login`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1362.clarification b/changelogs/client_server/newsfragments/1362.clarification new file mode 100644 index 00000000..1deb4500 --- /dev/null +++ b/changelogs/client_server/newsfragments/1362.clarification @@ -0,0 +1 @@ +Describe how the ``reason`` is handled for kicks/bans \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1364.feature b/changelogs/client_server/newsfragments/1364.feature new file mode 100644 index 00000000..733d6a1f --- /dev/null +++ b/changelogs/client_server/newsfragments/1364.feature @@ -0,0 +1 @@ +Document the ``server_name`` parameter on ``/join/{roomIdOrAlias}`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1365.feature b/changelogs/client_server/newsfragments/1365.feature new file mode 100644 index 00000000..d2864e96 --- /dev/null +++ b/changelogs/client_server/newsfragments/1365.feature @@ -0,0 +1 @@ +Document the CORS/preflight headers \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1371.clarification b/changelogs/client_server/newsfragments/1371.clarification new file mode 100644 index 00000000..88552fcd --- /dev/null +++ b/changelogs/client_server/newsfragments/1371.clarification @@ -0,0 +1 @@ +Mark ``GET /presence/{userId}/status`` as requiring authentication \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1373.clarification b/changelogs/client_server/newsfragments/1373.clarification new file mode 100644 index 00000000..21e18966 --- /dev/null +++ b/changelogs/client_server/newsfragments/1373.clarification @@ -0,0 +1 @@ +Describe the rate limit error response schema diff --git a/changelogs/client_server/newsfragments/1378.clarification b/changelogs/client_server/newsfragments/1378.clarification new file mode 100644 index 00000000..f952428b --- /dev/null +++ b/changelogs/client_server/newsfragments/1378.clarification @@ -0,0 +1 @@ +Clarify that clients must leave rooms before forgetting them \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1379.clarification b/changelogs/client_server/newsfragments/1379.clarification new file mode 100644 index 00000000..122b3900 --- /dev/null +++ b/changelogs/client_server/newsfragments/1379.clarification @@ -0,0 +1 @@ +Document guest access in ``/createRoom`` presets diff --git a/changelogs/client_server/newsfragments/1380.clarification b/changelogs/client_server/newsfragments/1380.clarification new file mode 100644 index 00000000..490a9a43 --- /dev/null +++ b/changelogs/client_server/newsfragments/1380.clarification @@ -0,0 +1 @@ +Define what a ``RoomEvent`` is on ``/rooms/{roomId}/messages`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1381.clarification b/changelogs/client_server/newsfragments/1381.clarification new file mode 100644 index 00000000..e5e599ac --- /dev/null +++ b/changelogs/client_server/newsfragments/1381.clarification @@ -0,0 +1 @@ +Clarify the request and result types on ``/search`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1390.feature b/changelogs/client_server/newsfragments/1390.feature new file mode 100644 index 00000000..48a53b0a --- /dev/null +++ b/changelogs/client_server/newsfragments/1390.feature @@ -0,0 +1 @@ +Add new user identifier object for logging in \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1397.feature b/changelogs/client_server/newsfragments/1397.feature new file mode 100644 index 00000000..e4bd248a --- /dev/null +++ b/changelogs/client_server/newsfragments/1397.feature @@ -0,0 +1 @@ +Document message formats on ``m.text`` and ``m.emote`` messages diff --git a/changelogs/client_server/newsfragments/1400.clarification b/changelogs/client_server/newsfragments/1400.clarification new file mode 100644 index 00000000..3fd29e92 --- /dev/null +++ b/changelogs/client_server/newsfragments/1400.clarification @@ -0,0 +1 @@ +Clarify some of the properties on the search result diff --git a/changelogs/client_server/newsfragments/1506.feature b/changelogs/client_server/newsfragments/1506.feature new file mode 100644 index 00000000..62ad1260 --- /dev/null +++ b/changelogs/client_server/newsfragments/1506.feature @@ -0,0 +1 @@ +Document and improve client interaction with pushers. diff --git a/changelogs/client_server/newsfragments/1507.new b/changelogs/client_server/newsfragments/1507.new new file mode 100644 index 00000000..6cc46870 --- /dev/null +++ b/changelogs/client_server/newsfragments/1507.new @@ -0,0 +1 @@ +``POST /account/3pid/msisdn/requestToken``, ``POST /register/msisdn/requestToken``, and ``POST /account/password/msisdn/requestToken`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1517.clarification b/changelogs/client_server/newsfragments/1517.clarification new file mode 100644 index 00000000..b16928c1 --- /dev/null +++ b/changelogs/client_server/newsfragments/1517.clarification @@ -0,0 +1 @@ +Clarify how access tokens are meant to be supplied to the homeserver. \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1542.feature b/changelogs/client_server/newsfragments/1542.feature new file mode 100644 index 00000000..7e94bdc5 --- /dev/null +++ b/changelogs/client_server/newsfragments/1542.feature @@ -0,0 +1 @@ +Guests can now call /context and /event to fetch events diff --git a/changelogs/client_server/pyproject.toml b/changelogs/client_server/pyproject.toml new file mode 100644 index 00000000..8fa3f6b5 --- /dev/null +++ b/changelogs/client_server/pyproject.toml @@ -0,0 +1,30 @@ +[tool.towncrier] + filename = "../client_server.rst" + directory = "newsfragments" + issue_format = "`#{issue} `_" + title_format = "{version}" + + [[tool.towncrier.type]] + directory = "breaking" + name = "Breaking Changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "deprecation" + name = "Deprecations" + showcontent = true + + [[tool.towncrier.type]] + directory = "new" + name = "New Endpoints" + showcontent = true + + [[tool.towncrier.type]] + directory = "feature" + name = "Backwards Compatible Changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "clarification" + name = "Spec Clarifications" + showcontent = true diff --git a/event-schemas/examples/m.room.encrypted#megolm b/event-schemas/examples/m.room.encrypted#megolm new file mode 100644 index 00000000..1f9b7520 --- /dev/null +++ b/event-schemas/examples/m.room.encrypted#megolm @@ -0,0 +1,14 @@ +{ + "content": { + "algorithm": "m.megolm.v1.aes-sha2", + "ciphertext": "AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...", + "device_id": "RJYKSTBOIE", + "sender_key": "IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA", + "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ" + }, + "event_id": "$WLGTSEFSEF:localhost", + "room_id": "!Cuyf34gef24t:localhost", + "origin_server_ts": 1476648761524, + "sender": "@example:localhost", + "type": "m.room.encrypted" +} diff --git a/event-schemas/examples/m.room.encrypted#olm b/event-schemas/examples/m.room.encrypted#olm new file mode 100644 index 00000000..abb23c31 --- /dev/null +++ b/event-schemas/examples/m.room.encrypted#olm @@ -0,0 +1,14 @@ +{ + "type": "m.room.encrypted", + "sender": "@example:localhost", + "content": { + "algorithm": "m.olm.v1.curve25519-aes-sha2", + "sender_key": "Szl29ksW/L8yZGWAX+8dY1XyFi+i5wm+DRhTGkbMiwU", + "ciphertext": { + "7qZcfnBmbEGzxxaWfBjElJuvn7BZx+lSz/SvFrDF/z8": { + "type": 0, + "body": "AwogGJJzMhf/S3GQFXAOrCZ3iKyGU5ZScVtjI0KypTYrW..." + } + } + } +} diff --git a/event-schemas/examples/m.room.encryption b/event-schemas/examples/m.room.encryption new file mode 100644 index 00000000..08f15239 --- /dev/null +++ b/event-schemas/examples/m.room.encryption @@ -0,0 +1,13 @@ +{ + "content": { + "algorithm": "m.megolm.v1.aes-sha2", + "rotation_period_ms": 604800000, + "rotation_period_msgs": 100 + }, + "event_id": "$WLGTSEFJJKJ:localhost", + "origin_server_ts": 1476648761524, + "sender": "@example:localhost", + "room_id": "!Cuyf34gef24t:localhost", + "state_key": "", + "type": "m.room.encryption" +} diff --git a/event-schemas/examples/m.room.message#m.emote b/event-schemas/examples/m.room.message#m.emote index 4280928e..79292ddf 100644 --- a/event-schemas/examples/m.room.message#m.emote +++ b/event-schemas/examples/m.room.message#m.emote @@ -2,7 +2,9 @@ "age": 242352, "content": { "body": "thinks this is an example emote", - "msgtype": "m.emote" + "msgtype": "m.emote", + "format": "org.matrix.custom.html", + "formatted_body": "thinks this is an example emote" }, "origin_server_ts": 1431961217939, "event_id": "$WLGTSEFSEF:localhost", diff --git a/event-schemas/examples/m.room.message#m.text b/event-schemas/examples/m.room.message#m.text index e00c7aa5..48a97db8 100644 --- a/event-schemas/examples/m.room.message#m.text +++ b/event-schemas/examples/m.room.message#m.text @@ -2,7 +2,9 @@ "age": 242352, "content": { "body": "This is an example text message", - "msgtype": "m.text" + "msgtype": "m.text", + "format": "org.matrix.custom.html", + "formatted_body": "This is an example text message" }, "origin_server_ts": 1431961217939, "event_id": "$WLGTSEFSEF:localhost", diff --git a/event-schemas/examples/m.room_key b/event-schemas/examples/m.room_key new file mode 100644 index 00000000..53f83e52 --- /dev/null +++ b/event-schemas/examples/m.room_key @@ -0,0 +1,9 @@ +{ + "content": { + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!Cuyf34gef24t:localhost", + "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", + "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..." + }, + "type": "m.room_key" +} diff --git a/event-schemas/schema/m.room.encrypted b/event-schemas/schema/m.room.encrypted new file mode 100644 index 00000000..6825be1d --- /dev/null +++ b/event-schemas/schema/m.room.encrypted @@ -0,0 +1,61 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + This event type is used when sending encrypted events. It can be used either + within a room (in which case it will have all of the `Room Event fields`_), or + as a `to-device`_ event. + +properties: + content: + properties: + algorithm: + type: string + enum: + - m.olm.curve25519-aes-sha256 + - m.megolm.v1.aes-sha + description: |- + The encryption algorithm used to encrypt this event. The + value of this field determines which other properties will be + present. + ciphertext: + oneOf: + - type: string + - type: object + additionalProperties: + type: object + title: CiphertextInfo + properties: + body: + type: string + description: The encrypted payload. + type: + type: integer + description: The Olm message type. + description: |- + The encrypted content of the event. Either the encrypted payload + itself, in the case of a Megolm event, or a map from the recipient + Curve25519 identity key to ciphertext information, in the case of an + Olm event. For more details, see `Messaging Algorithms`_. + sender_key: + type: string + description: The Curve25519 key of the sender. + device_id: + type: string + description: The ID of the sending device. Required with Megolm. + session_id: + type: string + description: |- + The ID of the session used to encrypt the message. Required with + Megolm. + required: + - algorithm + - sender_key + - ciphertext + type: object + type: + enum: + - m.room.encrypted + type: string +type: object diff --git a/event-schemas/schema/m.room.encryption b/event-schemas/schema/m.room.encryption new file mode 100644 index 00000000..d7c4d429 --- /dev/null +++ b/event-schemas/schema/m.room.encryption @@ -0,0 +1,36 @@ +--- +allOf: + - $ref: core-event-schema/state_event.yaml +description: Defines how messages sent in this room should be encrypted. +properties: + content: + properties: + algorithm: + type: string + enum: + - "m.megolm.v1.aes-sha2" + description: |- + The encryption algorithm to be used to encrypt messages sent in this + room. + rotation_period_ms: + type: integer + description: |- + How long the session should be used before changing it. ``604800000`` + (a week) is the recommended default. + rotation_period_msgs: + type: integer + description: |- + How many messages should be sent before changing the session. ``100`` is the + recommended default. + required: + - algorithm + type: object + state_key: + description: A zero-length string. + pattern: '^$' + type: string + type: + enum: + - m.room.encryption + type: string +type: object diff --git a/event-schemas/schema/m.room.message#m.emote b/event-schemas/schema/m.room.message#m.emote index 88860cb2..f67a184b 100644 --- a/event-schemas/schema/m.room.message#m.emote +++ b/event-schemas/schema/m.room.message#m.emote @@ -12,6 +12,16 @@ properties: enum: - m.emote 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.text b/event-schemas/schema/m.room.message#m.text index 2720172d..b481bcea 100644 --- a/event-schemas/schema/m.room.message#m.text +++ b/event-schemas/schema/m.room.message#m.text @@ -12,6 +12,16 @@ properties: enum: - m.text 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_key b/event-schemas/schema/m.room_key new file mode 100644 index 00000000..ef8c51c0 --- /dev/null +++ b/event-schemas/schema/m.room_key @@ -0,0 +1,35 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + This event type is used to exchange keys for end-to-end encryption. Typically + it is encrypted as an ``m.room.encrypted`` event, then sent as a `to-device`_ event. +properties: + content: + properties: + algorithm: + type: string + enum: ["m.megolm.v1.aes-sha2"] + description: |- + The encryption algorithm the key in this event is to be used with. + room_id: + type: string + description: The room where the key is used. + session_id: + type: string + description: The ID of the session that the key is for. + session_key: + type: string + description: The key to be exchanged. + required: + - algorithm + - room_id + - session_id + - session_key + type: object + type: + enum: + - m.room_key + type: string +type: object diff --git a/meta/documentation_style.rst b/meta/documentation_style.rst index 698eb027..c6bb62bf 100644 --- a/meta/documentation_style.rst +++ b/meta/documentation_style.rst @@ -32,6 +32,12 @@ complete specification to be merged correctly. These characters are: If you find yourself using ``^`` or beyond, you should rethink your document layout if possible. +Correct capitalisation for long section names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Headings should start with a capital letter, and use lower-case otherwise. + + TODOs ----- @@ -63,7 +69,11 @@ have English as their first language. Prefer British English (colour, -ise) to American English. The word "homeserver" is spelt thus (rather than "home server", "Homeserver", -or (argh) "Home Server"). +or (argh) "Home Server"). However, an identity server is two words. + +.. Rationale: "homeserver" distinguishes from a "home server" which is a server + you have at home. "Identity server" is clear, whereas "identityserver" is + horrible. Lists should: @@ -96,3 +106,7 @@ When writing OpenAPI specifications for the API endpoints, follow these rules: The description is also the place to define default values for optional properties. Use the wording "Defaults to X [if unspecified]." + + Some descriptions start with the word "Optional" to explicitly mark optional + properties and parameters. This is redundant. Instead, use the ``required`` + property to mark those that are required. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..b53982b8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[ tool.giles ] + + [ tool.giles.circleci_artifacts.docs ] + url = "gen/index.html" + message = "Click details to preview the HTML documentation." + + [ tool.giles.circleci_artifacts.swagger ] + url = "client-server/index.html" + message = "Click to preview the swagger build." diff --git a/scripts/continuserv/main.go b/scripts/continuserv/main.go index 330ddca7..2ef6fed9 100644 --- a/scripts/continuserv/main.go +++ b/scripts/continuserv/main.go @@ -98,6 +98,9 @@ func makeWalker(base string, w *fsnotify.Watcher) filepath.WalkFunc { log.Fatalf("Failed to get relative path of %s: %v", path, err) } + // Normalize slashes + rel = filepath.ToSlash(rel) + // skip a few things that we know don't form part of the spec if rel == "api/node_modules" || rel == "scripts/gen" || @@ -120,8 +123,19 @@ func filter(e fsnotify.Event) bool { return false } - // Avoid some temp files that vim writes - if strings.HasSuffix(e.Name, "~") || strings.HasSuffix(e.Name, ".swp") || strings.HasPrefix(e.Name, ".") { + _, fname := filepath.Split(e.Name) + + // Avoid some temp files that vim or emacs writes + if strings.HasSuffix(e.Name, "~") || strings.HasSuffix(e.Name, ".swp") || strings.HasPrefix(fname, ".") || + (strings.HasPrefix(fname, "#") && strings.HasSuffix(fname, "#")) { + return false + } + + // Forcefully ignore directories we don't care about (Windows, at least, tries to notify about some directories) + filePath := filepath.ToSlash(e.Name) // normalize slashes + if strings.Contains(filePath, "/scripts/tmp") || + strings.Contains(filePath, "/scripts/gen") || + strings.Contains(filePath, "/api/node_modules") { return false } @@ -147,7 +161,7 @@ func serve(w http.ResponseWriter, req *http.Request) { if file[0] == '/' { file = file[1:] } - b, ok = m.bytes[file] + b, ok = m.bytes[filepath.FromSlash(file)] // de-normalize slashes if ok && file == "api-docs.json" { w.Header().Set("Access-Control-Allow-Origin", "*") diff --git a/scripts/css/blockquote.css b/scripts/css/blockquote.css index 151d3bce..05fa73bc 100644 --- a/scripts/css/blockquote.css +++ b/scripts/css/blockquote.css @@ -1,5 +1,4 @@ blockquote { margin: 20px 0 30px; - border-left: 5px solid; padding-left: 20px; } diff --git a/scripts/dump-swagger.py b/scripts/dump-swagger.py index 110c4446..e02c554e 100755 --- a/scripts/dump-swagger.py +++ b/scripts/dump-swagger.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # dump-swagger reads all of the swagger API docs used in spec generation and # outputs a JSON file which merges them all, for use as input to a swagger UI @@ -103,7 +103,7 @@ for filename in os.listdir(cs_api_dir): output["paths"][path] = {} output["paths"][path][method] = spec -print "Generating %s" % output_file +print("Generating %s" % output_file) try: os.makedirs(os.path.dirname(output_file)) diff --git a/scripts/gendoc.py b/scripts/gendoc.py index 2b35f801..16c40af5 100755 --- a/scripts/gendoc.py +++ b/scripts/gendoc.py @@ -31,6 +31,7 @@ script_dir = os.path.dirname(os.path.abspath(__file__)) docs_dir = os.path.dirname(script_dir) spec_dir = os.path.join(docs_dir, "specification") tmp_dir = os.path.join(script_dir, "tmp") +changelog_dir = os.path.join(docs_dir, "changelogs") VERBOSE = False @@ -151,7 +152,7 @@ def is_title_line(prev_line, line, title_styles): def get_rst(file_info, title_level, title_styles, spec_dir, adjust_titles): # string are file paths to RST blobs - if isinstance(file_info, basestring): + if isinstance(file_info, str): log("%s %s" % (">" * (1 + title_level), file_info)) with open(os.path.join(spec_dir, file_info), "r") as f: rst = None @@ -194,7 +195,7 @@ def build_spec(target, out_filename): spec_dir=spec_dir, adjust_titles=True ) - outfile.write(section) + outfile.write(section.encode('UTF-8')) """ @@ -279,15 +280,16 @@ def rst2html(i, o, stylesheets): def addAnchors(path): log("add anchors %s" % path) - with open(path, "r") as f: + with open(path, "rb") as f: lines = f.readlines() - replacement = replacement = r'

\n\1' - with open(path, "w") as f: + replacement = r'

\n\1' + with open(path, "wb") as f: for line in lines: + line = line.decode("UTF-8") line = re.sub(r'()', replacement, line.rstrip()) - line = re.sub(r'(
)', replacement, line.rstrip()) - f.write(line + "\n") + line = re.sub(r'(
)', replacement, line.rstrip()) + f.write((line + "\n").encode('UTF-8')) def run_through_template(input_files, set_verbose, substitutions): @@ -364,7 +366,7 @@ def get_build_target(all_targets, target_name): resolved_files = [] for file_entry in target["files"]: # file_entry is a group id - if isinstance(file_entry, basestring) and file_entry.startswith("group:"): + if isinstance(file_entry, str) and file_entry.startswith("group:"): group = get_group(file_entry, 0) # The group may be resolved to a list of file entries, in which case # we want to extend the array to insert each of them rather than @@ -376,8 +378,8 @@ def get_build_target(all_targets, target_name): # file_entry is a dict which has more file entries as values elif isinstance(file_entry, dict): resolved_entry = {} - for (depth, entry) in file_entry.iteritems(): - if not isinstance(entry, basestring): + for (depth, entry) in file_entry.items(): + if not isinstance(entry, str): raise Exception( "Double-nested depths are not supported. Entry: %s" % (file_entry,) ) @@ -395,11 +397,11 @@ def get_build_target(all_targets, target_name): return build_target def log(line): - print "gendoc: %s" % line + print("gendoc: %s" % line) def logv(line): if VERBOSE: - print "gendoc:V: %s" % line + print("gendoc:V: %s" % line) def cleanup_env(): @@ -445,7 +447,7 @@ def main(targets, dest_dir, keep_intermediates, substitutions): stylesheets = glob.glob(os.path.join(script_dir, "css", "*.css")) - for target_name, templated_file in templated_files.iteritems(): + for target_name, templated_file in templated_files.items(): target = target_defs["targets"].get(target_name) version_label = None if target: @@ -480,7 +482,7 @@ def list_targets(): with open(os.path.join(spec_dir, "targets.yaml"), "r") as targ_file: target_defs = yaml.load(targ_file.read()) targets = target_defs["targets"].keys() - print "\n".join(targets) + print("\n".join(targets)) def extract_major(s): diff --git a/scripts/generate-matrix-org-assets b/scripts/generate-matrix-org-assets index cb3cf455..ed08f81d 100755 --- a/scripts/generate-matrix-org-assets +++ b/scripts/generate-matrix-org-assets @@ -8,6 +8,9 @@ cd `dirname $0`/.. mkdir -p assets +# generate specification/proposals.rst +./scripts/proposals.py + # generate the spec docs ./scripts/gendoc.py -d assets/spec diff --git a/scripts/proposals.py b/scripts/proposals.py index aa2252bc..5c670aff 100755 --- a/scripts/proposals.py +++ b/scripts/proposals.py @@ -6,29 +6,33 @@ import requests import re from datetime import datetime -from m2r import convert as m2r pagecount = 1 authors = set() prs = set() def getpage(url, page): - resp = requests.get(url + str(page)) + url = url + str(page) + resp = requests.get(url) for link in resp.links.values(): if link['rel'] == 'last': pagecount = re.search('page=(.+?)', link['url']).group(1) - return resp.json() + val = resp.json() + if not isinstance(val, list): + print(val) # Just dump the raw (likely error) response to the log + raise Exception("Error calling %s" % url) + return val def getbylabel(label): pagecount = 1 json = list() - urlbase = 'https://api.github.com/repos/matrix-org/matrix-doc/issues?state=all&labels=' + label + '&page=' + urlbase = 'https://api.github.com/repos/matrix-org/matrix-doc/issues?state=all&labels=proposal,' + label + '&page=' print(urlbase) json.extend(getpage(urlbase, 1)) for page in range(2, int(pagecount) + 1): - getpage(urlbase, page) + json.extend(getpage(urlbase, page)) return json @@ -42,7 +46,7 @@ issues = {} for label in labels: issues[label] = getbylabel(label) -text_file = open("../specification/proposals.rst", "w") +text_file = open("specification/proposals.rst", "w") text_file.write("Tables of Tracked Proposals\n---------------------------\n\n") @@ -64,7 +68,6 @@ for label in labels: for item in issues[label]: # set the created date, find local field, otherwise Github - print(item) body = str(item['body']) created = re.search('^Date: (.+?)\n', body, flags=re.MULTILINE) if created is not None: @@ -134,16 +137,27 @@ for label in labels: text_file.write(" - " + str(shepherd) + "\n") # PRs - pr_list = re.search('PRs: (.+?)$', str(item['body'])) - if pr_list is not None: - pr_list_formatted = set() - pr_list = pr_list.group(1) - for p in pr_list.split(","): - prs.add(p.strip()) - pr_list_formatted.add("`PR" + str(p.strip()) + "`_") - text_file.write(" - " + ', '.join(pr_list_formatted)) - text_file.write("\n") - else: + try: + pr_list = re.search('PRs: (.+?)$', str(item['body'])) + if pr_list is not None: + pr_list_formatted = set() + pr_list = pr_list.group(1) + for p in pr_list.split(","): + if re.match(r"#\d", p.strip()): + prs.add(p.strip()) + pr_list_formatted.add("`PR" + str(p.strip()) + "`_") + elif re.match(r"https://github.com/matrix-org/matrix-doc/pulls/\d", p.strip()): + pr = "#" + p.strip().replace('https://github.com/matrix-org/matrix-doc/pulls/', '') + prs.add(pr) + pr_list_formatted.add("`PR" + str(pr) + "`_") + else: + raise RuntimeWarning + text_file.write(" - " + ', '.join(pr_list_formatted)) + text_file.write("\n") + else: + text_file.write(" - \n") + except: + print("exception parsing PRs for MSC" + str(item['number'])) text_file.write(" - \n") text_file.write("\n\n\n") diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 9886fce4..2a7d7ff8 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -6,3 +6,6 @@ pygments >= 2.2.0 Jinja2 >= 2.9.6 jsonschema >= 2.6.0 PyYAML >= 3.12 +requests >= 2.18.4 +towncrier == 18.6.0 +six >= 1.11.0 \ No newline at end of file diff --git a/scripts/speculator/main.go b/scripts/speculator/main.go index 0380a3cd..12ec2aec 100644 --- a/scripts/speculator/main.go +++ b/scripts/speculator/main.go @@ -701,8 +701,8 @@ func ignoreExitCodeOne(err error) error { func main() { flag.Parse() - // It would be great to read this from github, but there's no convenient way to do so. - // Most of these memberships are "private", so would require some kind of auth. + // It would be great to read this from github + // cf https://github.com/matrix-org/matrix-doc/issues/1384 allowedMembers = map[string]bool{ "dbkr": true, "erikjohnston": true, @@ -713,6 +713,13 @@ func main() { "ara4n": true, "leonerd": true, "rxl881": true, + "uhoreg": true, + "turt2live": true, + "Half-Shot": true, + "anoadragon453": true, + "mujx": true, + "benparsons": true, + "KitsuneRal": true, } if err := initCache(); err != nil { log.Fatal(err) diff --git a/scripts/swagger-http-server.py b/scripts/swagger-http-server.py index 5ec00101..06d764aa 100755 --- a/scripts/swagger-http-server.py +++ b/scripts/swagger-http-server.py @@ -19,14 +19,14 @@ import argparse import os -import SimpleHTTPServer -import SocketServer +import http.server +import socketserver # Thanks to http://stackoverflow.com/a/13354482 -class MyHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): +class MyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): def end_headers(self): self.send_my_headers() - SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) + http.server.SimpleHTTPRequestHandler.end_headers(self) def send_my_headers(self): self.send_header("Access-Control-Allow-Origin", "*") @@ -49,7 +49,7 @@ if __name__ == '__main__': os.chdir(args.swagger_dir) - httpd = SocketServer.TCPServer(("localhost", args.port), + httpd = socketserver.TCPServer(("localhost", args.port), MyHTTPRequestHandler) - print "Serving at http://localhost:%i/api-docs.json" % args.port + print("Serving at http://localhost:%i/api-docs.json" % args.port) httpd.serve_forever() diff --git a/scripts/templating/batesian/__init__.py b/scripts/templating/batesian/__init__.py index da41b31b..e901590f 100644 --- a/scripts/templating/batesian/__init__.py +++ b/scripts/templating/batesian/__init__.py @@ -11,7 +11,6 @@ # 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. -from sets import Set class AccessKeyStore(object): @@ -22,7 +21,7 @@ class AccessKeyStore(object): if not existing_data: existing_data = {} self.data = existing_data - self.accessed_set = Set() + self.accessed_set = set() def keys(self): return self.data.keys() @@ -35,5 +34,5 @@ class AccessKeyStore(object): return self.data[key] def get_unaccessed_set(self): - data_list = Set(self.data.keys()) + data_list = set(self.data.keys()) return data_list - self.accessed_set \ No newline at end of file diff --git a/scripts/templating/batesian/sections.py b/scripts/templating/batesian/sections.py index c541d771..18a622f6 100644 --- a/scripts/templating/batesian/sections.py +++ b/scripts/templating/batesian/sections.py @@ -29,7 +29,7 @@ class Sections(object): def log(self, text): if self.debug: - print "batesian:sections: %s" % text + print("batesian:sections: %s" % text) def get_sections(self): render_list = inspect.getmembers(self, predicate=inspect.ismethod) @@ -40,7 +40,7 @@ class Sections(object): section_key = func_name[len("render_"):] self.log("Generating section '%s'" % section_key) section = func() - if isinstance(section, basestring): + if isinstance(section, str): if section_key in section_dict: raise Exception( ("%s : Section %s already exists. It must have been " + @@ -54,8 +54,8 @@ class Sections(object): ) elif isinstance(section, dict): self.log(" Generated multiple sections:") - for (k, v) in section.iteritems(): - if not isinstance(k, basestring) or not isinstance(v, basestring): + for (k, v) in section.items(): + if not isinstance(k, str) or not isinstance(v, str): raise Exception( ("Method %s returned multiple sections as a dict but " + "expected the dict elements to be strings but they aren't.") % diff --git a/scripts/templating/batesian/units.py b/scripts/templating/batesian/units.py index 8f748f6d..82cc52f9 100644 --- a/scripts/templating/batesian/units.py +++ b/scripts/templating/batesian/units.py @@ -41,7 +41,7 @@ class Units(object): trace = inspect.stack() if len(trace) > 1 and len(trace[1]) > 2: func_name = trace[1][3] + ":" - print "batesian:units:%s %s" % (func_name, text) + print("batesian:units:%s %s" % (func_name, text)) def get_units(self, debug=False): unit_list = inspect.getmembers(self, predicate=inspect.ismethod) @@ -50,7 +50,7 @@ class Units(object): if not func_name.startswith("load_"): continue unit_key = func_name[len("load_"):] - if len(inspect.getargs(func.func_code).args) > 1: + if len(inspect.getargs(func.__code__).args) > 1: unit_dict[unit_key] = func(self.substitutions) else: unit_dict[unit_key] = func() diff --git a/scripts/templating/build.py b/scripts/templating/build.py index d18569b6..fae4db56 100755 --- a/scripts/templating/build.py +++ b/scripts/templating/build.py @@ -63,6 +63,7 @@ import sys from textwrap import TextWrapper from matrix_templates.units import TypeTableRow +from functools import reduce def create_from_template(template, sections): @@ -138,7 +139,7 @@ def main(input_module, files=None, out_dir=None, verbose=False, substitutions={} return reduce(max, rowwidths, default if default is not None else default_width) - results = map(colwidth, keys, defaults) + results = list(map(colwidth, keys, defaults)) return results # make Jinja aware of the templates and filters @@ -167,7 +168,7 @@ def main(input_module, files=None, out_dir=None, verbose=False, substitutions={} # print out valid section keys if no file supplied if not files: - print "\nValid template variables:" + print("\nValid template variables:") for key in sections.keys(): sec_text = "" if (len(sections[key]) > 75) else ( "(Value: '%s')" % sections[key] @@ -175,8 +176,8 @@ def main(input_module, files=None, out_dir=None, verbose=False, substitutions={} sec_info = "%s characters" % len(sections[key]) if sections[key].count("\n") > 0: sec_info += ", %s lines" % sections[key].count("\n") - print " %s" % key - print " %s %s" % (sec_info, sec_text) + print(" %s" % key) + print(" %s %s" % (sec_info, sec_text)) return # check the input files and substitute in sections where required @@ -190,8 +191,8 @@ def main(input_module, files=None, out_dir=None, verbose=False, substitutions={} def process_file(env, sections, filename, output_filename): log("Parsing input template: %s" % filename) - with open(filename, "r") as file_stream: - temp_str = file_stream.read().decode("utf-8") + with open(filename, "rb") as file_stream: + temp_str = file_stream.read().decode('UTF-8') # do sanity checking on the template to make sure they aren't reffing things # which will never be replaced with a section. @@ -213,13 +214,13 @@ def process_file(env, sections, filename, output_filename): for old, new in substitutions.items(): output = output.replace(old, new) - with open(output_filename, "w") as f: - f.write(output.encode("utf-8")) + with open(output_filename, "wb") as f: + f.write(output.encode('UTF-8')) log("Output file for: %s" % output_filename) def log(line): - print "batesian: %s" % line + print("batesian: %s" % line) if __name__ == '__main__': parser = ArgumentParser( diff --git a/scripts/templating/matrix_templates/__init__.py b/scripts/templating/matrix_templates/__init__.py index 6b46192c..b81c5a30 100644 --- a/scripts/templating/matrix_templates/__init__.py +++ b/scripts/templating/matrix_templates/__init__.py @@ -11,8 +11,8 @@ # 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. -from sections import MatrixSections -from units import MatrixUnits +from .sections import MatrixSections +from .units import MatrixUnits import os exports = { diff --git a/scripts/templating/matrix_templates/sections.py b/scripts/templating/matrix_templates/sections.py index 64e32aa4..1a93c723 100644 --- a/scripts/templating/matrix_templates/sections.py +++ b/scripts/templating/matrix_templates/sections.py @@ -189,3 +189,17 @@ class MatrixSections(Sections): template = self.env.get_template("apis.tmpl") apis = self.units.get("apis") return template.render(apis=apis) + + def render_swagger_definition(self): + rendered = {} + template = self.env.get_template("schema-definition.tmpl") + subtitle_title_char = self.units.get("spec_targets")[ + "relative_title_styles" + ]["subtitle"] + definitions = self.units.get("swagger_definitions") + for group, swagger_def in definitions.items(): + rendered["definition_" + group] = template.render( + definition=swagger_def['definition'], + examples=swagger_def['examples'], + title_kind=subtitle_title_char) + return rendered \ No newline at end of file diff --git a/scripts/templating/matrix_templates/templates/schema-definition.tmpl b/scripts/templating/matrix_templates/templates/schema-definition.tmpl new file mode 100644 index 00000000..42a7ae47 --- /dev/null +++ b/scripts/templating/matrix_templates/templates/schema-definition.tmpl @@ -0,0 +1,21 @@ +{% import 'tables.tmpl' as tables -%} + +``{{definition.title}}`` Schema +{{(11 + definition.title | length) * title_kind}} + +{% if 'description' in definition %} +{{definition.description}} +{% endif %} + +{% for table in definition.tables -%} +{{"``"+table.title+"``" if table.title else "" }} +{{ tables.paramtable(table.rows) }} +{% endfor %} + +Example{% if examples | length > 1 %}s{% endif %}: + +{% for example in examples %} +.. code:: json + + {{example | jsonify(4, 4)}} +{% endfor %} diff --git a/scripts/templating/matrix_templates/units.py b/scripts/templating/matrix_templates/units.py index d8cfe1e3..90a87cd4 100644 --- a/scripts/templating/matrix_templates/units.py +++ b/scripts/templating/matrix_templates/units.py @@ -29,8 +29,9 @@ import os.path import re import subprocess import sys -import urllib import yaml +from functools import reduce +from six.moves.urllib.parse import urlencode, quote matrix_doc_dir=reduce(lambda acc,_: os.path.dirname(acc), range(1, 5), os.path.abspath(__file__)) @@ -42,6 +43,13 @@ HTTP_APIS = { os.path.join(matrix_doc_dir, "api/push-gateway"): "push", os.path.join(matrix_doc_dir, "api/server-server"): "ss", } +SWAGGER_DEFINITIONS = { + os.path.join(matrix_doc_dir, "api/application-service/definitions"): "as", + os.path.join(matrix_doc_dir, "api/client-server/definitions"): "cs", + os.path.join(matrix_doc_dir, "api/identity/definitions"): "is", + os.path.join(matrix_doc_dir, "api/push-gateway/definitions"): "push", + os.path.join(matrix_doc_dir, "api/server-server/definitions"): "ss", +} EVENT_EXAMPLES = os.path.join(matrix_doc_dir, "event-schemas/examples") EVENT_SCHEMA = os.path.join(matrix_doc_dir, "event-schemas/schema") CORE_EVENT_SCHEMA = os.path.join(matrix_doc_dir, "event-schemas/schema/core-event-schema") @@ -138,6 +146,7 @@ def inherit_parents(obj): Recurse through the 'allOf' declarations in the object """ logger.debug("inherit_parents %r" % obj) + parents = obj.get("allOf", []) if not parents: return obj @@ -147,7 +156,7 @@ def inherit_parents(obj): # settings defined in the child take priority over the parents, so we # iterate through the parents first, and then overwrite with the settings # from the child. - for p in map(inherit_parents, parents) + [obj]: + for p in list(map(inherit_parents, parents)) + [obj]: # child blats out type, title and description for key in ('type', 'title', 'description'): if p.get(key): @@ -250,12 +259,12 @@ def get_json_schema_object_fields(obj, enforce_title=False): tables.extend(res["tables"]) logger.debug("Done property %s" % key_name) - except Exception, e: + except Exception as e: e2 = Exception("Error reading property %s.%s: %s" % (obj_title, key_name, str(e))) # throw the new exception with the old stack trace, so that # we don't lose information about where the error occurred. - raise e2, None, sys.exc_info()[2] + raise e2.with_traceback(sys.exc_info()[2]) tables.insert(0, TypeTable(title=obj_title, rows=first_table_rows)) @@ -277,7 +286,9 @@ def get_json_schema_object_fields(obj, enforce_title=False): def process_data_type(prop, required=False, enforce_title=True): prop = inherit_parents(prop) - prop_type = prop['type'] + prop_type = prop.get('oneOf', prop.get('type', [])) + assert prop_type + tables = [] enum_desc = None is_object = False @@ -292,11 +303,35 @@ def process_data_type(prop, required=False, enforce_title=True): is_object = True elif prop_type == "array": - nested = process_data_type(prop["items"]) - prop_title = "[%s]" % nested["title"] - tables = nested["tables"] - enum_desc = nested["enum_desc"] + items = prop["items"] + # Items can be a list of schemas or a schema itself + # http://json-schema.org/latest/json-schema-validation.html#rfc.section.6.4 + if isinstance(items, list): + nested_titles = [] + for i in items: + nested = process_data_type(i) + tables.extend(nested['tables']) + nested_titles.append(nested['title']) + prop_title = "[%s]" % (", ".join(nested_titles), ) + else: + nested = process_data_type(prop["items"]) + prop_title = "[%s]" % nested["title"] + tables = nested["tables"] + enum_desc = nested["enum_desc"] + elif isinstance(prop_type, list): + prop_title = [] + for t in prop_type: + if isinstance(t, dict): + nested = process_data_type(t) + tables.extend(nested['tables']) + prop_title.append(nested['title']) + # Assuming there's at most one enum among type options + enum_desc = nested['enum_desc'] + if enum_desc: + enum_desc = "%s if the type is enum" % enum_desc + else: + prop_title.append(t) else: prop_title = prop_type @@ -370,6 +405,7 @@ def get_tables_for_response(schema): def get_example_for_schema(schema): """Returns a python object representing a suitable example for this object""" schema = inherit_parents(schema) + if 'example' in schema: example = schema['example'] return example @@ -380,7 +416,7 @@ def get_example_for_schema(schema): if 'properties' not in schema: raise Exception('"object" property has neither properties nor example') res = OrderedDict() - for prop_name, prop in schema['properties'].iteritems(): + for prop_name, prop in schema['properties'].items(): logger.debug("Parsing property %r" % prop_name) prop_example = get_example_for_schema(prop) res[prop_name] = prop_example @@ -389,7 +425,10 @@ def get_example_for_schema(schema): if proptype == 'array': if 'items' not in schema: raise Exception('"array" property has neither items nor example') - return [get_example_for_schema(schema['items'])] + items = schema['items'] + if isinstance(items, list): + return [get_example_for_schema(i) for i in items] + return [get_example_for_schema(items)] if proptype == 'integer': return 0 @@ -502,6 +541,15 @@ class MatrixUnits(Units): # assign value expected for this param val_type = param.get("type") # integer/string + if val_type == "array": + items = param.get("items") + if items: + if isinstance(items, list): + types = ", ".join(i.get("type") for i in items) + val_type = "[%s]" % (types,) + else: + val_type = "[%s]" % items.get("type") + if param.get("enum"): val_type = "enum" desc += ( @@ -518,7 +566,7 @@ class MatrixUnits(Units): if param_loc == "path": path_template = path_template.replace( - "{%s}" % param_name, urllib.quote(example) + "{%s}" % param_name, quote(example) ) elif param_loc == "query": if type(example) == list: @@ -527,7 +575,7 @@ class MatrixUnits(Units): else: example_query_params.append((param_name, example)) - except Exception, e: + except Exception as e: raise Exception("Error handling parameter %s" % param_name, e) # endfor[param] good_response = None @@ -551,14 +599,14 @@ class MatrixUnits(Units): ) if "headers" in good_response: headers = TypeTable() - for (header_name, header) in good_response["headers"].iteritems(): + for (header_name, header) in good_response["headers"].items(): headers.add_row( TypeTableRow(key=header_name, title=header["type"], desc=header["description"]), ) endpoint["res_headers"] = headers query_string = "" if len( - example_query_params) == 0 else "?" + urllib.urlencode( + example_query_params) == 0 else "?" + urlencode( example_query_params) if example_body: endpoint["example"][ @@ -600,12 +648,12 @@ class MatrixUnits(Units): body_tables = req_body_tables[1:] endpoint_data['req_body_tables'].extend(body_tables) - except Exception, e: + except Exception as e: e2 = Exception( "Error decoding body of API endpoint %s %s: %s" % (endpoint_data["method"], endpoint_data["path"], e) ) - raise e2, None, sys.exc_info()[2] + raise e2.with_traceback(sys.exc_info()[2]) def load_swagger_apis(self): @@ -628,6 +676,50 @@ class MatrixUnits(Units): apis[group_name] = api return apis + + def load_swagger_definitions(self): + defs = {} + for path, prefix in SWAGGER_DEFINITIONS.items(): + self._load_swagger_definitions_in_dir(defs, path, prefix) + return defs + + def _load_swagger_definitions_in_dir(self, defs, path, prefix, recurse=True): + if not os.path.exists(path): + return defs + for filename in os.listdir(path): + filepath = os.path.join(path, filename) + if os.path.isdir(filepath) and recurse: + safe_name = re.sub(r"[^a-zA-Z0-9_]", "_", filename) + dir_prefix = "_".join([prefix, safe_name]) + # We don't recurse because we have to stop at some point + self._load_swagger_definitions_in_dir( + defs, filepath, dir_prefix, recurse=False) + if not filename.endswith(".yaml"): + continue + filepath = os.path.join(path, filename) + logger.info("Reading swagger definition: %s" % filepath) + with open(filepath, "r") as f: + # strip .yaml + group_name = re.sub(r"[^a-zA-Z0-9_]", "_", filename[:-5]) + group_name = "%s_%s" % (prefix, group_name) + definition = yaml.load(f.read(), OrderedLoader) + definition = resolve_references(filepath, definition) + if 'type' not in definition: + continue + try: + example = get_example_for_schema(definition) + except: + example = None + pass # do nothing - we don't care + if 'title' not in definition: + definition['title'] = "NO_TITLE" + definition['tables'] = get_tables_for_schema(definition) + defs[group_name] = { + "definition": definition, + "examples": [example] if example is not None else [], + } + return defs + def load_common_event_fields(self): """Parse the core event schema files @@ -706,12 +798,12 @@ class MatrixUnits(Units): if filename != event_name: examples[event_name] = examples.get(event_name, []) examples[event_name].append(example) - except Exception, e: + except Exception as e: e2 = Exception("Error reading event example "+filepath+": "+ str(e)) # throw the new exception with the old stack trace, so that # we don't lose information about where the error occurred. - raise e2, None, sys.exc_info()[2] + raise e2.with_traceback(sys.exc_info()[2]) return examples @@ -725,12 +817,12 @@ class MatrixUnits(Units): filepath = os.path.join(path, filename) try: schemata[filename] = self.read_event_schema(filepath) - except Exception, e: + except Exception as e: e2 = Exception("Error reading event schema "+filepath+": "+ str(e)) # throw the new exception with the old stack trace, so that # we don't lose information about where the error occurred. - raise e2, None, sys.exc_info()[2] + raise e2.with_traceback(sys.exc_info()[2]) return schemata @@ -826,12 +918,42 @@ class MatrixUnits(Units): path = os.path.join(CHANGELOG_DIR, f) name = f[:-4] + # If there's a directory with the same name, we'll try to generate + # a towncrier changelog and prepend it to the general changelog. + tc_path = os.path.join(CHANGELOG_DIR, name) + tc_lines = [] + if os.path.isdir(tc_path): + logger.info("Generating towncrier changelog for: %s" % name) + p = subprocess.Popen( + ['towncrier', '--version', 'Unreleased Changes', '--name', name, '--draft'], + cwd=tc_path, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + ) + stdout, stderr = p.communicate() + if p.returncode != 0: + # Something broke - dump as much information as we can + logger.error("Towncrier exited with code %s" % p.returncode) + logger.error(stdout.decode('UTF-8')) + logger.error(stderr.decode('UTF-8')) + raw_log = "" + else: + raw_log = stdout.decode('UTF-8') + + # This is a bit of a hack, but it does mean that the log at least gets *something* + # to tell us it broke + if not raw_log.startswith("Unreleased Changes"): + logger.error("Towncrier appears to have failed to generate a changelog") + logger.error(raw_log) + raw_log = "" + tc_lines = raw_log.splitlines() + title_part = None changelog_lines = [] with open(path, "r") as f: lines = f.readlines() prev_line = None - for line in lines: + for line in (tc_lines + lines): if prev_line is None: prev_line = line continue @@ -847,7 +969,10 @@ class MatrixUnits(Units): # then bail out. changelog_lines.pop() break - changelog_lines.append(" " + line) + # Don't generate subheadings (we'll keep the title though) + if re.match("^[-]{3,}$", line.strip()): + continue + changelog_lines.append(" " + line + '\n') changelogs[name] = "".join(changelog_lines) return changelogs @@ -866,7 +991,7 @@ class MatrixUnits(Units): ['git', 'rev-parse', '--abbrev-ref', 'HEAD'], stderr=null, cwd=cwd, - ).strip() + ).strip().decode('UTF-8') except subprocess.CalledProcessError: git_branch = "" try: @@ -874,7 +999,7 @@ class MatrixUnits(Units): ['git', 'describe', '--exact-match'], stderr=null, cwd=cwd, - ).strip() + ).strip().decode('UTF-8') git_tag = "tag=" + git_tag except subprocess.CalledProcessError: git_tag = "" @@ -883,7 +1008,7 @@ class MatrixUnits(Units): ['git', 'rev-parse', '--short', 'HEAD'], stderr=null, cwd=cwd, - ).strip() + ).strip().decode('UTF-8') except subprocess.CalledProcessError: git_commit = "" try: @@ -892,7 +1017,7 @@ class MatrixUnits(Units): ['git', 'describe', '--dirty=' + dirty_string, "--all"], stderr=null, cwd=cwd, - ).strip().endswith(dirty_string) + ).strip().decode('UTF-8').endswith(dirty_string) git_dirty = "dirty" if is_dirty else "" except subprocess.CalledProcessError: git_dirty = "" @@ -903,7 +1028,7 @@ class MatrixUnits(Units): s for s in (git_branch, git_tag, git_commit, git_dirty,) if s - ).encode("ascii") + ).encode("ascii").decode('ascii') return { "string": git_version, "revision": git_commit diff --git a/scripts/test-and-build.sh b/scripts/test-and-build.sh index 7794f826..710b03dd 100755 --- a/scripts/test-and-build.sh +++ b/scripts/test-and-build.sh @@ -4,8 +4,13 @@ set -ex cd `dirname $0`/.. -virtualenv env +virtualenv -p python3 env . env/bin/activate + +# Print out the python versions for debugging purposes +python --version +pip --version + pip install -r scripts/requirements.txt # do sanity checks on the examples and swagger diff --git a/specification/appendices/identifier_grammar.rst b/specification/appendices/identifier_grammar.rst index e85bf410..7156c7d5 100644 --- a/specification/appendices/identifier_grammar.rst +++ b/specification/appendices/identifier_grammar.rst @@ -25,8 +25,7 @@ number of identifiers, as described below. The server name represents the address at which the homeserver in question can be reached by other homeservers. The complete grammar is:: - server_name = dns_name [ ":" port] - dns_name = host + server_name = host [ ":" port] port = *DIGIT where ``host`` is as defined by `RFC3986, section 3.2.2 @@ -60,6 +59,7 @@ The sigil characters are as follows: * ``@``: User ID * ``!``: Room ID * ``$``: Event ID +* ``+``: Group ID * ``#``: Room alias The precise grammar defining the allowable format of an identifier depends on @@ -207,6 +207,35 @@ readable. .. TODO-spec What is the grammar for the opaque part? https://matrix.org/jira/browse/SPEC-389 + +Group Identifiers ++++++++++++++++++ + +Groups within Matrix are uniquely identified by their group ID. The group +ID is namespaced to the group server which hosts this group and has the +form:: + + +localpart:domain + +The ``localpart`` of a group ID is an opaque identifier for that group. It MUST +NOT be empty, and MUST contain only the characters ``a-z``, ``0-9``, ``.``, +``_``, ``=``, ``-``, and ``/``. + +The ``domain`` of a group ID is the `server name`_ of the group server which +hosts this group. + +The length of a group ID, including the ``+`` sigil and the domain, MUST NOT +exceed 255 characters. + +The complete grammar for a legal group ID is:: + + group_id = "+" group_id_localpart ":" server_name + group_id_localpart = 1*group_id_char + group_id_char = DIGIT + / %x61-7A ; a-z + / "-" / "." / "=" / "_" / "/" + + Room Aliases ++++++++++++ diff --git a/specification/application_service_api.rst b/specification/application_service_api.rst index 31983a0c..9e84339f 100644 --- a/specification/application_service_api.rst +++ b/specification/application_service_api.rst @@ -1,4 +1,5 @@ .. Copyright 2016 OpenMarket Ltd +.. Copyright 2018 New Vector Ltd .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -39,7 +40,7 @@ This version of the specification is generated from Application Services -------------------- Application services are passive and can only observe events from a given -homeserver. They can inject events into rooms they are participating in. +homeserver (HS). They can inject events into rooms they are participating in. They cannot prevent events from being sent, nor can they modify the content of the event being sent. In order to observe events from a homeserver, the homeserver needs to be configured to pass certain types of traffic to the @@ -68,7 +69,13 @@ Registration Application services register "namespaces" of user IDs, room aliases and room IDs. These namespaces are represented as regular expressions. An application service is said to be "interested" in a given event if one of the IDs in the event match -the regular expression provided by the application service. An application +the regular expression provided by the application service, such as the room having +an alias or ID in the relevant namespaces. Similarly, the application service is +said to be interested in a given event if one of the application service's namespaced +users is the target of the event, or is a joined member of the room where the event +occurred. + +An application service can also state whether they should be the only ones who can manage a specified namespace. This is referred to as an "exclusive" namespace. An exclusive namespace prevents humans and other application @@ -167,6 +174,12 @@ An example registration file for an IRC-bridging application service is below: regex: "#_irc_bridge_.*" rooms: [] +Exclusive user and alias namespaces should begin with an underscore after the +sigil to avoid collisions with other users on the homeserver. Application +services should additionally attempt to identify the service they represent +in the reserved namespace. For example, ``@_irc_.*`` would be a good namespace +to register for an application service which deals with IRC. + .. WARNING:: If the homeserver in question has multiple application services, each ``as_token`` and ``id`` MUST be unique per application service as these are @@ -278,7 +291,8 @@ need to be able to adjust the ``origin_server_ts`` value to do this. Inputs: - Application service token (``as_token``) - - Desired timestamp + - Desired timestamp (in milliseconds since the unix epoch) + Notes: - This will only apply when sending events. @@ -325,6 +339,14 @@ normal users who attempt to create users or aliases *inside* an application service-defined namespace will receive the same ``M_EXCLUSIVE`` error code, but only if the application service has defined the namespace as ``exclusive``. +Using ``/sync`` and ``/events`` ++++++++++++++++++++++++++++++++ + +Application services wishing to use ``/sync`` or ``/events`` from the Client-Server +API MUST do so with a virtual user (provide a ``user_id`` via the query string). It +is expectected that the application service use the transactions pushed to it to +handle events rather than syncing with the user implied by ``sender_localpart``. + ID conventions ~~~~~~~~~~~~~~ .. TODO-spec diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index dec3a4f4..7cd14f7f 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -164,6 +164,25 @@ recommended. {{versions_cs_http_api}} +Web Browser Clients +------------------- + +It is realistic to expect that some clients will be written to be run within a +web browser or similar environment. In these cases, the homeserver should respond +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: + +.. code:: + + Access-Control-Allow-Origin: * + Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS + Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization + + Client Authentication --------------------- @@ -172,10 +191,6 @@ previously obtained credentials in the form of an ``access_token`` query parameter or through an Authorization Header of ``Bearer $access_token``. An access token is typically obtained via the `Login`_ or `Registration`_ processes. -When credentials are required but missing or invalid, the HTTP call will -return with a status of 401 and the error code, ``M_MISSING_TOKEN`` or -``M_UNKNOWN_TOKEN`` respectively. - .. NOTE:: This specification does not mandate a particular format for the access @@ -183,6 +198,24 @@ return with a status of 401 and the error code, ``M_MISSING_TOKEN`` or to choose an appropriate format. Server implementors may like to investigate `macaroons `_. +Using access tokens +~~~~~~~~~~~~~~~~~~~ + +Access tokens may be provided in two ways, both of which the homeserver MUST +support: + +1. Via a query string parameter, ``access_token=TheTokenHere``. +#. Via a request header, ``Authorization: Bearer TheTokenHere``. + +Clients are encouraged to use the ``Authorization`` header where possible +to prevent the access token being leaked in access/HTTP logs. The query +string should only be used in cases where the ``Authorization`` header is +inaccessible for the client. + +When credentials are required but missing or invalid, the HTTP call will +return with a status of 401 and the error code, ``M_MISSING_TOKEN`` or +``M_UNKNOWN_TOKEN`` respectively. + Relationship between access tokens and devices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -442,7 +475,7 @@ Password-based :Type: ``m.login.password`` :Description: - The client submits a username and secret password, both sent in plain-text. + The client submits an identifier and secret password, both sent in plain-text. To use this authentication type, clients should submit an auth dict as follows: @@ -450,7 +483,26 @@ To use this authentication type, clients should submit an auth dict as follows: { "type": "m.login.password", - "user": "", + "identifier": { + ... + }, + "password": "", + "session": "" + } + +where the ``identifier`` property is a user identifier object, as described in +`Identifier types`_. + +For example, to authenticate using the user's Matrix ID, clients whould submit: + +.. code:: json + + { + "type": "m.login.password", + "identifier": { + "type": "m.id.user", + "user": "" + }, "password": "", "session": "" } @@ -463,8 +515,11 @@ follows: { "type": "m.login.password", - "medium": "", - "address": "", + "identifier": { + "type": "m.id.thirdparty", + "medium": "", + "address": "" + }, "password": "", "session": "" } @@ -695,6 +750,78 @@ handle unknown login types: } +Identifier types +++++++++++++++++ + +Some authentication mechanisms use a user identifier object to identify a +user. The user identifier object has a ``type`` field to indicate the type of +identifier being used, and depending on the type, has other fields giving the +information required to identify the user as described below. + +This specification defines the following identifier types: + - ``m.id.user`` + - ``m.id.thirdparty`` + - ``m.id.phone`` + +Matrix User ID +<<<<<<<<<<<<<< +:Type: + ``m.id.user`` +:Description: + The user is identified by their Matrix ID. + +A client can identify a user using their Matrix ID. This can either be the +fully qualified Matrix user ID, or just the localpart of the user ID. + +.. code:: json + + "identifier": { + "type": "m.id.user", + "user": "" + } + +Third-party ID +<<<<<<<<<<<<<< +:Type: + ``m.id.thirdparty`` +:Description: + The user is identified by a third-party identifer in canonicalised form. + +A client can identify a user using a 3pid associated with the user's account on +the homeserver, where the 3pid was previously associated using the +|/account/3pid|_ API. See the `3PID Types`_ Appendix for a list of Third-party +ID media. + +.. code:: json + + "identifier": { + "type": "m.id.thirdparty", + "medium": "", + "address": "" + } + +Phone number +<<<<<<<<<<<< +:Type: + ``m.id.phone`` +:Description: + The user is identified by a phone number. + +A client can identify a user using a phone number associated with the user's +account, where the phone number was previously associated using the +|/account/3pid|_ API. The phone number can be passed in as entered by the +user; the homeserver will be responsible for canonicalising it. If the client +wishes to canonicalise the phone number, then it can use the +``m.id.thirdparty`` identifier type with a ``medium`` of ``msisdn`` instead. + +.. code:: json + + "identifier": { + "type": "m.id.phone", + "country": "", + "phone": "" + } + Login ~~~~~ @@ -710,7 +837,10 @@ request as follows: { "type": "m.login.password", - "user": "", + "identifier": { + "type": "m.id.user", + "user": "" + }, "password": "" } @@ -722,8 +852,10 @@ explicitly, as follows: { "type": "m.login.password", - "medium": "", - "address": "", + "identifier": { + "medium": "", + "address": "" + }, "password": "" } @@ -1398,7 +1530,7 @@ have to wait in milliseconds before they can try again. .. References .. _`macaroon`: http://research.google.com/pubs/pub41892.html -.. _`devices`: ../intro.html#devices +.. _`devices`: ../index.html#devices .. Links through the external API docs are below .. ============================================= @@ -1458,3 +1590,4 @@ have to wait in milliseconds before they can try again. .. _/user//account_data/: #put-matrix-client-%CLIENT_MAJOR_VERSION%-user-userid-account-data-type .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 +.. _`3PID Types`: ../appendices.html#pid-types diff --git a/specification/feature_profiles.rst b/specification/feature_profiles.rst index 7fc9696d..c6b8ef4c 100644 --- a/specification/feature_profiles.rst +++ b/specification/feature_profiles.rst @@ -42,6 +42,7 @@ Summary `Server Side Search`_ Optional Optional Optional Optional Optional `Server Administration`_ Optional Optional Optional Optional Optional `Event Context`_ Optional Optional Optional Optional Optional + `Third Party Networks`_ Optional Optional Optional Optional Optional ===================================== ========== ========== ========== ========== ========== *Please see each module for more details on what clients need to implement.* @@ -57,6 +58,7 @@ Summary .. _Server Side Search: `module:search`_ .. _Server Administration: `module:admin`_ .. _Event Context: `module:event-context`_ +.. _Third Party Networks: `module:third-party-networks`_ Clients ------- diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 89fcc3e6..3b037caf 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -114,100 +114,20 @@ session, within a 24 hour period since its most recent modification. Any attempts to perform these actions after the expiry will be rejected, and a new session should be created and used instead. -Creating a session +Email associations ~~~~~~~~~~~~~~~~~~ -A client makes a call to:: +{{email_associations_is_http_api}} - POST https://my.id.server:8090/_matrix/identity/api/v1/validate/email/requestToken +Phone number associations +~~~~~~~~~~~~~~~~~~~~~~~~~ - client_secret=monkeys_are_GREAT& - email=foo@bar.com& - send_attempt=1 +{{phone_associations_is_http_api}} -It may also optionally specify next_link. If next_link is specified, when the -validation is completed, the identity service will redirect the user to that -URL. +General +~~~~~~~ -This will create a new "session" on the identity service, identified by an -``sid``. - -The identity service will send an email containing a token. If that token is -presented to the identity service 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. - -We return the ``sid`` generated for this session to the caller, in a JSON object -containing the ``sid`` key. - -If a send_attempt is specified, the server will only send an email if the -send_attempt is a number greater than the most recent one which it has seen (or -if it has never seen one), scoped to that email address + client_secret pair. -This is to avoid repeatedly sending the same email in the case of request -retries between the POSTing user and the identity service. The client should -increment this value if they desire a new email (e.g. a reminder) to be sent. - -Note that Home Servers 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. - -Validating ownership of an email -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A user may make either a ``GET`` or a ``POST`` request to -``/_matrix/identity/api/v1/validate/email/submitToken`` with the following -parameters (either as query parameters or URL-encoded POST parameters): -- ``sid`` the sid for the session, generated by the ``requestToken`` call. -- ``client_secret`` the client secret which was supplied to the ``requestToken`` call. -- ``token`` the token generated by the ``requestToken`` call, and emailed to the user. - -If these three values 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. - -Otherwise, an error will be returned. - -Checking non-published 3pid ownership -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A client can check whether ownership of a 3pid was validated by making an -HTTP GET request to ``/_matrix/identity/api/v1/3pid/getValidated3pid``, passing -the ``sid`` and ``client_secret`` as query parameters from the ``requestToken`` -call. - -It will return something of either the form:: - - {"medium": "email", "validated_at": 1457622739026, "address": "foo@bar.com"} - -or:: - - {"errcode": "M_SESSION_NOT_VALIDATED", "error": "This validation session has not yet been completed"} - -If the ``sid`` and ``client_secret`` were not recognised, or were not correct, -an error will be returned. - -Publishing a validated association -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An association between a session and a Matrix user ID can be published by making -a URL-encoded HTTP POST request to ``/_matrix/identity/api/v1/3pid/bind`` with -the following parameters:: - - sid=sid& - client_secret=monkeys_are_GREAT& - mxid=@foo:bar.com - -If the session is still valid, this will publish an association between the -3pids validated on that session and the passed Matrix user ID. Future calls -to ``/lookup`` for any of the session's 3pids will return this association. - -If the 3pid has not yet been validated, the HTTP request will be rejected, and -the association will not be established. - -If the ``sid`` and ``client_secret`` were not recognised, or were not correct, -an error will be returned. +{{associations_is_http_api}} Invitation Storage ------------------ @@ -216,32 +136,6 @@ An identity service can store pending invitations to a user's 3pid, which will be retrieved and can be either notified on or look up when the 3pid is associated with a Matrix user ID. -If one makes a ``POST`` request to ``/_matrix/identity/api/v1/store-invite`` with the following URL-encoded POST parameters: - -- ``medium`` (string, required): The literal string ``email``. -- ``address`` (string, required): The email address of the invited user. -- ``room_id`` (string, required): The Matrix room ID to which the user is invited. -- ``sender`` (string, required): The matrix user ID of the inviting user. - -An arbitrary number of other parameters may also be specified. These may be used in the email generation described below. - -The service will look up whether the 3pid is bound to a Matrix user ID. If it is, the request will be rejected with a 400 status code. - -If the medium is something other than the literal string ``email``, the request will be rejected with a 400 status code. - -Otherwise, the service will then generate a random string called ``token``, and an ephemeral public key. - -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. - -The response body is then populated as the JSON-encoded dictionary containing the following fields: -- ``token`` (string): The generated token. -- ``public_keys`` ([string]): A list of [server's long-term public key, generated ephemeral public key]. -- ``display_name`` (string): The generated (redacted) display_name. - At a later point, if the owner of that particular 3pid binds it with a Matrix user ID, the identity server will attempt to make an HTTP POST to the Matrix user's homeserver which looks roughly as below:: POST https://bar.com:8448/_matrix/federation/v1/3pid/onbind @@ -273,7 +167,7 @@ At a later point, if the owner of that particular 3pid binds it with a Matrix us Where the signature is produced using a long-term private key. -Also, the generated ephemeral public key will be listed as valid on requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``. +{{store_invite_is_http_api}} Ephemeral invitation signing ---------------------------- @@ -281,23 +175,7 @@ Ephemeral invitation signing To aid clients who may not be able to perform crypto themselves, the identity service offers some crypto functionality to help in accepting invitations. This is less secure than the client doing it itself, but may be useful where this isn't possible. -The identity service will happily sign invitation details with a request-specified ed25519 private key for you, if you want it to. It takes URL-encoded POST parameters: -- mxid (string, required) -- token (string, required) -- private_key (string, required): The private key, encoded as `Unpadded base64`_. - -It will look up ``token`` which was stored in a call to ``store-invite``, and fetch the sender of the invite. It will then respond with JSON which looks something like:: - - { - "mxid": "@foo:bar.com", - "sender": "@baz:bar.com", - "signatures" { - "my.id.server": { - "ed25519:0": "def987" - } - }, - "token": "abc123" - } +{{invitation_signing_is_http_api}} .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 .. _`3PID Types`: ../appendices.html#pid-types diff --git a/specification/index.rst b/specification/index.rst index f9a3fef3..36c89958 100644 --- a/specification/index.rst +++ b/specification/index.rst @@ -30,18 +30,392 @@ communication ecosystem. To propose a change to the Matrix Spec, see the explanations at `Proposals for Spec Changes to Matrix `_. +.. contents:: Table of Contents +.. sectnum:: + Matrix APIs ----------- The specification consists of the following parts: -`Introduction to Matrix `_ provides a full introduction to Matrix and the spec. - {{apis}} The `Appendices `_ contain supplemental information not specific to one of the above APIs. +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 +and support a new global real-time communication ecosystem. The intention is to +provide an open decentralised pubsub layer for the internet for securely +persisting and publishing/subscribing JSON objects. This specification is the +ongoing result of standardising the APIs used by the various components of the +Matrix ecosystem to communicate with one another. + +The principles that Matrix attempts to follow are: + +- Pragmatic Web-friendly APIs (i.e. JSON over REST) +- Keep It Simple & Stupid + + + provide a simple architecture with minimal third-party dependencies. + +- Fully open: + + + Fully open federation - anyone should be able to participate in the global + Matrix network + + Fully open standard - publicly documented standard with no IP or patent + licensing encumbrances + + Fully open source reference implementation - liberally-licensed example + implementations with no IP or patent licensing encumbrances + +- Empowering the end-user + + + The user should be able to choose the server and clients they use + + The user should be control how private their communication is + + The user should know precisely where their data is stored + +- Fully decentralised - no single points of control over conversations or the + network as a whole +- Learning from history to avoid repeating it + + + Trying to take the best aspects of XMPP, SIP, IRC, SMTP, IMAP and NNTP + whilst trying to avoid their failings + + +The functionality that Matrix provides includes: + +- Creation and management of fully distributed chat rooms with no + single points of control or failure +- Eventually-consistent cryptographically secure synchronisation of room + state across a global open network of federated servers and services +- Sending and receiving extensible messages in a room with (optional) + end-to-end encryption +- Extensible user management (inviting, joining, leaving, kicking, banning) + mediated by a power-level based user privilege system. +- Extensible room state management (room naming, aliasing, topics, bans) +- Extensible user profile management (avatars, display names, etc) +- Managing user accounts (registration, login, logout) +- Use of 3rd Party IDs (3PIDs) such as email addresses, phone numbers, + Facebook accounts to authenticate, identify and discover users on Matrix. +- Trusted federation of Identity servers for: + + + Publishing user public keys for PKI + + Mapping of 3PIDs to Matrix IDs + + +The end goal of Matrix is to be a ubiquitous messaging layer for synchronising +arbitrary data between sets of people, devices and services - be that for +instant messages, VoIP call setups, or any other objects that need to be +reliably and persistently pushed from A to B in an interoperable and federated +manner. + + +Spec Change Proposals +~~~~~~~~~~~~~~~~~~~~~ +To propose a change to the Matrix Spec, see the explanations at `Proposals +for Spec Changes to Matrix `_. + + +Architecture +------------ + +Matrix defines APIs for synchronising extensible JSON objects known as +"events" between compatible clients, servers and services. Clients are +typically messaging/VoIP applications or IoT devices/hubs and communicate by +synchronising communication history with their "homeserver" using the +"Client-Server API". Each homeserver stores the communication history and +account information for all of its clients, and shares data with the wider +Matrix ecosystem by synchronising communication history with other homeservers +and their clients. + +Clients typically communicate with each other by emitting events in the +context of a virtual "room". Room data is replicated across *all of the +homeservers* whose users are participating in a given room. As such, *no +single homeserver has control or ownership over a given room*. Homeservers +model communication history as a partially ordered graph of events known as +the room's "event graph", which is synchronised with eventual consistency +between the participating servers using the "Server-Server API". This process +of synchronising shared conversation history between homeservers run by +different parties is called "Federation". Matrix optimises for the +Availability and Partitioned properties of CAP theorem at +the expense of Consistency. + +For example, for client A to send a message to client B, client A performs an +HTTP PUT of the required JSON event on its homeserver (HS) using the +client-server API. A's HS appends this event to its copy of the room's event +graph, signing the message in the context of the graph for integrity. A's HS +then replicates the message to B's HS by performing an HTTP PUT using the +server-server API. B's HS authenticates the request, validates the event's +signature, authorises the event's contents and then adds it to its copy of the +room's event graph. Client B then receives the message from his homeserver via +a long-lived GET request. + +:: + + How data flows between clients + ============================== + + { Matrix client A } { Matrix client B } + ^ | ^ | + | events | Client-Server API | events | + | V | V + +------------------+ +------------------+ + | |---------( HTTPS )--------->| | + | homeserver | | homeserver | + | |<--------( HTTPS )----------| | + +------------------+ Server-Server API +------------------+ + History Synchronisation + (Federation) + + +Users +~~~~~ + +Each client is associated with a user account, which is identified in Matrix +using a unique "user ID". This ID is namespaced to the homeserver which +allocated the account and has the form:: + + @localpart:domain + +See `'Identifier Grammar' the appendices `_ for full details of +the structure of user IDs. + +Devices +~~~~~~~ + +The Matrix specification has a particular meaning for the term "device". As a +user, I might have several devices: a desktop client, some web browsers, an +Android device, an iPhone, etc. They broadly relate to a real device in the +physical world, but you might have several browsers on a physical device, or +several Matrix client applications on a mobile device, each of which would be +its own device. + +Devices are used primarily to manage the keys used for end-to-end encryption +(each device gets its own copy of the decryption keys), but they also help +users manage their access - for instance, by revoking access to particular +devices. + +When a user first uses a client, it registers itself as a new device. The +longevity of devices might depend on the type of client. A web client will +probably drop all of its state on logout, and create a new device every time +you log in, to ensure that cryptography keys are not leaked to a new user. In +a mobile client, it might be acceptable to reuse the device if a login session +expires, provided the user is the same. + +Devices are identified by a ``device_id``, which is unique within the scope of +a given user. + +A user may assign a human-readable display name to a device, to help them +manage their devices. + +Events +~~~~~~ + +All data exchanged over Matrix is expressed as an "event". Typically each client +action (e.g. sending a message) correlates with exactly one event. Each event +has a ``type`` which is used to differentiate different kinds of data. ``type`` +values MUST be uniquely globally namespaced following Java's `package naming +conventions`_, e.g. +``com.example.myapp.event``. The special top-level namespace ``m.`` is reserved +for events defined in the Matrix specification - for instance ``m.room.message`` +is the event type for instant messages. Events are usually sent in the context +of a "Room". + +.. _package naming conventions: https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions + +Event Graphs +~~~~~~~~~~~~ + +.. _sect:event-graph: + +Events exchanged in the context of a room are stored in a directed acyclic graph +(DAG) called an "event graph". The partial ordering of this graph gives the +chronological ordering of events within the room. Each event in the graph has a +list of zero or more "parent" events, which refer to any preceding events +which have no chronological successor from the perspective of the homeserver +which created the event. + +Typically an event has a single parent: the most recent message in the room at +the point it was sent. However, homeservers may legitimately race with each +other when sending messages, resulting in a single event having multiple +successors. The next event added to the graph thus will have multiple parents. +Every event graph has a single root event with no parent. + +To order and ease chronological comparison between the events within the graph, +homeservers maintain a ``depth`` metadata field on each event. An event's +``depth`` is a positive integer that is strictly greater than the depths of any +of its parents. The root event should have a depth of 1. Thus if one event is +before another, then it must have a strictly smaller depth. + +Room structure +~~~~~~~~~~~~~~ + +A room is a conceptual place where users can send and receive events. Events are +sent to a room, and all participants in that room with sufficient access will +receive the event. Rooms are uniquely identified internally via "Room IDs", +which have the form:: + + !opaque_id:domain + +There is exactly one room ID for each room. Whilst the room ID does contain a +domain, it is simply for globally namespacing room IDs. The room does NOT +reside on the domain specified. + +See `'Identifier Grammar' in the appendices `_ for full details of +the structure of a room ID. + +The following conceptual diagram shows an +``m.room.message`` event being sent to the room ``!qporfwt:matrix.org``:: + + { @alice:matrix.org } { @bob:domain.com } + | ^ + | | + [HTTP POST] [HTTP GET] + Room ID: !qporfwt:matrix.org Room ID: !qporfwt:matrix.org + Event type: m.room.message Event type: m.room.message + Content: { JSON object } Content: { JSON object } + | | + V | + +------------------+ +------------------+ + | homeserver | | homeserver | + | matrix.org | | domain.com | + +------------------+ +------------------+ + | ^ + | [HTTP PUT] | + | Room ID: !qporfwt:matrix.org | + | Event type: m.room.message | + | Content: { JSON object } | + `-------> Pointer to the preceding message ------` + PKI signature from matrix.org + Transaction-layer metadata + PKI Authorization header + + ................................... + | Shared Data | + | State: | + | Room ID: !qporfwt:matrix.org | + | Servers: matrix.org, domain.com | + | Members: | + | - @alice:matrix.org | + | - @bob:domain.com | + | Messages: | + | - @alice:matrix.org | + | Content: { JSON object } | + |...................................| + +Federation maintains *shared data structures* per-room between multiple home +servers. The data is split into ``message events`` and ``state events``. + +Message events: + These describe transient 'once-off' activity in a room such as an + instant messages, VoIP call setups, file transfers, etc. They generally + describe communication activity. + +State events: + These describe updates to a given piece of persistent information + ('state') related to a room, such as the room's name, topic, membership, + participating servers, etc. State is modelled as a lookup table of key/value + pairs per room, with each key being a tuple of ``state_key`` and ``event type``. + Each state event updates the value of a given key. + +The state of the room at a given point is calculated by considering all events +preceding and including a given event in the graph. Where events describe the +same state, a merge conflict algorithm is applied. The state resolution +algorithm is transitive and does not depend on server state, as it must +consistently select the same event irrespective of the server or the order the +events were received in. Events are signed by the originating server (the +signature includes the parent relations, type, depth and payload hash) and are +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. + + +Room Aliases +++++++++++++ + +Each room can also have multiple "Room Aliases", which look like:: + + #room_alias:domain + +See `'Identifier Grammar' in the appendices `_ for full details of +the structure of a room alias. + +A room alias "points" to a room ID and is the human-readable label by which +rooms are publicised and discovered. The room ID the alias is pointing to can +be obtained by visiting the domain specified. Note that the mapping from a room +alias to a room ID is not fixed, and may change over time to point to a +different room ID. For this reason, Clients SHOULD resolve the room alias to a +room ID once and then use that ID on subsequent requests. + +When resolving a room alias the server will also respond with a list of servers +that are in the room that can be used to join via. + +:: + + HTTP GET + #matrix:domain.com !aaabaa:matrix.org + | ^ + | | + _______V____________________|____ + | domain.com | + | Mappings: | + | #matrix >> !aaabaa:matrix.org | + | #golf >> !wfeiofh:sport.com | + | #bike >> !4rguxf:matrix.org | + |________________________________| + +Identity +~~~~~~~~ + +Users in Matrix are identified via their Matrix user ID. However, +existing 3rd party ID namespaces can also be used in order to identify Matrix +users. A Matrix "Identity" describes both the user ID and any other existing IDs +from third party namespaces *linked* to their account. +Matrix users can *link* third-party IDs (3PIDs) such as email addresses, social +network accounts and phone numbers to their user ID. Linking 3PIDs creates a +mapping from a 3PID to a user ID. This mapping can then be used by Matrix +users in order to discover the user IDs of their contacts. +In order to ensure that the mapping from 3PID to user ID is genuine, a globally +federated cluster of trusted "Identity Servers" (IS) are used to verify the 3PID +and persist and replicate the mappings. + +Usage of an IS is not required in order for a client application to be part of +the Matrix ecosystem. However, without one clients will not be able to look up +user IDs using 3PIDs. + + +Profiles +~~~~~~~~ + +Users may publish arbitrary key/value data associated with their account - such +as a human readable display name, a profile photo URL, contact information +(email address, phone numbers, website URLs etc). + +.. TODO + Actually specify the different types of data - e.g. what format are display + names allowed to be? + +Private User Data +~~~~~~~~~~~~~~~~~ + +Users may also store arbitrary private key/value data in their account - such as +client preferences, or server configuration settings which lack any other +dedicated API. The API is symmetrical to managing Profile data. + +.. TODO + Would it really be overengineered to use the same API for both profile & + private user data, but with different ACLs? + Specification Versions ---------------------- @@ -57,3 +431,9 @@ The specification for each API is versioned in the form ``rX.Y.Z``. * A change to ``Z`` represents a change which is backwards-compatible on both sides. Typically this implies a clarification to the specification, rather than a change which must be implemented. + +License +------- + +The Matrix specification is licensed under the `Apache License, Version 2.0 +`_. diff --git a/specification/intro.rst b/specification/intro.rst deleted file mode 100644 index ad545248..00000000 --- a/specification/intro.rst +++ /dev/null @@ -1,400 +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. - -.. contents:: Table of Contents -.. sectnum:: - -.. Note that this file is specifically unversioned because we don't want to -.. have to add Yet Another version number, and the commentary on what specs we -.. have should hopefully not get complex enough that we need to worry about -.. versioning it. - -Introduction ------------- -.. 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 -and support a new global real-time communication ecosystem. The intention is to -provide an open decentralised pubsub layer for the internet for securely -persisting and publishing/subscribing JSON objects. This specification is the -ongoing result of standardising the APIs used by the various components of the -Matrix ecosystem to communicate with one another. - -The principles that Matrix attempts to follow are: - -- Pragmatic Web-friendly APIs (i.e. JSON over REST) -- Keep It Simple & Stupid - - + provide a simple architecture with minimal third-party dependencies. - -- Fully open: - - + Fully open federation - anyone should be able to participate in the global - Matrix network - + Fully open standard - publicly documented standard with no IP or patent - licensing encumbrances - + Fully open source reference implementation - liberally-licensed example - implementations with no IP or patent licensing encumbrances - -- Empowering the end-user - - + The user should be able to choose the server and clients they use - + The user should be control how private their communication is - + The user should know precisely where their data is stored - -- Fully decentralised - no single points of control over conversations or the - network as a whole -- Learning from history to avoid repeating it - - + Trying to take the best aspects of XMPP, SIP, IRC, SMTP, IMAP and NNTP - whilst trying to avoid their failings - - -The functionality that Matrix provides includes: - -- Creation and management of fully distributed chat rooms with no - single points of control or failure -- Eventually-consistent cryptographically secure synchronisation of room - state across a global open network of federated servers and services -- Sending and receiving extensible messages in a room with (optional) - end-to-end encryption -- Extensible user management (inviting, joining, leaving, kicking, banning) - mediated by a power-level based user privilege system. -- Extensible room state management (room naming, aliasing, topics, bans) -- Extensible user profile management (avatars, display names, etc) -- Managing user accounts (registration, login, logout) -- Use of 3rd Party IDs (3PIDs) such as email addresses, phone numbers, - Facebook accounts to authenticate, identify and discover users on Matrix. -- Trusted federation of Identity servers for: - - + Publishing user public keys for PKI - + Mapping of 3PIDs to Matrix IDs - - -The end goal of Matrix is to be a ubiquitous messaging layer for synchronising -arbitrary data between sets of people, devices and services - be that for -instant messages, VoIP call setups, or any other objects that need to be -reliably and persistently pushed from A to B in an interoperable and federated -manner. - - -Spec Change Proposals -~~~~~~~~~~~~~~~~~~~~~ -To propose a change to the Matrix Spec, see the explanations at `Proposals -for Spec Changes to Matrix `_. - - -Architecture ------------- - -Matrix defines APIs for synchronising extensible JSON objects known as -"events" between compatible clients, servers and services. Clients are -typically messaging/VoIP applications or IoT devices/hubs and communicate by -synchronising communication history with their "homeserver" using the -"Client-Server API". Each homeserver stores the communication history and -account information for all of its clients, and shares data with the wider -Matrix ecosystem by synchronising communication history with other homeservers -and their clients. - -Clients typically communicate with each other by emitting events in the -context of a virtual "room". Room data is replicated across *all of the -homeservers* whose users are participating in a given room. As such, *no -single homeserver has control or ownership over a given room*. Homeservers -model communication history as a partially ordered graph of events known as -the room's "event graph", which is synchronised with eventual consistency -between the participating servers using the "Server-Server API". This process -of synchronising shared conversation history between homeservers run by -different parties is called "Federation". Matrix optimises for the -Availability and Partitioned properties of CAP theorem at -the expense of Consistency. - -For example, for client A to send a message to client B, client A performs an -HTTP PUT of the required JSON event on its homeserver (HS) using the -client-server API. A's HS appends this event to its copy of the room's event -graph, signing the message in the context of the graph for integrity. A's HS -then replicates the message to B's HS by performing an HTTP PUT using the -server-server API. B's HS authenticates the request, validates the event's -signature, authorises the event's contents and then adds it to its copy of the -room's event graph. Client B then receives the message from his homeserver via -a long-lived GET request. - -:: - - How data flows between clients - ============================== - - { Matrix client A } { Matrix client B } - ^ | ^ | - | events | Client-Server API | events | - | V | V - +------------------+ +------------------+ - | |---------( HTTPS )--------->| | - | homeserver | | homeserver | - | |<--------( HTTPS )----------| | - +------------------+ Server-Server API +------------------+ - History Synchronisation - (Federation) - - -Users -~~~~~ - -Each client is associated with a user account, which is identified in Matrix -using a unique "user ID". This ID is namespaced to the homeserver which -allocated the account and has the form:: - - @localpart:domain - -See the `appendices `_ for full details of -the structure of user IDs. - -Devices -~~~~~~~ - -The Matrix specification has a particular meaning for the term "device". As a -user, I might have several devices: a desktop client, some web browsers, an -Android device, an iPhone, etc. They broadly relate to a real device in the -physical world, but you might have several browsers on a physical device, or -several Matrix client applications on a mobile device, each of which would be -its own device. - -Devices are used primarily to manage the keys used for end-to-end encryption -(each device gets its own copy of the decryption keys), but they also help -users manage their access - for instance, by revoking access to particular -devices. - -When a user first uses a client, it registers itself as a new device. The -longevity of devices might depend on the type of client. A web client will -probably drop all of its state on logout, and create a new device every time -you log in, to ensure that cryptography keys are not leaked to a new user. In -a mobile client, it might be acceptable to reuse the device if a login session -expires, provided the user is the same. - -Devices are identified by a ``device_id``, which is unique within the scope of -a given user. - -A user may assign a human-readable display name to a device, to help them -manage their devices. - -Events -~~~~~~ - -All data exchanged over Matrix is expressed as an "event". Typically each client -action (e.g. sending a message) correlates with exactly one event. Each event -has a ``type`` which is used to differentiate different kinds of data. ``type`` -values MUST be uniquely globally namespaced following Java's `package naming -conventions`_, e.g. -``com.example.myapp.event``. The special top-level namespace ``m.`` is reserved -for events defined in the Matrix specification - for instance ``m.room.message`` -is the event type for instant messages. Events are usually sent in the context -of a "Room". - -.. _package naming conventions: https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions - -Event Graphs -~~~~~~~~~~~~ - -.. _sect:event-graph: - -Events exchanged in the context of a room are stored in a directed acyclic graph -(DAG) called an "event graph". The partial ordering of this graph gives the -chronological ordering of events within the room. Each event in the graph has a -list of zero or more "parent" events, which refer to any preceding events -which have no chronological successor from the perspective of the homeserver -which created the event. - -Typically an event has a single parent: the most recent message in the room at -the point it was sent. However, homeservers may legitimately race with each -other when sending messages, resulting in a single event having multiple -successors. The next event added to the graph thus will have multiple parents. -Every event graph has a single root event with no parent. - -To order and ease chronological comparison between the events within the graph, -homeservers maintain a ``depth`` metadata field on each event. An event's -``depth`` is a positive integer that is strictly greater than the depths of any -of its parents. The root event should have a depth of 1. Thus if one event is -before another, then it must have a strictly smaller depth. - -Room structure -~~~~~~~~~~~~~~ - -A room is a conceptual place where users can send and receive events. Events are -sent to a room, and all participants in that room with sufficient access will -receive the event. Rooms are uniquely identified internally via "Room IDs", -which have the form:: - - !opaque_id:domain - -There is exactly one room ID for each room. Whilst the room ID does contain a -domain, it is simply for globally namespacing room IDs. The room does NOT -reside on the domain specified. - -See the `appendices `_ for full details of -the structure of a room ID. - -The following conceptual diagram shows an -``m.room.message`` event being sent to the room ``!qporfwt:matrix.org``:: - - { @alice:matrix.org } { @bob:domain.com } - | ^ - | | - [HTTP POST] [HTTP GET] - Room ID: !qporfwt:matrix.org Room ID: !qporfwt:matrix.org - Event type: m.room.message Event type: m.room.message - Content: { JSON object } Content: { JSON object } - | | - V | - +------------------+ +------------------+ - | homeserver | | homeserver | - | matrix.org | | domain.com | - +------------------+ +------------------+ - | ^ - | [HTTP PUT] | - | Room ID: !qporfwt:matrix.org | - | Event type: m.room.message | - | Content: { JSON object } | - `-------> Pointer to the preceding message ------` - PKI signature from matrix.org - Transaction-layer metadata - PKI Authorization header - - ................................... - | Shared Data | - | State: | - | Room ID: !qporfwt:matrix.org | - | Servers: matrix.org, domain.com | - | Members: | - | - @alice:matrix.org | - | - @bob:domain.com | - | Messages: | - | - @alice:matrix.org | - | Content: { JSON object } | - |...................................| - -Federation maintains *shared data structures* per-room between multiple home -servers. The data is split into ``message events`` and ``state events``. - -Message events: - These describe transient 'once-off' activity in a room such as an - instant messages, VoIP call setups, file transfers, etc. They generally - describe communication activity. - -State events: - These describe updates to a given piece of persistent information - ('state') related to a room, such as the room's name, topic, membership, - participating servers, etc. State is modelled as a lookup table of key/value - pairs per room, with each key being a tuple of ``state_key`` and ``event type``. - Each state event updates the value of a given key. - -The state of the room at a given point is calculated by considering all events -preceding and including a given event in the graph. Where events describe the -same state, a merge conflict algorithm is applied. The state resolution -algorithm is transitive and does not depend on server state, as it must -consistently select the same event irrespective of the server or the order the -events were received in. Events are signed by the originating server (the -signature includes the parent relations, type, depth and payload hash) and are -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. - - -Room Aliases -++++++++++++ - -Each room can also have multiple "Room Aliases", which look like:: - - #room_alias:domain - -See the `appendices `_ for full details of -the structure of a room alias. - -A room alias "points" to a room ID and is the human-readable label by which -rooms are publicised and discovered. The room ID the alias is pointing to can -be obtained by visiting the domain specified. Note that the mapping from a room -alias to a room ID is not fixed, and may change over time to point to a -different room ID. For this reason, Clients SHOULD resolve the room alias to a -room ID once and then use that ID on subsequent requests. - -When resolving a room alias the server will also respond with a list of servers -that are in the room that can be used to join via. - -:: - - HTTP GET - #matrix:domain.com !aaabaa:matrix.org - | ^ - | | - _______V____________________|____ - | domain.com | - | Mappings: | - | #matrix >> !aaabaa:matrix.org | - | #golf >> !wfeiofh:sport.com | - | #bike >> !4rguxf:matrix.org | - |________________________________| - -Identity -~~~~~~~~ - -Users in Matrix are identified via their Matrix user ID. However, -existing 3rd party ID namespaces can also be used in order to identify Matrix -users. A Matrix "Identity" describes both the user ID and any other existing IDs -from third party namespaces *linked* to their account. -Matrix users can *link* third-party IDs (3PIDs) such as email addresses, social -network accounts and phone numbers to their user ID. Linking 3PIDs creates a -mapping from a 3PID to a user ID. This mapping can then be used by Matrix -users in order to discover the user IDs of their contacts. -In order to ensure that the mapping from 3PID to user ID is genuine, a globally -federated cluster of trusted "Identity Servers" (IS) are used to verify the 3PID -and persist and replicate the mappings. - -Usage of an IS is not required in order for a client application to be part of -the Matrix ecosystem. However, without one clients will not be able to look up -user IDs using 3PIDs. - - -Profiles -~~~~~~~~ - -Users may publish arbitrary key/value data associated with their account - such -as a human readable display name, a profile photo URL, contact information -(email address, phone numbers, website URLs etc). - -.. TODO - Actually specify the different types of data - e.g. what format are display - names allowed to be? - -Private User Data -~~~~~~~~~~~~~~~~~ - -Users may also store arbitrary private key/value data in their account - such as -client preferences, or server configuration settings which lack any other -dedicated API. The API is symmetrical to managing Profile data. - -.. TODO - Would it really be overengineered to use the same API for both profile & - private user data, but with different ACLs? - -License -------- - -The Matrix specification is licensed under the `Apache License, Version 2.0 -`_. diff --git a/specification/modules/cas_login.rst b/specification/modules/cas_login.rst index b651431a..5de98057 100644 --- a/specification/modules/cas_login.rst +++ b/specification/modules/cas_login.rst @@ -98,9 +98,9 @@ check for certain user attributes in the response. Any required attributes should be configured by the server administrator. Once the ticket has been validated, the server MUST map the CAS ``user_id`` -to a valid `Matrix user identifier <../intro.html#user-identifiers>`_. The +to a valid `Matrix user identifier <../index.html#user-identifiers>`_. The guidance in `Mapping from other character sets -<../intro.html#mapping-from-other-character-sets>`_ may be useful. +<../index.html#mapping-from-other-character-sets>`_ may be useful. If the generated user identifier represents a new user, it should be registered as a new user. diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index 1f778bc2..fa461cc2 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -21,12 +21,6 @@ Matrix optionally supports end-to-end encryption, allowing rooms to be created whose conversation contents is not decryptable or interceptable on any of the participating homeservers. -.. WARNING:: - - End to end encryption is being worked on and will be coming soon. This - section is incomplete. You can read more about what's underway at - http://matrix.org/speculator/spec/drafts%2Fe2e/client_server/unstable.html#end-to-end-encryption. - Key Distribution ---------------- Encryption and Authentication in Matrix is based around public-key @@ -159,7 +153,7 @@ It is therefore expected that each client will maintain a list of devices for a number of users (in practice, typically each user with whom we share an encrypted room). Furthermore, it is likely that this list will need to be persisted between invocations of the client application (to preserve device -verification data and to alert Alice if Bob suddently gets a new +verification data and to alert Alice if Bob suddenly gets a new device). Alice's client can maintain a list of Bob's devices via the following @@ -176,9 +170,10 @@ process: flag. #. During its normal processing of responses to |/sync|_, Alice's client - inspects the |device_lists|_ field. If it is tracking the device lists of - any of the listed users, then it marks the device lists for those users - outdated, and initiates another request to |/keys/query|_ for them. + inspects the ``changed`` property of the |device_lists|_ field. If it is + tracking the device lists of any of the listed users, then it marks the + device lists for those users outdated, and initiates another request to + |/keys/query|_ for them. #. Periodically, Alice's client stores the ``next_batch`` field of the result from |/sync|_ in persistent storage. If Alice later restarts her client, it @@ -214,6 +209,18 @@ process: that the first request's results are ignored (possibly by cancelling the request). +.. Note:: + + When Bob and Alice share a room, with Bob tracking Alice's devices, she may leave + the room and then add a new device. Bob will not be notified of this change, + as he doesn't share a room anymore with Alice. When they start sharing a + room again, Bob has an out-of-date list of Alice's devices. In order to address + this issue, Bob's homeserver will add Alice's user ID to the ``changed`` property of + the ``device_lists`` field, thus Bob will update his list of Alice's devices as part + of his normal processing. Note that Bob can also be notified when he stops sharing + any room with Alice by inspecting the ``left`` property of the ``device_lists`` + field, and as a result should remove her from its list of tracked users. + .. |device_lists| replace:: ``device_lists`` .. _`device_lists`: `device_lists_sync`_ @@ -228,10 +235,238 @@ A homeserver should rate-limit the number of one-time keys that a given user or remote server can claim. A homeserver should discard the public part of a one time key once it has given that key to another user. +Device verification +------------------- + +Before Alice sends Bob encrypted data, or trusts data received from him, she +may want to verify that she is actually communicating with him, rather than a +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. + +Device verification may reach one of several conclusions. For example: + +* Alice may "accept" the device. This means that she is satisfied that the + device belongs to Bob. She can then encrypt sensitive material for that + device, and knows that messages received were sent from that device. + +* Alice may "reject" the device. She will do this if she knows or suspects + that Bob does not control that device (or equivalently, does not trust + Bob). She will not send sensitive material to that device, and cannot trust + messages apparently received from it. + +* Alice may choose to skip the device verification process. She is not able + to verify that the device actually belongs to Bob, but has no reason to + suspect otherwise. The encryption protocol continues to protect against + passive eavesdroppers. + +.. NOTE:: + + Once the signing key has been verified, it is then up to the encryption + 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. + +Messaging Algorithms +-------------------- + +Messaging Algorithm Names +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Messaging algorithm names use the extensible naming scheme used throughout this +specification. Algorithm names that start with ``m.`` are reserved for +algorithms defined by this specification. Implementations wanting to experiment +with new algorithms must be uniquely globally namespaced following Java's package +naming conventions. + +Algorithm names should be short and meaningful, and should list the primitives +used by the algorithm so that it is easier to see if the algorithm is using a +broken primitive. + +A name of ``m.olm.v1`` is too short: it gives no information about the primitives +in use, and is difficult to extend for different primitives. However a name of +``m.olm.v1.ecdh-curve25519-hdkfsha256.hmacsha256.hkdfsha256-aes256-cbc-hmac64sha256`` +is too long despite giving a more precise description of the algorithm: it adds +to the data transfer overhead and sacrifices clarity for human readers without +adding any useful extra information. + +``m.olm.v1.curve25519-aes-sha2`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The name ``m.olm.v1.curve25519-aes-sha2`` corresponds to version 1 of the Olm +ratchet, as defined by the `Olm specification`_. This uses: + +* Curve25519 for the initial key agreement. +* HKDF-SHA-256 for ratchet key derivation. +* Curve25519 for the root key ratchet. +* HMAC-SHA-256 for the chain key ratchet. +* HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256 for authenticated encryption. + +Devices that support Olm must include "m.olm.v1.curve25519-aes-sha2" in their +list of supported messaging algorithms, must list a Curve25519 device key, and +must publish Curve25519 one-time keys. + +An event encrypted using Olm has the following format: + +.. code:: json + + { + "type": "m.room.encrypted", + "content": { + "algorithm": "m.olm.v1.curve25519-aes-sha2", + "sender_key": "", + "ciphertext": { + "": { + "type": 0, + "body": "" + } + } + } + } + +``ciphertext`` is a mapping from device Curve25519 key to an encrypted payload +for that device. ``body`` is a Base64-encoded Olm message body. ``type`` is an +integer indicating the type of the message body: 0 for the initial pre-key +message, 1 for ordinary messages. + +Olm sessions will generate messages with a type of 0 until they receive a +message. Once a session has decrypted a message it will produce messages with +a type of 1. + +When a client receives a message with a type of 0 it must first check if it +already has a matching session. If it does then it will use that session to +try to decrypt the message. If there is no existing session then the client +must create a new session and use the new session to decrypt the message. A +client must not persist a session or remove one-time keys used by a session +until it has successfully decrypted a message using that session. + +Messages with type 1 can only be decrypted with an existing session. If there +is no matching session, the client must treat this as an invalid message. + +The plaintext payload is of the form: + +.. code:: json + + { + "type": "", + "content": "", + "sender": "", + "recipient": "", + "recipient_keys": { + "ed25519": "" + }, + "keys": { + "ed25519": "" + } + } + +The type and content of the plaintext message event are given in the payload. + +Other properties are included in order to prevent an attacker from publishing +someone else's curve25519 keys as their own and subsequently claiming to have +sent messages which they didn't. +``sender`` must correspond to the user who sent the event, ``recipient`` to +the local user, and ``recipient_keys`` to the local ed25519 key. + +Clients must confirm that the ``sender_key`` and the ``ed25519`` field value +under the ``keys`` property match the keys returned by |/keys/query|_ for +the given user, and must also verify the signature of the payload. Without +this check, a client cannot be sure that the sender device owns the private +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. + +``m.megolm.v1.aes-sha2`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +The name ``m.megolm.v1.aes-sha2`` corresponds to version 1 of the Megolm +ratchet, as defined by the `Megolm specification`_. This uses: + +* HMAC-SHA-256 for the hash ratchet. +* HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256 for authenticated encryption. +* Ed25519 for message authenticity. + +Devices that support Megolm must support Olm, and include "m.megolm.v1.aes-sha2" in +their list of supported messaging algorithms. + +An event encrypted using Megolm has the following format: + +.. code:: json + + { + "type": "m.room.encrypted", + "content": { + "algorithm": "m.megolm.v1.aes-sha2", + "sender_key": "", + "device_id": "", + "session_id": "", + "ciphertext": "" + } + } + +The encrypted payload can contain any message event. The plaintext is of the form: + +.. code:: json + + { + "type": "", + "content": "", + "room_id": "" + } + +We include the room ID in the payload, because otherwise the homeserver would +be able to change the room a message was sent in. + +Clients must guard against replay attacks by keeping track of the ratchet indices +of Megolm sessions. They should reject messages with a ratchet index that they +have already decrypted. Care should be taken in order to avoid false positives, as a +client may decrypt the same event twice as part of its normal processing. + +As with Olm events, clients must confirm that the ``sender_key`` belongs to the user +who sent the message. The same reasoning applies, but the sender ed25519 key has to be +inferred from the ``keys.ed25519`` property of the event which established the Megolm +session. + +In order to enable end-to-end encryption in a room, clients can send a +``m.room.encryption`` state event specifying ``m.megolm.v1.aes-sha2`` as its +``algorithm`` property. + +When creating a Megolm session in a room, clients must share the corresponding session +key using Olm with the intended recipients, so that they can decrypt future messages +encrypted using this session. A ``m.room_key`` event is used to do this. Clients +must also handle ``m.room_key`` events sent by other devices in order to decrypt their +messages. Protocol definitions -------------------- +Events +~~~~~~ + +{{m_room_encryption_event}} + +{{m_room_encrypted_event}} + +{{m_room_key_event}} + +Key management API +~~~~~~~~~~~~~~~~~~ + {{keys_cs_http_api}} @@ -249,6 +484,9 @@ specified). The client is expected to use |/keys/query|_ or |/keys/changes|_ for the equivalent functionality after an initial sync, as documented in `Tracking the device list for a user`_. +It also adds a ``one_time_keys_count`` property. Note the spelling difference +with the ``one_time_key_counts`` property in the |/keys/upload|_ response. + .. todo: generate this from a swagger definition? .. device_lists: { changed: ["@user:server", ... ]}, @@ -258,6 +496,9 @@ Parameter Type Description ============ =========== ===================================================== device_lists DeviceLists Optional. Information on e2e device updates. Note: only present on an incremental sync. +|device_otk| {string: Optional. For each key algorithm, the number of + integer} unclaimed one-time keys currently held on the server + for this device. ============ =========== ===================================================== ``DeviceLists`` @@ -265,10 +506,20 @@ device_lists DeviceLists Optional. Information on e2e device updates. Note: ========= ========= ============================================= Parameter Type Description ========= ========= ============================================= -changed [string] List of users who have updated their device identity keys - since the previous sync response. +changed [string] List of users who have updated their device identity keys, + or who now share an encrypted room with the client since + the previous sync response. +left [string] List of users with whom we do not share any encrypted rooms + anymore since the previous sync response. ========= ========= ============================================= +.. NOTE:: + + For optimal performance, Alice should be added to ``changed`` in Bob's sync only + when she adds a new device, or when Alice and Bob now share a room but didn't + share any room previously. However, for the sake of simpler logic, a server + may add Alice to ``changed`` when Alice and Bob share a new room, even if they + previously already shared a room. Example response: @@ -281,17 +532,27 @@ Example response: "changed": [ "@alice:example.com", ], + "left": [ + "@bob:example.com", + ], }, + "device_one_time_keys_count": { + "curve25519": 10, + "signed_curve25519": 20 + } } .. References .. _ed25519: http://ed25519.cr.yp.to/ .. _curve25519: https://cr.yp.to/ecdh.html +.. _`Olm specification`: http://matrix.org/docs/spec/olm.html +.. _`Megolm specification`: http://matrix.org/docs/spec/megolm.html .. _`Signing JSON`: ../appendices.html#signing-json .. |m.olm.v1.curve25519-aes-sha2| replace:: ``m.olm.v1.curve25519-aes-sha2`` +.. |device_otk| replace:: device_one_time_keys_count .. |/keys/upload| replace:: ``/keys/upload`` .. _/keys/upload: #post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-upload diff --git a/specification/modules/guest_access.rst b/specification/modules/guest_access.rst index 4e04aa0d..d579da83 100644 --- a/specification/modules/guest_access.rst +++ b/specification/modules/guest_access.rst @@ -50,6 +50,8 @@ The following API endpoints are allowed to be accessed by guest accounts for retrieving events: * `GET /rooms/:room_id/state <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state>`_ +* `GET /rooms/:room_id/context/:event_id <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-context-eventid>`_ +* `GET /rooms/:room_id/event/:event_id <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-event-eventid>`_ * `GET /rooms/:room_id/state/:event_type/:state_key <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state-eventtype-statekey>`_ * `GET /rooms/:room_id/messages <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages>`_ * `GET /rooms/:room_id/initialSync <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync>`_ diff --git a/specification/modules/openid.rst b/specification/modules/openid.rst new file mode 100644 index 00000000..63406719 --- /dev/null +++ b/specification/modules/openid.rst @@ -0,0 +1,24 @@ +.. Copyright 2018 New Vector Ltd. +.. +.. Licensed under the Apache License, Version 2.0 (the "License"); +.. you may not use this file except in compliance with the License. +.. You may obtain a copy of the License at +.. +.. http://www.apache.org/licenses/LICENSE-2.0 +.. +.. Unless required by applicable law or agreed to in writing, software +.. distributed under the License is distributed on an "AS IS" BASIS, +.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +.. See the License for the specific language governing permissions and +.. limitations under the License. + +OpenID +====== + +.. _module:openid: + +This module allows users to verify their identity with a third party service. The +third party service does need to be matrix-aware in that it will need to know to +resolve matrix homeservers to exchange the user's token for identity information. + +{{openid_cs_http_api}} diff --git a/specification/modules/push.rst b/specification/modules/push.rst index 9bb65b96..e9ee8c90 100644 --- a/specification/modules/push.rst +++ b/specification/modules/push.rst @@ -622,3 +622,5 @@ shouldn't be sent in the push itself where possible. Instead, Push Gateways should send a "sync" command to instruct the client to get new events from the homeserver directly. + +.. _`Push Gateway Specification`: ../push_gateway/unstable.html diff --git a/specification/modules/tags.rst b/specification/modules/tags.rst index 25e48ab3..f965c2e8 100644 --- a/specification/modules/tags.rst +++ b/specification/modules/tags.rst @@ -1,4 +1,5 @@ .. Copyright 2016 OpenMarket Ltd +.. Copyright 2018 New Vector Ltd .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -17,22 +18,19 @@ Room Tagging .. _module:tagging: -Users can add tags to rooms. Tags are short strings used to label rooms, e.g. -"work", "family". A room may have multiple tags. Tags are only visible to the -user that set them but are shared across all their devices. +Users can add tags to rooms. Tags are namespaced strings used to label rooms. +A room may have multiple tags. Tags are only visible to the user that set them +but are shared across all their devices. Events ------ The tags on a room are received as single ``m.tag`` event in the -``account_data`` section of a room in a ``/sync``. +``account_data`` section of a room. The content of the ``m.tag`` event is a +``tags`` key whose value is an object mapping the name of each tag to another +object. -The ``m.tag`` can also be received in a ``/events`` response or in the -``account_data`` section of a room in ``/initialSync``. ``m.tag`` -events appearing in ``/events`` will have a ``room_id`` with the room -the tags are for. - -Each tag has an associated JSON object with information about the tag, e.g how +The JSON object associated with each tag gives information about the tag, e.g how to order the rooms with a given tag. Ordering information is given under the ``order`` key as a number between 0 and @@ -43,25 +41,27 @@ after the rooms with that tag that have an ``order`` key. The name of a tag MUST not exceed 255 bytes. -The name of a tag should be human readable. When displaying tags for a room a -client should display this human readable name. When adding a tag for a room -a client may offer a list to choose from that includes all the tags that the -user has previously set on any of their rooms. +The tag namespace is defined as follows: -Two special names are listed in the specification: - -* ``m.favourite`` -* ``m.lowpriority`` - -{{m_tag_event}} - -Tags namespaces are defined in the following way, depending on how the client are expected to interpret them: - -* The namespace ``m.*`` is reserved for tags defined in the current specification -* The namespace ``u.*`` is reserved for user-defined tags, and the client should not try to interpret as anything other than an utf8 string +* The namespace ``m.*`` is reserved for tags defined in the Matrix specification. Clients must ignore + any tags in this namespace they don't understand. +* The namespace ``u.*`` is reserved for user-defined tags. The portion of the string after the ``u.`` + is defined to be the display name of this tag. No other semantics should be inferred from tags in + this namespace. * A client or app willing to use special tags for advanced functionnality should namespace them similarly to state keys: ``tld.name.*`` * Any tag in the ``tld.name.*`` form but not matching the namespace of the current client should be ignored -* Any tag not matching the previous rules should be interpreted as an user tag from the ``u.*`` namespace +* Any tag not matching the above rules should be interpreted as a user tag from the ``u.*`` namespace, as if + the name had already had ``u.`` stripped from the start (ie. the name of the tag is used as the + display name directly). These non-namespaced tags are supported for historical reasons. New tags should use + one of the defined namespaces above. + +Two special names are listed in the specification: +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_tag_event}} Client Behaviour ---------------- diff --git a/specification/modules/third_party_networks.rst b/specification/modules/third_party_networks.rst new file mode 100644 index 00000000..cd4ce414 --- /dev/null +++ b/specification/modules/third_party_networks.rst @@ -0,0 +1,20 @@ +Third Party Networks +==================== + +.. _module:third-party-networks: + +Application services can provide access to third party networks via bridging. +This allows Matrix users to communicate with users on other communication +platforms, with messages ferried back and forth by the application service. A +single application service may bridge multiple third party networks, and many +individual locations within those networks. A single third party network +location may be bridged to multiple Matrix rooms. + +Third Party Lookups +------------------- + +A client may wish to provide a rich interface for joining third party +locations and connecting with third party users. Information necessary for +such an interface is provided by third party lookups. + +{{third_party_lookup_cs_http_api}} \ No newline at end of file diff --git a/specification/proposals.rst b/specification/proposals.rst index 6c41a0ac..371850ab 100644 --- a/specification/proposals.rst +++ b/specification/proposals.rst @@ -1,681 +1,6 @@ Tables of Tracked Proposals --------------------------- -proposal-wip -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This file is autogenerated by a jenkins build process -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC455 `_ - - Do we want to specify a matrix:// URI scheme for rooms? (SPEC-5) - - 2014-09-15 - - 2018-05-15 - - `455-1 `_ - - `@KitsuneRal`_ - - None - - - * - `MSC586 `_ - - Federation API for canonicalising MXIDs - - 2014-10-27 - - 2018-05-15 - - `586-1 `_ - - `@ara4n`_ - - None - - - * - `MSC489 `_ - - Extensible Profiles. (SPEC-93) - - 2015-01-19 - - 2018-05-15 - - `489-1 `_ - - `@erikjohnston`_ - - None - - - * - `MSC1196 `_ - - Matrix Escape Hatch (Versioned Rooms) - - 2015-10-22 - - 2018-05-15 - - `1196-1 `_ - - `@leonerd`_ - - None - - - * - `MSC1148 `_ - - Support for websockets - - 2015-11-16 - - 2018-06-04 - - `1148-1 `_, `1148-2 `_ - - `@richvdh`_, `@krombel`_ - - None - - - * - `MSC1238 `_ - - Push to Talk - - 2016-04-13 - - 2018-05-15 - - `1238-1 `_ - - `@aviraldg`_ - - None - - `PR#310`_ - * - `MSC1198 `_ - - Threading API - - 2016-05-23 - - 2018-05-15 - - `1198-1 `_ - - `@Half-Shot`_ - - None - - - * - `MSC1207 `_ - - Publishing Room Lists for 3rd party networks - - 2016-10-21 - - 2018-05-31 - - `1207-1 `_ - - `@erikjohnston`_ - - None - - - * - `MSC441 `_ - - Support for Reactions / Aggregations - - 2016-12-25 - - 2018-05-15 - - `441-1 `_ - - `@pik`_ - - `@ara4n`_ - - - * - `MSC1237 `_ - - Improving m.location with GeoJSON and accuracy - - 2017-05-19 - - 2018-05-15 - - `1237-1 `_ - - `@Half-Shot`_ - - None - - `PR#919`_ - * - `MSC971 `_ - - Add groups stuff to spec - - 2017-08-10 - - 2018-05-20 - - `971-1 `_, `971-2 `_, `971-3 `_ - - `@erikjohnston`_ - - None - - - * - `MSC1215 `_ - - Groups as Rooms - - 2017-10-17 - - 2018-05-15 - - `1215-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1217 `_ - - Visibility of groups to group members and their non-members - - 2017-10-30 - - 2018-05-15 - - `1217-1 `_ - - `@lampholder`_ - - None - - - * - `MSC1218 `_ - - Bridging group membership with 3rd party group sources - - 2017-11-15 - - 2018-05-15 - - `1218-1 `_ - - `@lukebarnard1`_ - - None - - - * - `MSC1219 `_ - - Proposal for storing megolm keys serverside - - 2017-11-23 - - 2018-05-15 - - `1219-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1221 `_ - - Improving Presence - - 2017-12-26 - - 2018-05-24 - - `1221-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1222 `_ - - Pushing updates about Groups (Communities) to clients - - 2018-01-02 - - 2018-05-24 - - `1222-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1225 `_ - - Extensible event types & fallback in Matrix - - 2018-02-18 - - 2018-05-15 - - `1225-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1227 `_ - - Proposal for lazy-loading room members to improve initial sync speed and client RAM usage - - 2018-03-05 - - 2018-05-28 - - `1227-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1206 `_ - - Proposal for improved bot support - - 2018-03-14 - - 2018-05-15 - - `1206-1 `_ - - `@turt2live`_ - - None - - - * - `MSC1228 `_ - - Removing MXIDs from events - - 2018-04-19 - - 2018-05-15 - - `1228-1 `_ - - `@richvdh`_ - - None - - - * - `MSC1231 `_ - - Handling Consent for Privacy Policy - - 2018-05-02 - - 2018-05-15 - - `1231-1 `_ - - `@neilisfragile`_ - - None - - - * - `MSC1267 `_ - - Interactive Key Verification - - 2018-05-28 - - 2018-05-28 - - `1267-1 `_ - - `@uhoreg`_ - - None - - - * - `MSC1280 `_ - - Mechanisms for communicating erasure requests to bots and federated homeservers - - 2018-06-05 - - 2018-06-05 - - `1280-1 `_ - - `@richvdh`_ - - None - - - - - -proposal-ready-for-review -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC1232 `_ - - Media limits API - - 2018-05-04 - - 2018-06-05 - - `1232-1 `_ - - `@Half-Shot`_ - - None - - `PR#1189`_ - * - `MSC1256 `_ - - Custom emoji and sticker packs in matrix - - 2018-05-23 - - 2018-05-24 - - `1256-1 `_ - - `@turt2live`_ - - None - - - * - `MSC1270 `_ - - Add /_matrix/media/v1/resolve_url to Client-Server API: download and preview urls in the clients despite CORS - - 2018-05-31 - - 2018-06-05 - - `1270-1 `_ - - `@oivoodoo`_ - - None - - - - - -proposal-in-review -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC433 `_ - - Support for discovering API endpoints via .well-known URIs (SPEC-121) - - 2015-03-08 - - 2018-05-15 - - `433-1 `_, `433-2 `_ - - `@maxidor`_, `others`_ - - `@uhoreg`_ - - - * - `MSC1194 `_ - - A way for HSes to remove bindings from ISes - - 2018-05-09 - - 2018-05-15 - - `1194-1 `_ - - `@dbkr`_ - - None - - - - - -proposal-passed-review -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC1226 `_ - - State Reset mitigation proposal - - 2018-02-20 - - 2018-05-15 - - `1226-1 `_ - - `@richvdh`_ - - None - - - * - `MSC1229 `_ - - Mitigating abuse of the event depth parameter over federation - - 2018-04-30 - - 2018-05-15 - - `1229-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1236 `_ - - Matrix Widget API v2 - - 2018-05-13 - - 2018-05-15 - - `1236-1 `_ - - `@rxl881`_ - - None - - - - - -spec-pr-missing -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC1200 `_ - - Configuration of E2E encryption in a room - - 2016-06-16 - - 2018-05-31 - - `1200-1 `_ - - `@richvdh`_ - - None - - - * - `MSC1201 `_ - - Device Management API - - 2016-07-14 - - 2018-05-15 - - `1201-1 `_ - - `@richvdh`_ - - None - - - * - `MSC1203 `_ - - 3rd Party Entity lookup API - - 2016-07-21 - - 2018-05-15 - - `1203-1 `_ - - `@leonerd`_ - - None - - - * - `MSC1205 `_ - - Proposal for multi-device deletion API - - 2016-10-12 - - 2018-05-15 - - `1205-1 `_ - - `@richvdh`_ - - None - - - * - `MSC1208 `_ - - Encrypted attachment format - - 2016-10-26 - - 2018-05-15 - - `1208-1 `_ - - `@NegativeMjark`_ - - None - - - * - `MSC739 `_ - - Reporting inappropriate content in Matrix - - 2016-11-21 - - 2018-05-31 - - `739-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1211 `_ - - Megolm session export format - - 2017-01-03 - - 2018-05-15 - - `1211-1 `_ - - `@richvdh`_ - - None - - - * - `MSC1212 `_ - - Device List Update Stream - - 2017-01-18 - - 2018-05-15 - - `1212-1 `_ - - `@richvdh`_ - - None - - - * - `MSC829 `_ - - Need to spec msisdn login API - - 2017-03-08 - - 2018-05-15 - - `829-1 `_ - - `@dbkr`_ - - None - - - * - `MSC855 `_ - - spec m.login.msisdn UI auth type - - 2017-03-24 - - 2018-05-15 - - `855-1 `_ - - `@dbkr`_ - - None - - - * - `MSC910 `_ - - Add new Read Marker API to docs - - 2017-05-08 - - 2018-05-15 - - - - `@lukebarnard1`_ - - None - - - * - `MSC1067 `_ - - Spec @mentions - - 2017-07-14 - - 2018-05-15 - - `1067-1 `_ - - `@lukebarnard1`_ - - None - - - * - `MSC1214 `_ - - Related Groups (i.e. flair) - - 2017-10-03 - - 2018-05-15 - - `1214-1 `_ - - `@lukebarnard1`_ - - None - - - * - `MSC1033 `_ - - Doc @room notifications - - 2017-10-23 - - 2018-05-31 - - - - `@dbkr`_ - - None - - - * - `MSC1183 `_ - - Document key-share requests - - 2018-04-30 - - 2018-05-31 - - `1183-1 `_ - - `@richvdh`_ - - None - - - * - `MSC1230 `_ - - Temporary mitigation for depth parameter abuse - - 2018-05-01 - - 2018-05-15 - - `1230-1 `_ - - `@ara4n`_ - - None - - - * - `MSC1234 `_ - - Rich Replies format - - 2018-05-12 - - 2018-05-18 - - `1234-1 `_ - - `@t3chguy`_ - - None - - - - - -merged -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC1197 `_ - - Ignoring Users - - 2016-05-03 - - 2018-05-18 - - `1197-1 `_ - - `@erikjohnston`_ - - None - - `PR#1142`_ - * - `MSC1199 `_ - - Notifications API - - 2016-05-23 - - 2018-05-15 - - `1199-1 `_ - - `@dbkr`_ - - None - - - * - `MSC1204 `_ - - Access Token Semantics (refresh and macaroons) - aka Auth Sept 2016 Edition - - 2016-09-29 - - 2018-05-15 - - `1204-1 `_ - - `@richvdh`_ - - None - - - * - `MSC953 `_ - - Add /user_directory/search API - - 2017-05-31 - - 2018-05-10 - - `953-1 `_ - - `@erikjohnston`_ - - None - - - * - `MSC1233 `_ - - A proposal for organising spec proposals - - 2018-05-10 - - 2018-05-18 - - `1233-1 `_ - - `@ara4n`_ - - None - - `PR#1240`_ - - - -abandoned -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC531 `_ - - Homeservers as OAuth authorization endpoints (resource owners) (SPEC-206) - - 2015-07-25 - - 2018-05-15 - - `531-1 `_ - - `@Kegsay`_ - - None - - - * - `MSC1202 `_ - - Profile Personae - - 2016-07-15 - - 2018-05-15 - - `1202-1 `_ - - `@erikjohnston`_ - - None - - - * - `MSC1209 `_ - - Server Side Profile API - - 2016-11-01 - - 2018-05-15 - - `1209-1 `_ - - `@erikjohnston`_ - - None - - - * - `MSC1213 `_ - - Set defaults for m.federate - - 2017-04-10 - - 2018-05-18 - - `1213-1 `_ - - `@psaavedra`_ - - None - - - - - -obsolete -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. list-table:: - :header-rows: 1 - :widths: auto - :stub-columns: 1 - - * - MSC - - Proposal Title - - Creation Date - - Update Date - - Documentation - - Author - - Shepherd - - PRs - * - `MSC1223 `_ - - Replies event format - - 2018-02-01 - - 2018-05-15 - - `1223-1 `_ - - `@t3chguy`_ - - None - - - * - `MSC1224 `_ - - Replies - next steps - - 2018-02-03 - - 2018-05-15 - - `1224-1 `_ - - `@t3chguy`_ - - None - - - * - `MSC1235 `_ - - Proposal for Calendar Events - - 2018-02-06 - - 2018-05-15 - - `1235-1 `_ - - `@Half-Shot`_ - - None - - - * - `MSC1220 `_ - - Rich quoting proposal - - 2018-05-10 - - 2018-05-15 - - `1220-1 `_ - - `@t3chguy`_ - - None - - - - - - - -.. _@rxl881: https://github.com/rxl881 -.. _@turt2live: https://github.com/turt2live -.. _@erikjohnston: https://github.com/erikjohnston -.. _@t3chguy: https://github.com/t3chguy -.. _@Kegsay: https://github.com/Kegsay -.. _@KitsuneRal: https://github.com/KitsuneRal -.. _@leonerd: https://github.com/leonerd -.. _@psaavedra: https://github.com/psaavedra -.. _@ara4n: https://github.com/ara4n -.. _@krombel: https://github.com/krombel -.. _@maxidor: https://github.com/maxidor -.. _@uhoreg: https://github.com/uhoreg -.. _@pik: https://github.com/pik -.. _@neilisfragile: https://github.com/neilisfragile -.. _@lukebarnard1: https://github.com/lukebarnard1 -.. _others: https://github.com/thers -.. _@Half-Shot: https://github.com/Half-Shot -.. _@aviraldg: https://github.com/aviraldg -.. _@oivoodoo: https://github.com/oivoodoo -.. _@richvdh: https://github.com/richvdh -.. _@NegativeMjark: https://github.com/NegativeMjark -.. _@lampholder: https://github.com/lampholder -.. _@dbkr: https://github.com/dbkr -.. _PR#310: https://github.com/matrix-org/matrix-doc/pull/310 -.. _PR#1142: https://github.com/matrix-org/matrix-doc/pull/1142 -.. _PR#1240: https://github.com/matrix-org/matrix-doc/pull/1240 -.. _PR#1189: https://github.com/matrix-org/matrix-doc/pull/1189 -.. _PR#919: https://github.com/matrix-org/matrix-doc/pull/919 \ No newline at end of file +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 cfa59b8f..cc4d5d22 100644 --- a/specification/proposals_intro.rst +++ b/specification/proposals_intro.rst @@ -30,9 +30,10 @@ The process for submitting a Matrix Spec Change (MSC) Proposal is as follows: `_ is available. -- Make a new issue at https://github.com/matrix-org/matrix-doc/issues (or - modify an existing one), whose description should list the metadata as per - below. +- Make a new issue at https://github.com/matrix-org/matrix-doc/issues, whose + description should list the metadata as per below. Use the github search + function to attempt to locate any related github issues, and link any that + are found in the body of the new issue. - Gather feedback as widely as possible from the community and core team on the proposal. diff --git a/specification/push_gateway.rst b/specification/push_gateway.rst index 29a41bf7..e4a9d6ea 100644 --- a/specification/push_gateway.rst +++ b/specification/push_gateway.rst @@ -67,4 +67,9 @@ This describes the format used by "HTTP" pushers to send notifications of events to Push Gateways. If the endpoint returns an HTTP error code, the homeserver SHOULD retry for a reasonable amount of time using exponential backoff. +When pushing notifications for events, the hoemserver is expected to include all of +the event-related fields in the ``/notify`` request. When the homeserver is performing +a push where the ``format`` is ``"event_id_only"``, only the ``event_id``, ``room_id``, +``counts``, and ``devices`` are required to be populated. + {{push_notifier_push_http_api}} diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index 9ad04d10..e7498d98 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -1,5 +1,6 @@ .. Copyright 2016 OpenMarket Ltd .. Copyright 2017 New Vector Ltd +.. Copyright 2018 New Vector Ltd .. .. Licensed under the Apache License, Version 2.0 (the "License"); .. you may not use this file except in compliance with the License. @@ -16,16 +17,19 @@ Federation API ============== +.. WARNING:: + This API is unstable and will change without warning or discussion while + we work towards a r0 release (scheduled for August 2018). + Matrix homeservers use the Federation APIs (also known as server-server APIs) to communicate with each other. Homeservers use these APIs to push messages to -each other in real-time, to -historic messages from each other, and to +each other in real-time, to retrieve historic messages from each other, and to query profile and presence information about users on each other's servers. -The APIs are implemented using HTTPS GETs and PUTs between each of the -servers. These HTTPS requests are strongly authenticated using public key -signatures at the TLS transport layer and using public key signatures in -HTTP Authorization headers at the HTTP layer. +The APIs are implemented using HTTPS requests between each of the servers. +These HTTPS requests are strongly authenticated using public key signatures +at the TLS transport layer and using public key signatures in HTTP +Authorization headers at the HTTP layer. There are three main kinds of communication that occur between homeservers: @@ -73,26 +77,34 @@ Server Discovery Resolving Server Names ~~~~~~~~~~~~~~~~~~~~~~ -Each matrix homeserver is identified by a server name consisting of a DNS name +Each matrix homeserver is identified by a server name consisting of a hostname and an optional TLS port. .. code:: - server_name = dns_name [ ":" tls_port] - dns_name = + server_name = hostname [ ":" tls_port] tls_port = *DIGIT .. ** If the port is present then the server is discovered by looking up an AAAA or -A record for the DNS name and connecting to the specified TLS port. If the port +A record for the hostname and connecting to the specified TLS port. If the port is absent then the server is discovered by looking up a ``_matrix._tcp`` SRV -record for the DNS name. If this record does not exist then the server is -discovered by looking up an AAAA or A record on the DNS name and taking the +record for the hostname. If this record does not exist then the server is +discovered by looking up an AAAA or A record on the hostname and taking the default fallback port number of 8448. Homeservers may use SRV records to load balance requests between multiple TLS endpoints or to failover to another endpoint if an endpoint fails. +If the DNS name is a literal IP address, the port specified or the fallback +port should be used. + +When making requests to servers, use the DNS name of the target server in the +``Host`` header, regardless of the host given in the SRV record. For example, +if making a request to ``example.org``, and the SRV record resolves to ``matrix. +example.org``, the ``Host`` header in the request should be ``example.org``. The +port number for target server should not appear in the ``Host`` header. + Server implementation ~~~~~~~~~~~~~~~~~~~~~~ @@ -101,15 +113,17 @@ Server implementation Retrieving Server Keys ~~~~~~~~~~~~~~~~~~~~~~ -Version 2 -+++++++++ +.. NOTE:: + There was once a "version 1" of the key exchange. It has been removed from the + specification due to lack of significance. It may be reviewed `here + `_. -Each homeserver publishes its public keys under ``/_matrix/key/v2/server/``. -Homeservers query for keys by either getting ``/_matrix/key/v2/server/`` +Each homeserver publishes its public keys under ``/_matrix/key/v2/server/{keyId}``. +Homeservers query for keys by either getting ``/_matrix/key/v2/server/{keyId}`` directly or by querying an intermediate notary server using a -``/_matrix/key/v2/query`` API. Intermediate notary servers query the -``/_matrix/key/v2/server/`` API on behalf of another server and sign the -response with their own key. A server may query multiple notary servers to +``/_matrix/key/v2/query/{serverName}/{keyId}`` API. Intermediate notary servers +query the ``/_matrix/key/v2/server/{keyId}`` API on behalf of another server and +sign the response with their own key. A server may query multiple notary servers to ensure that they all report the same public keys. This approach is borrowed from the `Perspectives Project`_, but modified to @@ -121,185 +135,124 @@ server by querying other servers. .. _Perspectives Project: https://web.archive.org/web/20170702024706/https://perspectives-project.org/ Publishing Keys -^^^^^^^^^^^^^^^ ++++++++++++++++ Homeservers publish the allowed TLS fingerprints and signing keys in a JSON object at ``/_matrix/key/v2/server/{key_id}``. The response contains a list of ``verify_keys`` that are valid for signing federation requests made by the -server and for signing events. It contains a list of ``old_verify_keys`` -which are only valid for signing events. Finally the response contains a list -of TLS certificate fingerprints to validate any connection made to the server. +homeserver and for signing events. It contains a list of ``old_verify_keys`` which +are only valid for signing events. Finally the response contains a list of TLS +certificate fingerprints to validate any connection made to the homeserver. -A server may have multiple keys active at a given time. A server may have any -number of old keys. It is recommended that servers return a single JSON -response listing all of its keys whenever any ``key_id`` is requested to reduce -the number of round trips needed to discover the relevant keys for a server. -However a server may return a different responses for a different ``key_id``. +{{keys_server_ss_http_api}} -The ``tls_certificates`` contain a list of hashes of the X.509 TLS certificates -currently used by the server. The list must include SHA-256 hashes for every -certificate currently in use by the server. These fingerprints are valid until -the millisecond POSIX timestamp in ``valid_until_ts``. - -The ``verify_keys`` can be used to sign requests and events made by the server -until the millisecond POSIX timestamp in ``valid_until_ts``. If a homeserver -receives an event with a ``origin_server_ts`` after the ``valid_until_ts`` then -it should request that ``key_id`` for the originating server to check whether -the key has expired. - -The ``old_verify_keys`` can be used to sign events with an ``origin_server_ts`` -before the ``expired_ts``. The ``expired_ts`` is a millisecond POSIX timestamp -of when the originating server stopped using that key. - -Intermediate notary servers should cache a response for half of its remaining -life time to avoid serving a stale response. Originating servers should avoid -returning responses that expire in less than an hour to avoid repeated requests -for an about to expire certificate. Requesting servers should limit how -frequently they query for certificates to avoid flooding a server with requests. - -If a server goes offline intermediate notary servers should continue to return -the last response they received from that server so that the signatures of old -events sent by that server can still be checked. - -==================== =================== ====================================== - Key Type Description -==================== =================== ====================================== -``server_name`` String DNS name of the homeserver. -``verify_keys`` Object Public keys of the homeserver for - verifying digital signatures. -``old_verify_keys`` Object The public keys that the server used - to use and when it stopped using them. -``signatures`` Object Digital signatures for this object - signed using the ``verify_keys``. -``tls_fingerprints`` Array of Objects Hashes of X.509 TLS certificates used - by this this server encoded as `Unpadded Base64`_. -``valid_until_ts`` Integer POSIX timestamp when the list of valid - keys should be refreshed. -==================== =================== ====================================== - - -.. code:: json - - { - "old_verify_keys": { - "ed25519:auto1": { - "expired_ts": 922834800000, - "key": "Base+64+Encoded+Old+Verify+Key" - } - }, - "server_name": "example.org", - "signatures": { - "example.org": { - "ed25519:auto2": "Base+64+Encoded+Signature" - } - }, - "tls_fingerprints": [ - { - "sha256": "Base+64+Encoded+SHA-256-Fingerprint" - } - ], - "valid_until_ts": 1052262000000, - "verify_keys": { - "ed25519:auto2": { - "key": "Base+64+Encoded+Signature+Verification+Key" - } - } - } Querying Keys Through Another Server -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +++++++++++++++++++++++++++++++++++++ -Servers may offer a query API ``_matrix/key/v2/query/`` for getting the keys -for another server. This API can be used to GET at list of JSON objects for a -given server or to POST a bulk query for a number of keys from a number of -servers. Either way the response is a list of JSON objects containing the -JSON published by the server under ``_matrix/key/v2/server/`` signed by -both the originating server and by this server. +Servers may query another server's keys through a notary server. The notary +server may be another homeserver. The notary server will retrieve keys from +the queried servers through use of the ``/_matrix/key/v2/server/{keyId}`` +API. The notary server will additionally sign the response from the queried +server before returning the results. -The ``minimum_valid_until_ts`` is a millisecond POSIX timestamp indicating -when the returned certificate will need to be valid until to be useful to the -requesting server. This can be set using the maximum ``origin_server_ts`` of -an batch of events that a requesting server is trying to validate. This allows -an intermediate notary server to give a prompt cached response even if the -originating server is offline. +Notary servers can return keys for servers that are offline or having issues +serving their own keys by using cached responses. Keys can be queried from +multiple servers to mitigate against DNS spoofing. -This API can return keys for servers that are offline be using cached responses -taken from when the server was online. Keys can be queried from multiple -servers to mitigate against DNS spoofing. +{{keys_query_ss_http_api}} -Requests: +Authentication +-------------- + +Request Authentication +~~~~~~~~~~~~~~~~~~~~~~ + +Every HTTP request made by a homeserver is authenticated using public key +digital signatures. The request method, target and body are signed by wrapping +them in a JSON object and signing it using the JSON signing algorithm. The +resulting signatures are added as an Authorization header with an auth scheme +of ``X-Matrix``. Note that the target field should include the full path +starting with ``/_matrix/...``, including the ``?`` and any query parameters if +present, but should not include the leading ``https:``, nor the destination +server's hostname. + +Step 1 sign JSON: .. code:: - GET /_matrix/key/v2/query/${server_name}/${key_id}/?minimum_valid_until_ts=${minimum_valid_until_ts} HTTP/1.1 - - POST /_matrix/key/v2/query HTTP/1.1 - Content-Type: application/json - { - "server_keys": { - "$server_name": { - "$key_id": { - "minimum_valid_until_ts": $posix_timestamp - } - } - } - } - - -Response: - -.. code:: - - HTTP/1.1 200 OK - Content-Type: application/json - { - "server_keys": [ - # List of responses with same format as /_matrix/key/v2/server - # signed by both the originating server and this server. - ] - } - -Version 1 -+++++++++ -.. WARNING:: - Version 1 of key distribution is obsolete - - -Homeservers publish their TLS certificates and signing keys in a JSON object -at ``/_matrix/key/v1``. - -==================== =================== ====================================== - Key Type Description -==================== =================== ====================================== -``server_name`` String DNS name of the homeserver. -``verify_keys`` Object Public keys of the homeserver for - verifying digital signatures. -``signatures`` Object Digital signatures for this object - signed using the ``verify_keys``. -``tls_certificate`` String The X.509 TLS certificate used by this - this server encoded as `Unpadded Base64`_. -==================== =================== ====================================== - -.. code:: json - - { - "server_name": "example.org", + "method": "GET", + "uri": "/target", + "origin": "origin.hs.example.com", + "destination": "destination.hs.example.com", + "content": , "signatures": { - "example.org": { - "ed25519:auto": "Base+64+Encoded+Signature" + "origin.hs.example.com": { + "ed25519:key1": "ABCDEF..." } - }, - "tls_certificate": "Base+64+Encoded+DER+Encoded+X509+TLS+Certificate", - "verify_keys": { - "ed25519:auto": "Base+64+Encoded+Signature+Verification+Key" } - } + } -When fetching the keys for a server the client should check that the TLS -certificate in the JSON matches the TLS server certificate for the connection -and should check that the JSON signatures are correct for the supplied -``verify_keys`` +Step 2 add Authorization header: + +.. code:: + + GET /target HTTP/1.1 + Authorization: X-Matrix origin=origin.example.com,key="ed25519:key1",sig="ABCDEF..." + Content-Type: application/json + + + + +Example python code: + +.. code:: python + + def authorization_headers(origin_name, origin_signing_key, + destination_name, request_method, request_target, + content=None): + request_json = { + "method": request_method, + "uri": request_target, + "origin": origin_name, + "destination": destination_name, + } + + if content_json is not None: + request["content"] = content + + signed_json = sign_json(request_json, origin_name, origin_signing_key) + + authorization_headers = [] + + for key, sig in signed_json["signatures"][origin_name].items(): + authorization_headers.append(bytes( + "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"" % ( + origin_name, key, sig, + ) + )) + + return ("Authorization", authorization_headers) + +Response Authentication +~~~~~~~~~~~~~~~~~~~~~~~ + +Responses are authenticated by the TLS server certificate. A homeserver should +not send a request until it has authenticated the connected server to avoid +leaking messages to eavesdroppers. + +Client TLS Certificates +~~~~~~~~~~~~~~~~~~~~~~~ + +Requests are authenticated at the HTTP layer rather than at the TLS layer +because HTTP services like Matrix are often deployed behind load balancers that +handle the TLS and these load balancers make it difficult to check TLS client +certificates. + +A homeserver may provide a TLS client certificate and the receiving homeserver +may check that the client certificate matches the certificate of the origin +homeserver. Transactions ------------ @@ -309,41 +262,7 @@ of Transaction messages, which are encoded as JSON objects, passed over an HTTP PUT request. A Transaction is meaningful only to the pair of homeservers that exchanged it; they are not globally-meaningful. -Each transaction has: - - An opaque transaction ID, unique among transactions from the same origin. - - A timestamp (UNIX epoch time in milliseconds) generated by its origin - server. - - An origin and destination server name. - - A list of PDUs and EDUs - the actual message payload that the Transaction - carries. - -Transaction Fields -~~~~~~~~~~~~~~~~~~ - -==================== =================== ====================================== - Key Type Description -==================== =================== ====================================== -``origin`` String **Required**. ``server_name`` of homeserver sending - this transaction. -``origin_server_ts`` Integer **Required**. Timestamp in milliseconds on - originating homeserver when this - transaction started. -``pdus`` List of Objects **Required**. List of persistent updates to rooms. -``edus`` List of Objects List of ephemeral messages. May be omitted - if there are no ephemeral messages to - be sent. -==================== =================== ====================================== - -Example: - -.. code:: json - - { - "origin_server_ts":1404835423000, - "origin":"matrix.org", - "pdus":[...], - "edus":[...] - } +{{transactions_ss_http_api}} PDUs ---- @@ -351,75 +270,6 @@ PDUs Each PDU contains a single Room Event which the origin server wants to send to the destination. - -PDU Fields -~~~~~~~~~~ - -==================== ================== ======================================= - Key Type Description -==================== ================== ======================================= -``room_id`` String **Required**. Room identifier. -``sender`` String **Required**. The ID of the user sending - the event. -``origin`` String **Required**. ``server_name`` of the - homeserver that created this event. -``event_id`` String **Required**. Unique identifier for the - event being sent. -``origin_server_ts`` Integer **Required**. Timestamp in milliseconds - on origin homeserver when this event - was created. -``type`` String **Required**. Event type -``state_key`` String Optional. If this key is present, the - event is a state event, and it will - replace previous events with the same - ``type`` and ``state_key`` in the room - state. -``content`` Object **Required**. The content of the event. -``prev_events`` List of (String, **Required**. Event IDs and hashes of - {String: String}) the most recent events in the room that - pairs the homeserver was aware of when it - made this event -``depth`` Integer **Required**. The maximum depth of the - ``prev_events``, plus one -``auth_events`` List of (String, **Required**. Event IDs and hashes for - {String: String}) the "auth events" of this event. - pairs -``hashes`` {String: String} **Required**. Hashes of the PDU, - following the algorithm specified in - `Signing Events`_. -``signatures`` {String: **Required**. Signatures of the redacted - {String: String}} PDU, following the algorithm specified - in `Signing Events`_. -``redacts`` String Optional. For redaction events, the ID - of the event being redacted -``unsigned`` Object Optional. Additional data added by the - origin server but not covered by the - ``signatures``. -==================== ================== ======================================= - -Example: - -.. code:: json - - { - "room_id": "!UcYsUzyxTGDxLBEvLy:example.org", - "sender": "@alice:example.com", - "origin": "example.com", - "event_id": "$a4ecee13e2accdadf56c1025:example.com", - "origin_server_ts": 1404838188000, - "type": "m.room.message", - "prev_events": [ - ["$af232176:example.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}] - ], - "hashes": {"sha256": "thishashcoversallfieldsincasethisisredacted"}, - "signatures": { - "example.com": { - "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus" - } - }, - "content": {...} - } - The ``prev_events`` field of a PDU identifies the "parents" of the event, and thus establishes a partial ordering on events within the room by linking them into a Directed Acyclic Graph (DAG). The sending server should populate this @@ -450,6 +300,8 @@ following subset of the room state: - The current ``m.room.join_rules`` event, if any. - The sender's current ``m.room.member`` event, if any. +{{definition_ss_pdu}} + Authorization of PDUs ~~~~~~~~~~~~~~~~~~~~~ @@ -500,145 +352,139 @@ the state of the room. state of the room. For example, a redacted ``join`` event will still result in the user being considered joined. +The rules are as follows: + 1. If type is ``m.room.create``, allow if and only if it has no previous events - *i.e.* it is the first event in the room. -#. If type is ``m.room.member``: +2. If type is ``m.room.member``: - a. If ``membership`` is ``join``: + a. If ``membership`` is ``join``: - i. If the only previous event is an ``m.room.create`` - and the ``state_key`` is the creator, allow. + i. If the only previous event is an ``m.room.create`` + and the ``state_key`` is the creator, allow. - #. If the ``sender`` does not match ``state_key``, reject. + #. If the ``sender`` does not match ``state_key``, reject. - #. If the user's current membership state is ``invite`` or ``join``, - allow. + #. If the user's current membership state is ``invite`` or ``join``, + allow. - #. If the ``join_rule`` is ``public``, allow. + #. If the ``join_rule`` is ``public``, allow. - #. Otherwise, reject. + #. Otherwise, reject. - #. If ``membership`` is ``invite``: + b. If ``membership`` is ``invite``: - i. If the ``sender``'s current membership state is not ``join``, reject. + i. If the ``sender``'s current membership state is not ``join``, reject. - #. If *target user*'s current membership state is ``join`` or ``ban``, - reject. + #. If *target user*'s current membership state is ``join`` or ``ban``, + reject. - #. If the ``sender``'s power level is greater than or equal to the *invite - level*, allow. + #. If the ``sender``'s power level is greater than or equal to the *invite + level*, allow. - #. Otherwise, reject. + #. Otherwise, reject. - #. If ``membership`` is ``leave``: + c. If ``membership`` is ``leave``: - i. If the ``sender`` matches ``state_key``, allow if and only if that user's - current membership state is ``invite`` or ``join``. + i. If the ``sender`` matches ``state_key``, allow if and only if that user's + current membership state is ``invite`` or ``join``. - #. If the ``sender``'s current membership state is not ``join``, reject. + #. If the ``sender``'s current membership state is not ``join``, reject. - #. If the *target user*'s current membership state is ``ban``, and the - ``sender``'s power level is less than the *ban level*, reject. + #. If the *target user*'s current membership state is ``ban``, and the + ``sender``'s power level is less than the *ban level*, reject. - #. If the ``sender``'s power level is greater than or equal to the *kick - level*, and the *target user*'s power level is less than the - ``sender``'s power level, allow. + #. If the ``sender``'s power level is greater than or equal to the *kick + level*, and the *target user*'s power level is less than the + ``sender``'s power level, allow. - #. Otherwise, reject. + #. Otherwise, reject. - #. If ``membership`` is ``ban``: + d. If ``membership`` is ``ban``: - i. If the ``sender``'s current membership state is not ``join``, reject. + i. If the ``sender``'s current membership state is not ``join``, reject. - #. If the ``sender``'s power level is greater than or equal to the *ban - level*, and the *target user*'s power level is less than the - ``sender``'s power level, allow. + #. If the ``sender``'s power level is greater than or equal to the *ban + level*, and the *target user*'s power level is less than the + ``sender``'s power level, allow. - #. Otherwise, reject. + #. Otherwise, reject. - #. Otherwise, the membership is unknown. Reject. + e. Otherwise, the membership is unknown. Reject. -#. If the ``sender``'s current membership state is not ``join``, reject. +3. If the ``sender``'s current membership state is not ``join``, reject. -#. If the event type's *required power level* is greater than the ``sender``'s power +4. If the event type's *required power level* is greater than the ``sender``'s power level, reject. -#. If type is ``m.room.power_levels``: +5. If type is ``m.room.power_levels``: - a. If there is no previous ``m.room.power_levels`` event in the room, allow. + a. 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: + b. For each of the keys ``users_default``, ``events_default``, + ``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as + each entry being changed under the ``events`` or ``users`` keys: - i. If the current value is higher than the ``sender``'s current power level, - reject. + 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. + #. 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: + c. For each entry being changed under the ``users`` key, other than the + ``sender``'s own entry: - i. If the current value is equal to the ``sender``'s current power level, - reject. + i. If the current value is equal to the ``sender``'s current power level, + reject. - #. Otherwise, allow. + d. Otherwise, allow. -#. If type is ``m.room.redaction``: +6. If type is ``m.room.redaction``: - #. If the ``sender``'s power level is greater than or equal to the *redact - level*, allow. + a. If the ``sender``'s power level is greater than or equal to the *redact + level*, allow. - #. If the ``sender`` of the event being redacted is the same as the - ``sender`` of the ``m.room.redaction``, allow. + #. If the ``sender`` of the event being redacted is the same as the + ``sender`` of the ``m.room.redaction``, allow. - #. Otherwise, reject. + #. Otherwise, reject. -#. Otherwise, allow. +7. Otherwise, allow. .. NOTE:: - Some consequences of these rules: + Some consequences of these rules: - * Unless you are a member of the room, the only permitted operations (apart - from the intial create/join) are: joining a public room; accepting or - rejecting an invitation to a room. + * Unless you are a member of the room, the only permitted operations (apart + from the intial create/join) are: joining a public room; accepting or + rejecting an invitation to a room. - * To unban somebody, you must have power level greater than or equal to both - the kick *and* ban levels, *and* greater than the target user's power - level. + * To unban somebody, you must have power level greater than or equal to both + the kick *and* ban levels, *and* greater than the target user's power + level. .. TODO-spec - I think there is some magic about 3pid invites too. + I think there is some magic about 3pid invites too. + +Retrieving event authorization information +++++++++++++++++++++++++++++++++++++++++++ + +The homeserver may be missing event authorization information, or wish to check +with other servers to ensure it is receiving the correct auth chain. These APIs +give the homeserver an avenue for getting the information it needs. + +{{event_auth_ss_http_api}} EDUs ---- -.. WARNING:: - This section may be misleading or inaccurate. - EDUs, by comparison to PDUs, do not have an ID, a room ID, or a list of -"previous" IDs. The only mandatory fields for these are the type, origin and -destination homeserver names, and the actual nested content. +"previous" IDs. They are intended to be non-persistent data such as user +presence, typing notifications, etc. -======================== ============ ========================================= - Key Type Description -======================== ============ ========================================= -``edu_type`` String The type of the ephemeral message. -``content`` Object Content of the ephemeral message. -======================== ============ ========================================= - -.. code:: json - - { - "edu_type":"m.presence", - "origin":"blue", - "destination":"orange", - "content":{...} - } +{{definition_ss_edu}} Room State Resolution --------------------- @@ -729,102 +575,58 @@ A *conflict* occurs between states where those states have different ``event_ids`` for the same ``(state_type, state_key)``. The events thus affected are said to be *conflicting* events. -Protocol URLs -------------- -.. WARNING:: - This section may be misleading or inaccurate. +Backfilling and retrieving missing events +----------------------------------------- -All these URLs are name-spaced within a prefix of:: +Once a homeserver has joined a room, it receives all the events emitted by +other homeservers in that room, and is thus aware of the entire history of the +room from that moment onwards. Since users in that room are able to request the +history by the ``/messages`` client API endpoint, it's possible that they might +step backwards far enough into history before the homeserver itself was a +member of that room. - /_matrix/federation/v1/... +To cover this case, the federation API provides a server-to-server analog of +the ``/messages`` client API, allowing one homeserver to fetch history from +another. This is the ``/backfill`` API. -For active pushing of messages representing live activity "as it happens":: +To request more history, the requesting homeserver picks another homeserver +that it thinks may have more (most likely this should be a homeserver for +some of the existing users in the room at the earliest point in history it +has currently), and makes a ``/backfill`` request. - PUT .../send// - Body: JSON encoding of a single Transaction - Response: TODO-doc +Similar to backfilling a room's history, a server may not have all the events +in the graph. That server may use the ``/get_missing_events`` API to acquire +the events it is missing. -The transaction_id path argument will override any ID given in the JSON body. -The destination name will be set to that of the receiving server itself. Each -embedded PDU in the transaction body will be processed. +.. TODO-spec + Specify (or remark that it is unspecified) how the server handles divergent + history. DFS? BFS? Anything weirder? +{{backfill_ss_http_api}} -To fetch all the state of a given room:: +Retrieving events +----------------- - GET .../state// - Response: JSON encoding of a single Transaction containing multiple PDUs +In some circumstances, a homeserver may be missing a particular event or information +about the room which cannot be easily determined from backfilling. These APIs provide +homeservers with the option of getting events and the state of the room at a given +point in the timeline. -Retrieves a snapshot of the entire current state of the given room. The -response will contain a single Transaction, inside which will be a list of PDUs -that encode the state. +{{events_ss_http_api}} -To fetch a particular event:: - - GET .../event// - Response: JSON encoding of a partial Transaction containing the event - -Retrieves a single event. The response will contain a partial Transaction, -having just the ``origin``, ``origin_server_ts`` and ``pdus`` fields; the -event will be encoded as the only PDU in the ``pdus`` list. - - -To backfill events on a given room:: - - GET .../backfill// - Query args: v, limit - Response: JSON encoding of a single Transaction containing multiple PDUs - -Retrieves a sliding-window history of previous PDUs that occurred on the given -room. Starting from the PDU ID(s) given in the "v" argument, the PDUs that -preceded it are retrieved, up to a total number given by the "limit" argument. - - -To stream events all the events:: - - GET .../pull/ - Query args: origin, v - Response: JSON encoding of a single Transaction consisting of multiple PDUs - -Retrieves all of the transactions later than any version given by the "v" -arguments. - - -To make a query:: - - GET .../query/ - Query args: as specified by the individual query types - Response: JSON encoding of a response object - -Performs a single query request on the receiving homeserver. The Query Type -part of the path specifies the kind of query being made, and its query -arguments have a meaning specific to that kind of query. The response is a -JSON-encoded object whose meaning also depends on the kind of query. - - -To join a room:: - - GET .../make_join// - Response: JSON encoding of a join proto-event - - PUT .../send_join// - Response: JSON encoding of the state of the room at the time of the event - -Performs the room join handshake. For more information, see "Joining Rooms" -below. - Joining Rooms ------------- -When a new user wishes to join room that the user's homeserver already knows +When a new user wishes to join a room that the user's homeserver already knows about, the homeserver can immediately determine if this is allowable by -inspecting the state of the room, and if it is acceptable, it can generate, -sign, and emit a new ``m.room.member`` state event adding the user into that -room. When the homeserver does not yet know about the room it cannot do this +inspecting the state of the room. If it is acceptable, it can generate, sign, +and emit a new ``m.room.member`` state event adding the user into that room. +When the homeserver does not yet know about the room it cannot do this directly. Instead, it must take a longer multi-stage handshaking process by which it first selects a remote homeserver which is already participating in -that room, and uses it to assist in the joining process. This is the remote +that room, and use it to assist in the joining process. This is the remote join handshake. This handshake involves the homeserver of the new member wishing to join @@ -865,192 +667,69 @@ homeservers, though most in practice will use just two. <---------- join response The first part of the handshake usually involves using the directory server to -request the room ID and join candidates. This is covered in more detail on the -directory server documentation, below. In the case of a new user joining a -room as a result of a received invite, the joining user's homeserver could -optimise this step away by picking the origin server of that invite message as -the join candidate. However, the joining server should be aware that the origin -server of the invite might since have left the room, so should be prepared to -fall back on the regular join flow if this optimisation fails. +request the room ID and join candidates through the |/query/directory|_ +API endpoint. In the case of a new user joining a room as a result of a received +invite, the joining user's homeserver could optimise this step away by picking +the origin server of that invite message as the join candidate. However, the +joining server should be aware that the origin server of the invite might since +have left the room, so should be prepared to fall back on the regular join flow +if this optimisation fails. Once the joining server has the room ID and the join candidates, it then needs to obtain enough information about the room to fill in the required fields of the ``m.room.member`` event. It obtains this by selecting a resident from the -candidate list, and requesting the ``make_join`` endpoint using a ``GET`` -request, specifying the room ID and the user ID of the new member who is -attempting to join. +candidate list, and using the ``GET /make_join`` endpoint. The resident server +will then reply with enough information for the joining server to fill in the +event. -The resident server replies to this request with a JSON-encoded object having a -single key called ``event``; within this is an object whose fields contain some -of the information that the joining server will need. Despite its name, this -object is not a full event; notably it does not need to be hashed or signed by -the resident homeserver. The required fields are: - -==================== ======== ============ - Key Type Description -==================== ======== ============ -``type`` String The value ``m.room.member`` -``auth_events`` List An event-reference list containing the - authorization events that would allow this member - to join -``content`` Object The event content -``depth`` Integer (this field must be present but is ignored; it - may be 0) -``origin`` String The name of the resident homeserver -``origin_server_ts`` Integer A timestamp added by the resident homeserver -``prev_events`` List An event-reference list containing the immediate - predecessor events -``room_id`` String The room ID of the room -``sender`` String The user ID of the joining member -``state_key`` String The user ID of the joining member -==================== ======== ============ - -The ``content`` field itself must be an object, containing: - -============== ====== ============ - Key Type Description -============== ====== ============ -``membership`` String The value ``join`` -============== ====== ============ - -The joining server now has sufficient information to construct the real join -event from these protoevent fields. It copies the values of most of them, -adding (or replacing) the following fields: - -==================== ======= ============ - Key Type Description -==================== ======= ============ -``event_id`` String A new event ID specified by the joining homeserver -``origin`` String The name of the joining homeserver -``origin_server_ts`` Integer A timestamp added by the joining homeserver -==================== ======= ============ - -This will be a true event, so the joining server should apply the event-signing -algorithm to it, resulting in the addition of the ``hashes`` and ``signatures`` -fields. +The joining server is expected to add or replace the ``origin``, ``origin_server_ts``, +and ``event_id`` on the templated event received by the resident server. This +event is then signed by the joining server. To complete the join handshake, the joining server must now submit this new -event to an resident homeserver, by using the ``send_join`` endpoint. This is -invoked using the room ID and the event ID of the new member event. +event to a resident homeserver, by using the ``PUT /send_join`` endpoint. The resident homeserver then accepts this event into the room's event graph, -and responds to the joining server with the full set of state for the newly- -joined room. This is returned as a two-element list, whose first element is the -integer 200, and whose second element is an object which contains the -following keys: +and responds to the joining server with the full set of state for the +newly-joined room. The resident server must also send the event to other servers +participating in the room. -============== ===== ============ - Key Type Description -============== ===== ============ -``auth_chain`` List A list of events giving all of the events in the auth - chains for the join event and the events in ``state``. -``state`` List A complete list of the prevailing state events at the - instant just before accepting the new ``m.room.member`` - event. -============== ===== ============ +{{joins_ss_http_api}} .. TODO-spec - (paul) I don't really understand why the full auth_chain events are given here. What purpose does it serve expanding them out in full, when surely they'll appear in the state anyway? -Backfilling ------------ - -Once a homeserver has joined a room, it receives all the events emitted by -other homeservers in that room, and is thus aware of the entire history of the -room from that moment onwards. Since users in that room are able to request the -history by the ``/messages`` client API endpoint, it's possible that they might -step backwards far enough into history before the homeserver itself was a -member of that room. - -To cover this case, the federation API provides a server-to-server analog of -the ``/messages`` client API, allowing one homeserver to fetch history from -another. This is the ``/backfill`` API. - -To request more history, the requesting homeserver picks another homeserver -that it thinks may have more (most likely this should be a homeserver for some -of the existing users in the room at the earliest point in history it has -currently), and makes a ``/backfill`` request. The parameters of this request -give an event ID that the requesting homeserver wishes to obtain, and a number -specifying how many more events of history before that one to return at most. - -The response to this request is an object with the following keys: - -==================== ======== ============ - Key Type Description -==================== ======== ============ -``pdus`` List A list of events -``origin`` String The name of the resident homeserver -``origin_server_ts`` Integer A timestamp added by the resident homeserver -==================== ======== ============ - -The list of events given in ``pdus`` is returned in reverse chronological -order; having the most recent event first (i.e. the event whose event ID is -that requested by the requestor in the ``v`` parameter). - -.. TODO-spec - Specify (or remark that it is unspecified) how the server handles divergent - history. DFS? BFS? Anything weirder? - Inviting to a room ------------------ -When a user wishes to invite an other user to a local room and the other user -is on a different server, the inviting server will send a request to the invited -server:: +When a user on a given homeserver invites another user on the same homeserver, +the homeserver may sign the membership event itself and skip the process defined +here. However, when a user invites another user on a different homeserver, a request +to that homeserver to have the event signed and verified must be made. - PUT .../invite/{roomId}/{eventId} +{{invites_ss_http_api}} -The required fields in the JSON body are: +Leaving Rooms (Rejecting Invites) +--------------------------------- -==================== ======== ============ - Key Type Description -==================== ======== ============ -``room_id`` String The room ID of the room. Must be the same as the - room ID specified in the path. -``event_id`` String The ID of the event. Must be the same as the event - ID specified in the path. -``type`` String The value ``m.room.member``. -``auth_events`` List An event-reference list containing the IDs of the - authorization events that would allow this member - to be invited in the room. -``content`` Object The content of the event. -``depth`` Integer The depth of the event. -``origin`` String The name of the inviting homeserver. -``origin_server_ts`` Integer A timestamp added by the inviting homeserver. -``prev_events`` List An event-reference list containing the IDs of the - immediate predecessor events. -``sender`` String The Matrix ID of the user who sent the original - `m.room.third_party_invite`. -``state_key`` String The Matrix ID of the invited user. -``signatures`` Object The signature of the event from the origin server. -``unsigned`` Object An object containing the properties that aren't - part of the signature's computation. -==================== ======== ============ +Normally homeservers can send appropriate ``m.room.member`` events to have users +leave the room, or to reject local invites. Remote invites from other homeservers +do not involve the server in the graph and therefore need another approach to +reject the invite. Joining the room and promptly leaving is not recommended as +clients and servers will interpret that as accepting the invite, then leaving the +room rather than rejecting the invite. -Where the ``content`` key contains the content for the ``m.room.member`` event -specified in the `Client-Server API`_. Note that the ``membership`` property of -the content must be ``invite``. +Similar to the `Joining Rooms`_ handshake, the server which wishes to leave the +room starts with sending a ``/make_leave`` request to a resident server. In the +case of rejecting invites, the resident server may be the server which sent the +invite. After receiving a template event from ``/make_leave``, the leaving server +signs the event and replaces the ``event_id`` with it's own. This is then sent to +the resident server via ``/send_leave``. The resident server will then send the +event to other servers in the room. -Upon receiving this request, the invited homeserver will append its signature to -the event and respond to the request with the following JSON body:: - - [ - 200, - "event": {...} - ] - -Where ``event`` contains the event signed by both homeservers, using the same -JSON keys as the initial request on ``/invite/{roomId}/{eventId}``. Note that, -except for the ``signatures`` object (which now contains an additional signature), -all of the event's keys remain the same as in the event initially provided. - -This response format is due to a typo in Synapse, the first implementation of -Matrix's APIs, and is preserved to maintain compatibility. - -Now that the event has been signed by both the inviting homeserver and the -invited homeserver, it can be sent to all of the users in the room. +{{leaving_ss_http_api}} Third-party invites ------------------- @@ -1080,7 +759,7 @@ and public keys the identity server provided as a response to the invite storage request. When a third-party identifier with pending invites gets bound to a Matrix ID, -the identity server will send a ``POST`` request to the ID's homeserver as described +the identity server will send a POST request to the ID's homeserver as described in the `Invitation Storage`_ section of the Identity Service API. The following process applies for each invite sent by the identity server: @@ -1093,38 +772,9 @@ If the invited homeserver is in the room the invite came from, it can auth the event and send it. However, if the invited homeserver isn't in the room the invite came from, it -will need to request the room's homeserver to auth the event:: +will need to request the room's homeserver to auth the event. - PUT .../exchange_third_party_invite/{roomId} - -Where ``roomId`` is the ID of the room the invite is for. - -The required fields in the JSON body are: - -==================== ======= ================================================== - Key Type Description -==================== ======= ================================================== -``type`` String The event type. Must be `m.room.member`. -``room_id`` String The ID of the room the event is for. Must be the - same as the ID specified in the path. -``sender`` String The Matrix ID of the user who sent the original - `m.room.third_party_invite`. -``state_key`` String The Matrix ID of the invited user. -``content`` Object The content of the event. -==================== ======= ================================================== - -Where the ``content`` key contains the content for the ``m.room.member`` event -as described in the `Client-Server API`_. Its ``membership`` key must be -``invite`` and its content must include the ``third_party_invite`` object. - -The inviting homeserver will then be able to authenticate the event. It will send -a fully authenticated event to the invited homeserver as described in the `Inviting -to a room`_ section above. - -Once the invited homeserver responded with the event to which it appended its -signature, the inviting homeserver will respond with ``200 OK`` and an empty body -(``{}``) to the initial request on ``/exchange_third_party_invite/{roomId}`` and -send the now verified ``m.room.member`` invite event to the room's members. +{{third_party_invite_ss_http_api}} Verifying the invite ++++++++++++++++++++ @@ -1144,105 +794,33 @@ It will then use these keys to verify that the ``signed`` object (in the ``third_party_invite`` object from the ``m.room.member`` event's content) was signed by the same identity server. -Since this ``signed`` object can only be delivered once in the ``POST`` request +Since this ``signed`` object can only be delivered once in the POST request emitted by the identity server upon binding between the third-party identifier and the Matrix ID, and contains the invited user's Matrix ID and the token delivered when the invite was stored, this verification will prove that the ``m.room.member`` invite event comes from the user owning the invited third-party identifier. -Authentication --------------- +Public Room Directory +--------------------- -Request Authentication -~~~~~~~~~~~~~~~~~~~~~~ +To compliment the `Client-Server API`_'s room directory, homeservers need a +way to query the public rooms for another server. This can be done by making +a request to the ``/publicRooms`` endpoint for the server the room directory +should be retrieved for. -Every HTTP request made by a homeserver is authenticated using public key -digital signatures. The request method, target and body are signed by wrapping -them in a JSON object and signing it using the JSON signing algorithm. The -resulting signatures are added as an Authorization header with an auth scheme -of X-Matrix. Note that the target field should include the full path starting with -``/_matrix/...``, including the ``?`` and any query parameters if present, but -should not include the leading ``https:``, nor the destination server's -hostname. - -Step 1 sign JSON: - -.. code:: - - { - "method": "GET", - "uri": "/target", - "origin": "origin.hs.example.com", - "destination": "destination.hs.example.com", - "content": , - "signatures": { - "origin.hs.example.com": { - "ed25519:key1": "ABCDEF..." - } - } - } - -Step 2 add Authorization header: - -.. code:: - - GET /target HTTP/1.1 - Authorization: X-Matrix origin=origin.example.com,key="ed25519:key1",sig="ABCDEF..." - Content-Type: application/json - - +{{public_rooms_ss_http_api}} -Example python code: +Typing Notifications +-------------------- -.. code:: python - - def authorization_headers(origin_name, origin_signing_key, - destination_name, request_method, request_target, - content=None): - request_json = { - "method": request_method, - "uri": request_target, - "origin": origin_name, - "destination": destination_name, - } - - if content_json is not None: - request["content"] = content - - signed_json = sign_json(request_json, origin_name, origin_signing_key) - - authorization_headers = [] - - for key, sig in signed_json["signatures"][origin_name].items(): - authorization_headers.append(bytes( - "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"" % ( - origin_name, key, sig, - ) - )) - - return ("Authorization", authorization_headers) - -Response Authentication -~~~~~~~~~~~~~~~~~~~~~~~ - -Responses are authenticated by the TLS server certificate. A homeserver should -not send a request until it has authenticated the connected server to avoid -leaking messages to eavesdroppers. - -Client TLS Certificates -~~~~~~~~~~~~~~~~~~~~~~~ - -Requests are authenticated at the HTTP layer rather than at the TLS layer -because HTTP services like Matrix are often deployed behind load balancers that -handle the TLS and these load balancers make it difficult to check TLS client -certificates. - -A homeserver may provide a TLS client certificate and the receiving homeserver -may check that the client certificate matches the certificate of the origin -homeserver. +When a server's users send typing notifications, those notifications need to +be sent to other servers in the room so their users are aware of the same +state. Receiving servers should verify that the user is in the room, and is +a user belonging to the sending server. +{{definition_ss_event_schemas_m_typing}} Presence -------- @@ -1307,36 +885,42 @@ Rejecting a presence invite:: - Explain the zero-byte presence inference logic See also: docs/client-server/model/presence -Profiles +Receipts -------- -The server API for profiles is based entirely on the following Federation -Queries. There are no additional EDU or PDU types involved, other than the -implicit ``m.presence`` and ``m.room.member`` events (see section below). +Receipts are EDUs used to communicate a marker for a given event. Currently the +only kind of receipt supported is a "read receipt", or where in the event graph +the user has read up to. -Querying profile information:: +Read receipts for events events that a user sent do not need to be sent. It is +implied that by sending the event the user has read up to the event. - Query type: profile +{{definition_ss_event_schemas_m_receipt}} - Arguments: - user_id: the ID of the user whose profile to return - field: (optional) string giving a field name +Querying for information +------------------------ - Returns: JSON object containing the following keys: - displayname: string of free-form text - avatar_url: string containing an HTTP-scheme URL +Queries are a way to retrieve information from a homeserver about a resource, +such as a user or room. The endpoints here are often called in conjunction with +a request from a client on the client-server API in order to complete the call. -If the query contains the optional ``field`` key, it should give the name of a -result field. If such is present, then the result should contain only a field -of that name, with no others present. If not, the result should contain as much -of the user's profile as the homeserver has available and can make public. +There are several types of queries that can be made. The generic endpoint to +represent all queries is described first, followed by the more specific queries +that can be made. -Directory ---------- +{{query_ss_http_api}} -The server API for directory queries is also based on Federation Queries. +OpenID +------ -{{directory_ss_http_api}} +Third party services can exchange an access token previously generated by the +`Client-Server API` for information about a user. This can help verify that a +user is who they say they are without granting full access to the user's account. + +Access tokens generated by the OpenID API are only good for the OpenID API and +nothing else. + +{{openid_ss_http_api}} Send-to-device messaging ------------------------ @@ -1360,7 +944,21 @@ the following EDU:: messages: The messages to send. A map from user ID, to a map from device ID to message body. The device ID may also be *, meaning all known devices - for the user. + for the user + + +Content Repository +------------------ + +Attachments to events (images, files, etc) are uploaded to a homeserver via the +Content Repository described in the `Client-Server API`_. When a server wishes +to serve content originating from a remote server, it needs to ask the remote +server for the media. + +Servers should use the server described in the Matrix Content URI, which has the +format ``mxc://{ServerName}/{MediaID}``. Servers should use the download endpoint +described in the `Client-Server API`_, being sure to use the ``allow_remote`` +parameter (set to ``false``). Signing Events @@ -1492,7 +1090,7 @@ Servers can then transmit the entire event or the event with the non-essential keys removed. If the entire event is present, receiving servers can then check the event by computing the SHA-256 of the event, excluding the ``hash`` object. If the keys have been redacted, then the ``hash`` object is included when -calculating the SHA-256 instead. +calculating the SHA-256 hash instead. New hash functions can be introduced by adding additional keys to the ``hash`` object. Since the ``hash`` object cannot be redacted a server shouldn't allow @@ -1506,9 +1104,12 @@ that are too long. [[TODO(markjh) We might want to allow the server to omit the output of well known hash functions like SHA-256 when none of the keys have been redacted]] +.. |/query/directory| replace:: ``/query/directory`` +.. _/query/directory: #get-matrix-federation-v1-query-directory + .. _`Invitation storage`: ../identity_service/unstable.html#invitation-storage .. _`Identity Service API`: ../identity_service/unstable.html -.. _`Client-Server API`: ../client_server/unstable.html#m-room-member +.. _`Client-Server API`: ../client_server/unstable.html .. _`Inviting to a room`: #inviting-to-a-room .. _`Canonical JSON`: ../appendices.html#canonical-json .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 diff --git a/specification/targets.yaml b/specification/targets.yaml index bc9d94c8..5480bbc5 100644 --- a/specification/targets.yaml +++ b/specification/targets.yaml @@ -2,9 +2,6 @@ targets: index: files: - index.rst - intro: - files: - - intro.rst client_server: files: - client_server_api.rst @@ -68,6 +65,8 @@ groups: # reusable blobs of files when prefixed with 'group:' - modules/ignore_users.rst - modules/stickers.rst - modules/report_content.rst + - modules/third_party_networks.rst + - modules/openid.rst title_styles: ["=", "-", "~", "+", "^", "`", "@", ":"]