diff --git a/.circleci/config.yml b/.circleci/config.yml index 3a0b6032..659380b0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,9 +23,55 @@ buildswaggerui: &buildswaggerui wget https://raw.githubusercontent.com/matrix-org/matrix.org/master/scripts/swagger-ui.patch patch api/client-server/index.html swagger-ui.patch +checkexamples: &checkexamples + name: Check Event Examples + command: | + source /env/bin/activate + cd event-schemas + ./check_examples.py + cd ../api + ./check_examples.py + +genmatrixassets: &genmatrixassets + name: Generate/Verify matrix.org assets + command: | + source /env/bin/activate + ./scripts/generate-matrix-org-assets + +validateapi: &validateapi + name: Validate OpenAPI specifications + command: | + cd api + npm install + node validator.js -s "client-server" + +buildspeculator: &buildspeculator + name: Build Speculator + command: | + cd scripts/speculator + go build -v + +buildcontinuserv: &buildcontinuserv + name: Build Continuserv + command: | + cd scripts/continuserv + go build -v version: 2 jobs: + validate-docs: + docker: + - image: node:alpine + steps: + - checkout + - run: *validateapi + check-docs: + docker: + - image: uhoreg/matrix-doc-build + steps: + - checkout + - run: *checkexamples + - run: *genmatrixassets # We don't actually use the assets, but we do want to make sure they build build-docs: docker: - image: uhoreg/matrix-doc-build @@ -37,7 +83,6 @@ jobs: - 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 @@ -50,6 +95,18 @@ jobs: - 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 + build-dev-scripts: + docker: + - image: golang:1.8 + steps: + - checkout + - run: + name: Install Dependencies + command: | + go get -v github.com/hashicorp/golang-lru + go get -v gopkg.in/fsnotify/fsnotify.v1 + - run: *buildcontinuserv + - run: *buildspeculator workflows: version: 2 @@ -58,6 +115,9 @@ workflows: jobs: - build-docs - build-swagger + - check-docs + - validate-docs + - build-dev-scripts notify: webhooks: diff --git a/api/application-service/definitions/location.yaml b/api/application-service/definitions/location.yaml index 4967ef61..5a0f92c8 100644 --- a/api/application-service/definitions/location.yaml +++ b/api/application-service/definitions/location.yaml @@ -19,12 +19,14 @@ properties: protocol: description: The protocol ID that the third party location is a part of. type: string - example: irc + example: "irc" fields: description: Information used to identify this third party location. type: object - example: - "network": "freenode" + example: { + "network": "freenode", "channel": "#matrix" + } +required: ['alias', 'protocol', 'fields'] title: Location type: object \ No newline at end of file diff --git a/api/application-service/definitions/protocol.yaml b/api/application-service/definitions/protocol.yaml index 231e8288..851091d6 100644 --- a/api/application-service/definitions/protocol.yaml +++ b/api/application-service/definitions/protocol.yaml @@ -11,41 +11,60 @@ # 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: Protocol +type: object properties: user_fields: - description: Fields used to identify a third party user. + description: |- + Fields which may be used to identify a third party user. These should be + ordered to suggest the way that entities may be grouped, where higher + groupings are ordered first. For example, the name of a network should be + searched before the nickname of a 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. + description: |- + Fields which may be used to identify a third party location. These should be + ordered to suggest the way that entities may be grouped, where higher + groupings are ordered first. For example, the name of a network should be + searched before the name of a channel. 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. + description: A content URI representing an icon for 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. + description: |- + The type definitions for the fields defined in the ``user_fields`` and + ``location_fields``. Each entry in those arrays MUST have an entry here. The + ``string`` key for this object is field name itself. + + May be an empty object if no fields are defined. 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 + additionalProperties: + 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. This may be relatively + coarse to verify the value as the application service providing this protocol + may apply additional validation or filtering. + type: string + placeholder: + description: An placeholder serving as a valid example of the field value. + type: string + required: ['regexp', 'placeholder'] + required: ['fieldname'] example: { "network": { "regexp": "([a-z0-9]+\\.)*[a-z0-9]+", @@ -63,17 +82,32 @@ properties: 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. + For example, multiple networks on IRC if multiple are provided by the + same application service. 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 + title: Protocol Instance + properties: + desc: + type: string + description: A human-readable description for the protocol, such as the name. + example: "Freenode" + icon: + type: string + description: |- + An optional content URI representing the protocol. Overrides the one provided + at the higher level Protocol object. + example: "mxc://example.org/JkLmNoPq" + fields: + type: object + description: Preset values for ``fields`` the client may use to search by. + example: { + "network": "freenode" + } + network_id: + type: string + description: A unique identifier across all instances. + example: "freenode" + required: ['desc', 'fields', 'network_id'] +required: ['user_fields', 'location_fields', 'icon', 'field_types', 'instances'] diff --git a/api/application-service/definitions/protocol_metadata.yaml b/api/application-service/definitions/protocol_metadata.yaml index 2b2c8f4e..e7bf45da 100644 --- a/api/application-service/definitions/protocol_metadata.yaml +++ b/api/application-service/definitions/protocol_metadata.yaml @@ -36,6 +36,7 @@ example: { }, "instances": [ { + "network_id": "freenode", "desc": "Freenode", "icon": "mxc://example.org/JkLmNoPq", "fields": { @@ -59,6 +60,7 @@ example: { }, "instances": [ { + "network_id": "gitter", "desc": "Gitter", "icon": "mxc://example.org/zXyWvUt", "fields": {} diff --git a/api/application-service/definitions/security.yaml b/api/application-service/definitions/security.yaml new file mode 100644 index 00000000..bcfc69c0 --- /dev/null +++ b/api/application-service/definitions/security.yaml @@ -0,0 +1,18 @@ +# 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. +homeserverAccessToken: + type: apiKey + description: The ``hs_token`` provided by the application service's registration. + name: access_token + in: query diff --git a/api/application-service/definitions/user.yaml b/api/application-service/definitions/user.yaml index a7b2287e..258e7c13 100644 --- a/api/application-service/definitions/user.yaml +++ b/api/application-service/definitions/user.yaml @@ -21,11 +21,13 @@ properties: protocol: description: The protocol ID that the third party location is a part of. type: string - example: gitter + example: "gitter" fields: description: Information used to identify this third party location. type: object - example: + example: { "user": "jim" + } +required: ['userid', 'protocol', 'fields'] title: User type: object \ No newline at end of file diff --git a/api/application-service/application_service.yaml b/api/application-service/protocols.yaml similarity index 75% rename from api/application-service/application_service.yaml rename to api/application-service/protocols.yaml index bc6e45d5..32ac2c3c 100644 --- a/api/application-service/application_service.yaml +++ b/api/application-service/protocols.yaml @@ -1,4 +1,3 @@ -# Copyright 2016 OpenMarket Ltd # Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,83 +19,15 @@ host: localhost:8008 schemes: - https - http -basePath: "/" +basePath: /_matrix/app/v1 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: - "/transactions/{txnId}": - put: - summary: Send some events to the application service. - description: |- - 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 - name: txnId - type: string - description: |- - The transaction ID for this set of events. Homeservers generate - these IDs and they are used to ensure idempotency of requests. - required: true - x-example: "35" - - in: body - name: body - description: A list of events. - 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" - }, - { - "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: - type: array - description: A list of events - items: - type: object - title: Event - required: ["events"] - responses: - 200: - description: The transaction was processed successfully. - examples: - application/json: { - } - schema: - type: object - "/_matrix/app/unstable/thirdparty/protocol/{protocol}": + "/thirdparty/protocol/{protocol}": get: summary: Retrieve metadata about a specific protocol that the application service supports. description: |- @@ -104,6 +35,8 @@ paths: with specific information about the various third party networks that an application service supports. operationId: getProtocolMetadata + security: + - homeserverAccessToken: [] parameters: - in: path name: protocol @@ -143,7 +76,7 @@ paths: } schema: $ref: ../client-server/definitions/errors/error.yaml - "/_matrix/app/unstable/thirdparty/user/{protocol}": + "/thirdparty/user/{protocol}": get: summary: Retrieve the Matrix User ID of a corresponding third party user. description: |- @@ -151,6 +84,8 @@ paths: User ID linked to a user on the third party network, given a set of user parameters. operationId: queryUserByProtocol + security: + - homeserverAccessToken: [] parameters: - in: path name: protocol @@ -196,12 +131,14 @@ paths: } schema: $ref: ../client-server/definitions/errors/error.yaml - "/_matrix/app/unstable/thirdparty/location/{protocol}": + "/thirdparty/location/{protocol}": get: - summary: Retreive Matrix-side portal rooms leading to a third party location. + summary: Retrieve 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 + security: + - homeserverAccessToken: [] parameters: - in: path name: protocol @@ -247,13 +184,15 @@ paths: } schema: $ref: ../client-server/definitions/errors/error.yaml - "/_matrix/app/unstable/thirdparty/location": + "/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 + Retrieve an array of third party network locations from a Matrix room alias. operationId: queryLocationByAlias + security: + - homeserverAccessToken: [] parameters: - in: query name: alias @@ -292,12 +231,14 @@ paths: } schema: $ref: ../client-server/definitions/errors/error.yaml - "/_matrix/app/unstable/thirdparty/user": + "/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. + Retrieve an array of third party users from a Matrix User ID. operationId: queryUserByID + security: + - homeserverAccessToken: [] parameters: - in: query name: userid diff --git a/api/application-service/query_room.yaml b/api/application-service/query_room.yaml index b885cb86..2fbc87d1 100644 --- a/api/application-service/query_room.yaml +++ b/api/application-service/query_room.yaml @@ -20,11 +20,13 @@ host: localhost:8008 schemes: - https - http -basePath: "/" +basePath: /_matrix/app/v1 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: "/rooms/{roomAlias}": get: @@ -36,6 +38,8 @@ paths: homeserver will send this request when it receives a request to join a room alias within the application service's namespace. operationId: queryRoomByAlias + security: + - homeserverAccessToken: [] parameters: - in: path name: roomAlias diff --git a/api/application-service/query_user.yaml b/api/application-service/query_user.yaml index 0431b5e4..da363382 100644 --- a/api/application-service/query_user.yaml +++ b/api/application-service/query_user.yaml @@ -20,11 +20,13 @@ host: localhost:8008 schemes: - https - http -basePath: "/" +basePath: /_matrix/app/v1 consumes: - application/json produces: - application/json +securityDefinitions: + $ref: definitions/security.yaml paths: "/users/{userId}": get: @@ -36,6 +38,8 @@ paths: send this request when it receives an event for an unknown user ID in the application service's namespace, such as a room invite. operationId: queryUserById + security: + - homeserverAccessToken: [] parameters: - in: path name: userId diff --git a/api/application-service/transactions.yaml b/api/application-service/transactions.yaml new file mode 100644 index 00000000..98181196 --- /dev/null +++ b/api/application-service/transactions.yaml @@ -0,0 +1,78 @@ +# 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. +# 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 Application Service API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/app/v1 +produces: + - application/json +securityDefinitions: + $ref: definitions/security.yaml +paths: + "/transactions/{txnId}": + put: + summary: Send some events to the application service. + description: |- + 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 + security: + - homeserverAccessToken: [] + parameters: + - in: path + name: txnId + type: string + description: |- + The transaction ID for this set of events. Homeservers generate + these IDs and they are used to ensure idempotency of requests. + required: true + x-example: "35" + - in: body + name: body + description: A list of events. + schema: + type: object + example: { + "events": [ + {"$ref": "../../event-schemas/examples/m.room.member"}, + {"$ref": "../../event-schemas/examples/m.room.message#m.text"} + ] + } + description: Transaction information + properties: + events: + type: array + description: |- + A list of events, formatted as per the Client-Server API. + items: + type: object + title: Event + required: ["events"] + responses: + 200: + description: The transaction was processed successfully. + examples: + application/json: {} + schema: + type: object diff --git a/api/client-server/admin.yaml b/api/client-server/admin.yaml index 2fdac82b..09942a10 100644 --- a/api/client-server/admin.yaml +++ b/api/client-server/admin.yaml @@ -105,7 +105,8 @@ paths: type: string description: Most recently seen IP address of the session. last_seen: - type: number + type: integer + format: int64 description: Unix timestamp that the session was last active. user_agent: type: string diff --git a/api/client-server/appservice_room_directory.yaml b/api/client-server/appservice_room_directory.yaml new file mode 100644 index 00000000..49393cd4 --- /dev/null +++ b/api/client-server/appservice_room_directory.yaml @@ -0,0 +1,88 @@ +# 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 Application Service Room Directory API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https + - http +basePath: /_matrix/client/%CLIENT_MAJOR_VERSION% +consumes: + - application/json +produces: + - application/json +securityDefinitions: + # Note: this is the same access_token definition used elsewhere in the client + # server API, however this expects an access token for an application service. + $ref: definitions/security.yaml +paths: + "/directory/list/appservice/{networkId}/{roomId}": + put: + summary: |- + Updates a room's visibility in the application service's room directory. + description: |- + Updates the visibility of a given room on the application service's room + directory. + + This API is similar to the room directory visibility API used by clients + to update the homeserver's more general room directory. + + This API requires the use of an application service access token (``as_token``) + instead of a typical client's access_token. This API cannot be invoked by + users who are not identified as application services. + operationId: updateAppserviceRoomDirectoryVsibility + parameters: + - in: path + type: string + name: networkId + description: |- + The protocol (network) ID to update the room list for. This would + have been provided by the application service as being listed as + a supported protocol. + required: true + x-example: "irc" + - in: path + type: string + name: roomId + description: The room ID to add to the directory. + required: true + x-example: "!somewhere:domain.com" + - in: body + name: body + required: true + schema: + type: object + properties: + visibility: + type: string + enum: ["public", "private"] + description: |- + Whether the room should be visible (public) in the directory + or not (private). + example: "public" + required: ['visibility'] + security: + # again, this is the appservice's token - not a typical client's + - accessToken: [] + responses: + 200: + description: The room's directory visibility has been updated. + schema: + type: object + examples: + application/json: {} + tags: + - Application service room directory management diff --git a/api/client-server/content-repo.yaml b/api/client-server/content-repo.yaml index b3e9517b..5f4e9111 100644 --- a/api/client-server/content-repo.yaml +++ b/api/client-server/content-repo.yaml @@ -259,7 +259,8 @@ paths: description: "The URL to get a preview of" required: true - in: query - type: number + type: integer + format: int64 x-example: 1510610716656 name: ts description: |- @@ -276,7 +277,8 @@ paths: type: object properties: "matrix:image:size": - type: number + type: integer + format: int64 description: |- The byte-size of the image. Omitted if there is no image attached. "og:image": @@ -324,7 +326,8 @@ paths: type: object properties: m.upload.size: - type: number + type: integer + format: int64 description: |- The maximum size an upload can be in bytes. Clients SHOULD use this as a guide when uploading content. diff --git a/api/client-server/definitions/event_filter.yaml b/api/client-server/definitions/event_filter.yaml index 1cae3ea9..8c96917f 100644 --- a/api/client-server/definitions/event_filter.yaml +++ b/api/client-server/definitions/event_filter.yaml @@ -11,7 +11,7 @@ # 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: Filter +title: EventFilter properties: limit: description: The maximum number of events to return. diff --git a/api/client-server/definitions/public_rooms_response.yaml b/api/client-server/definitions/public_rooms_response.yaml index fc6ccb44..ab701051 100644 --- a/api/client-server/definitions/public_rooms_response.yaml +++ b/api/client-server/definitions/public_rooms_response.yaml @@ -45,7 +45,7 @@ properties: description: |- The name of the room, if any. num_joined_members: - type: number + type: integer description: |- The number of members joined to the room. room_id: @@ -82,7 +82,7 @@ properties: 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 + type: integer description: |- An estimate on the total number of public rooms, if the server has an estimate. diff --git a/api/client-server/definitions/room_event_filter.yaml b/api/client-server/definitions/room_event_filter.yaml index 7d9184b5..c36b3768 100644 --- a/api/client-server/definitions/room_event_filter.yaml +++ b/api/client-server/definitions/room_event_filter.yaml @@ -13,23 +13,23 @@ # limitations under the License. allOf: - $ref: event_filter.yaml -title: RoomEventFilter -properties: - not_rooms: - description: A list of room IDs to exclude. If this list is absent then no rooms - are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` - filter. - items: - type: string - type: array - rooms: - description: A list of room IDs to include. If this list is absent then all rooms - are included. - items: - type: string - type: array - contains_url: - type: boolean - description: If ``true``, includes only events with a url key in their content. If - ``false``, excludes those events. -type: object +- type: object + title: RoomEventFilter + properties: + not_rooms: + description: A list of room IDs to exclude. If this list is absent then no rooms + are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` + filter. + items: + type: string + type: array + rooms: + description: A list of room IDs to include. If this list is absent then all rooms + are included. + items: + type: string + type: array + contains_url: + type: boolean + description: If ``true``, includes only events with a ``url`` key in their content. If + ``false``, excludes those events. Defaults to ``false``. diff --git a/api/client-server/definitions/sync_filter.yaml b/api/client-server/definitions/sync_filter.yaml index 69b245a3..33bead26 100644 --- a/api/client-server/definitions/sync_filter.yaml +++ b/api/client-server/definitions/sync_filter.yaml @@ -11,6 +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. +type: object +title: Filter properties: event_fields: description: List of event fields to include. If this list is absent then all @@ -40,6 +42,7 @@ properties: room: title: RoomFilter description: Filters to be applied to room data. + type: object properties: not_rooms: description: A list of room IDs to exclude. If this list is absent then no rooms @@ -76,5 +79,3 @@ properties: allOf: - $ref: room_event_filter.yaml description: The per user account data to include for rooms. - type: object -type: object diff --git a/api/client-server/definitions/wellknown/homeserver.yaml b/api/client-server/definitions/wellknown/homeserver.yaml new file mode 100644 index 00000000..92ff34ed --- /dev/null +++ b/api/client-server/definitions/wellknown/homeserver.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: Homeserver Information +description: |- + Used by clients to discover homeserver information. +type: object +properties: + base_url: + type: string + description: The base URL for the homeserver for client-server connections. + example: https://matrix.example.com +required: + - base_url diff --git a/api/client-server/definitions/wellknown/identity_server.yaml b/api/client-server/definitions/wellknown/identity_server.yaml new file mode 100644 index 00000000..a8f7c31c --- /dev/null +++ b/api/client-server/definitions/wellknown/identity_server.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: Identity Server Information +description: |- + Used by clients to discover identity server information. +type: object +properties: + base_url: + type: string + description: The base URL for the identity server for client-server connections. + example: https://identity.example.com +required: + - base_url diff --git a/api/client-server/directory.yaml b/api/client-server/directory.yaml index ee42cf84..78ddfa29 100644 --- a/api/client-server/directory.yaml +++ b/api/client-server/directory.yaml @@ -41,7 +41,7 @@ paths: required: true x-example: "#monkeys:matrix.org" - in: body - name: roomInfo + name: body description: Information about this room alias. required: true schema: @@ -50,24 +50,24 @@ paths: room_id: type: string description: The room ID to set. + required: ['room_id'] example: { - "room_id": "!abnjk1jdasj98:capuchins.com" - } + "room_id": "!abnjk1jdasj98:capuchins.com" + } responses: 200: description: The mapping was created. examples: - application/json: { - } + application/json: {} schema: type: object 409: description: A room alias with that name already exists. examples: application/json: { - "errcode": "M_UNKNOWN", - "error": "Room alias #monkeys:matrix.org already exists." - } + "errcode": "M_UNKNOWN", + "error": "Room alias #monkeys:matrix.org already exists." + } schema: "$ref": "definitions/errors/error.yaml" tags: diff --git a/api/client-server/filter.yaml b/api/client-server/filter.yaml index b34da7b6..db215196 100644 --- a/api/client-server/filter.yaml +++ b/api/client-server/filter.yaml @@ -54,44 +54,45 @@ paths: allOf: - $ref: "definitions/sync_filter.yaml" example: { - "room": { - "state": { - "types": ["m.room.*"], - "not_rooms": ["!726s6s6q:example.com"] - }, - "timeline": { - "limit": 10, - "types": ["m.room.message"], - "not_rooms": ["!726s6s6q:example.com"], - "not_senders": ["@spam:example.com"] - }, - "ephemeral": { - "types": ["m.receipt", "m.typing"], - "not_rooms": ["!726s6s6q:example.com"], - "not_senders": ["@spam:example.com"] - } + "room": { + "state": { + "types": ["m.room.*"], + "not_rooms": ["!726s6s6q:example.com"] }, - "presence": { - "types": ["m.presence"], - "not_senders": ["@alice:example.com"] + "timeline": { + "limit": 10, + "types": ["m.room.message"], + "not_rooms": ["!726s6s6q:example.com"], + "not_senders": ["@spam:example.com"] }, - "event_format": "client", - "event_fields": ["type", "content", "sender"] - } + "ephemeral": { + "types": ["m.receipt", "m.typing"], + "not_rooms": ["!726s6s6q:example.com"], + "not_senders": ["@spam:example.com"] + } + }, + "presence": { + "types": ["m.presence"], + "not_senders": ["@alice:example.com"] + }, + "event_format": "client", + "event_fields": ["type", "content", "sender"] + } responses: 200: description: The filter was created. - examples: - application/json: { - "filter_id": "66696p746572" - } schema: type: object properties: filter_id: type: string description: |- - The ID of the filter that was created. + The ID of the filter that was created. Cannot start + with a ``{`` as this character is used to determine + if the filter provided is inline JSON or a previously + declared filter by homeservers on some APIs. + example: "66696p746572" + required: ['filter_id'] tags: - Room participation "/user/{userId}/filter/{filterId}": diff --git a/api/client-server/list_public_rooms.yaml b/api/client-server/list_public_rooms.yaml index 72a12060..43239b5c 100644 --- a/api/client-server/list_public_rooms.yaml +++ b/api/client-server/list_public_rooms.yaml @@ -123,7 +123,7 @@ paths: parameters: - in: query name: limit - type: number + type: integer description: |- Limit the number of results returned. - in: query @@ -173,7 +173,7 @@ paths: type: object properties: limit: - type: number + type: integer description: |- Limit the number of results returned. since: @@ -194,8 +194,26 @@ paths: description: |- A string to search for in the room metadata, e.g. name, topic, canonical alias etc. (Optional). + include_all_networks: + type: boolean + description: |- + Whether or not to include all known networks/protocols from + application services on the homeserver. Defaults to false. + example: false + third_party_instance_id: + type: string + description: |- + The specific third party network/protocol to request from the + homeserver. Can only be used if ``include_all_networks`` is false. + example: "irc" example: { - "limit": 10, "filter": {"generic_search_term": "foo"}} + "limit": 10, + "filter": { + "generic_search_term": "foo" + }, + "include_all_networks": false, + "third_party_instance_id": "irc" + } responses: 200: description: A list of the rooms on the server. @@ -233,7 +251,7 @@ paths: description: |- The name of the room, if any. num_joined_members: - type: number + type: integer description: |- The number of members joined to the room. room_id: @@ -270,7 +288,7 @@ paths: 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 + type: integer description: |- An estimate on the total number of public rooms, if the server has an estimate. diff --git a/api/client-server/notifications.yaml b/api/client-server/notifications.yaml index e10e5bfd..b450885b 100644 --- a/api/client-server/notifications.yaml +++ b/api/client-server/notifications.yaml @@ -45,7 +45,7 @@ paths: required: false x-example: "xxxxx" - in: query - type: number + type: integer name: limit description: Limit on the number of events to return in this request. required: false diff --git a/api/client-server/pushrules.yaml b/api/client-server/pushrules.yaml index ceb9954b..e23c9189 100644 --- a/api/client-server/pushrules.yaml +++ b/api/client-server/pushrules.yaml @@ -343,6 +343,8 @@ paths: This endpoint allows the creation, modification and deletion of pushers for this user ID. The behaviour of this endpoint varies depending on the values in the JSON body. + + When creating push rules, they MUST be enabled by default. operationId: setPushRule security: - accessToken: [] @@ -424,7 +426,7 @@ paths: required: ["actions"] responses: 200: - description: The pusher was set. + description: The push rule was created/updated. examples: application/json: { } diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml index 56da9add..e4b05629 100644 --- a/api/client-server/registration.yaml +++ b/api/client-server/registration.yaml @@ -218,7 +218,7 @@ paths: description: The email address example: "example@example.com" send_attempt: - type: number + type: integer description: Used to distinguish protocol level retries from requests to re-send the email. example: 1 required: ["client_secret", "email", "send_attempt"] @@ -283,7 +283,7 @@ paths: description: The phone number. example: "example@example.com" send_attempt: - type: number + type: integer 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"] diff --git a/api/client-server/room_state.yaml b/api/client-server/room_state.yaml index c04fb803..bda66eb8 100644 --- a/api/client-server/room_state.yaml +++ b/api/client-server/room_state.yaml @@ -87,6 +87,16 @@ paths: type: string description: |- A unique identifier for the event. + 403: + description: |- + The sender doesn't have permission to send the event into the room. + schema: + $ref: "definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "You do not have permission to send the event." + } tags: - Room participation "/rooms/{roomId}/state/{eventType}": @@ -142,5 +152,15 @@ paths: type: string description: |- A unique identifier for the event. + 403: + description: |- + The sender doesn't have permission to send the event into the room. + schema: + $ref: "definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "You do not have permission to send the event." + } tags: - Room participation diff --git a/api/client-server/search.yaml b/api/client-server/search.yaml index e4118c32..4a5f4515 100644 --- a/api/client-server/search.yaml +++ b/api/client-server/search.yaml @@ -41,7 +41,7 @@ paths: type: string description: |- The point to return events from. If given, this should be a - `next_batch` result from a previous call to this endpoint. + ``next_batch`` result from a previous call to this endpoint. x-example: "YWxsCgpOb25lLDM1ODcwOA" - in: body name: body @@ -95,6 +95,7 @@ paths: # for now :/ description: |- This takes a `filter`_. + $ref: "definitions/room_event_filter.yaml" order_by: title: "Ordering" type: string @@ -179,7 +180,7 @@ paths: description: Mapping of category name to search criteria. properties: count: - type: number + type: integer description: An approximate count of the total number of results found. highlights: type: array diff --git a/api/client-server/sync.yaml b/api/client-server/sync.yaml index 4b44c20e..21af0d1e 100644 --- a/api/client-server/sync.yaml +++ b/api/client-server/sync.yaml @@ -77,13 +77,14 @@ paths: - in: query name: set_presence type: string - enum: ["offline"] + enum: ["offline", "online", "unavailable"] description: |- Controls whether the client is automatically marked as online by polling this API. If this parameter is omitted then the client is automatically marked as online when it uses this API. Otherwise if the parameter is set to "offline" then the client is not marked as - being online when it uses this API. + being online when it uses this API. When set to "unavailable", the + client is marked as being idle. x-example: "offline" - in: query name: timeout @@ -227,6 +228,14 @@ paths: room up to the point when the user left. allOf: - $ref: "definitions/timeline_batch.yaml" + account_data: + title: Account Data + type: object + description: |- + The private data that this user has attached to + this room. + allOf: + - $ref: "definitions/event_batch.yaml" presence: title: Presence type: object diff --git a/api/client-server/third_party_lookup.yaml b/api/client-server/third_party_lookup.yaml index cba9ce22..3d348df2 100644 --- a/api/client-server/third_party_lookup.yaml +++ b/api/client-server/third_party_lookup.yaml @@ -73,7 +73,7 @@ paths: $ref: definitions/errors/error.yaml "/thirdparty/location/{protocol}": get: - summary: Retreive Matrix-side portals rooms leading to a third party location. + summary: Retrieve 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 @@ -151,7 +151,7 @@ paths: 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 + Retrieve an array of third party network locations from a Matrix room alias. operationId: queryLocationByAlias security: @@ -181,7 +181,7 @@ paths: 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. + Retrieve an array of third party users from a Matrix User ID. operationId: queryUserByID security: - accessToken: [] diff --git a/api/client-server/users.yaml b/api/client-server/users.yaml index 8a60a7bc..98744719 100644 --- a/api/client-server/users.yaml +++ b/api/client-server/users.yaml @@ -55,7 +55,7 @@ paths: description: The term to search for example: "foo" limit: - type: number + type: integer description: The maximum number of results to return. Defaults to 10. example: 10 required: ["search_term"] diff --git a/api/client-server/wellknown.yaml b/api/client-server/wellknown.yaml new file mode 100644 index 00000000..24e190f9 --- /dev/null +++ b/api/client-server/wellknown.yaml @@ -0,0 +1,66 @@ +# 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 Server Discovery API" + version: "1.0.0" +host: localhost:8008 +schemes: + - https +basePath: /.well-known +produces: + - application/json +paths: + "/matrix/client": + get: + summary: Gets Matrix server discovery information about the domain. + description: |- + Gets discovery information about the domain. The file may include + additional keys, which MUST follow the Java package naming convention, + e.g. ``com.example.myapp.property``. This ensures property names are + suitably namespaced for each application and reduces the risk of + clashes. + + Note that this endpoint is not necessarily handled by the homeserver, + but by another webserver, to be used for discovering the homeserver URL. + operationId: getWellknown + responses: + 200: + description: Server discovery information. + examples: + application/json: { + "m.homeserver": { + "base_url": "https://matrix.example.com" + }, + "m.identity_server": { + "base_url": "https://identity.example.com" + } + } + schema: + type: object + properties: + m.homeserver: + description: Information about the homeserver to connect to. + "$ref": "definitions/wellknown/homeserver.yaml" + m.identity_server: + description: Optional. Information about the identity server to connect to. + "$ref": "definitions/wellknown/identity_server.yaml" + additionalProperties: + description: Application-dependent keys using Java package naming convention. + required: + - m.homeserver + 404: + description: No server discovery information available. + tags: + - Server administration diff --git a/api/identity/associations.yaml b/api/identity/associations.yaml index 784bb5d6..4225919b 100644 --- a/api/identity/associations.yaml +++ b/api/identity/associations.yaml @@ -46,10 +46,10 @@ paths: description: Validation information for the session. examples: application/json: { - "medium": "email", - "validated_at": 1457622739026, - "address": "louise@bobs.burgers" - } + "medium": "email", + "validated_at": 1457622739026, + "address": "louise@bobs.burgers" + } schema: type: object properties: @@ -62,6 +62,7 @@ paths: validated_at: type: integer description: Timestamp indicating the time that the 3pid was validated. + required: ['medium', 'address', 'validated_at'] 400: description: |- The session has not been validated. @@ -71,16 +72,20 @@ paths: ``errcode`` will be ``M_SESSION_EXPIRED``. examples: application/json: { - "errcode": "M_SESSION_NOT_VALIDATED", - "error": "This validation session has not yet been completed" - } + "errcode": "M_SESSION_NOT_VALIDATED", + "error": "This validation session has not yet been completed" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" 404: description: The Session ID or client secret were not found examples: application/json: { - "errcode": "M_NO_VALID_SESSION", - "error": "No valid session was found matching that sid and client secret" - } + "errcode": "M_NO_VALID_SESSION", + "error": "No valid session was found matching that sid and client secret" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/bind": post: summary: Publish an association between a session and a Matrix user ID. @@ -101,10 +106,10 @@ paths: schema: type: object example: { - "sid": "1234", - "client_secret": "monkeys_are_GREAT", - "mxid": "@ears:matrix.org" - } + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "mxid": "@ears:matrix.org" + } properties: sid: type: string @@ -121,19 +126,19 @@ paths: 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, + "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" - } + "signatures": { + "matrix.org": { + "ed25519:0": "ENiU2YORYUJgE6WBMitU0mppbQjidDLanAusj8XS2nVRHPu+0t42OKA/r6zV6i2MzUbNQ3c3MiLScJuSsOiVDQ" } } + } schema: type: object properties: @@ -158,6 +163,15 @@ paths: 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. + $ref: "../../schemas/server-signatures.yaml" + required: + - address + - medium + - mxid + - not_before + - not_after + - ts + - signatures 400: description: |- The association was not published. @@ -167,13 +181,17 @@ paths: ``errcode`` will be ``M_SESSION_EXPIRED``. examples: application/json: { - "errcode": "M_SESSION_NOT_VALIDATED", - "error": "This validation session has not yet been completed" - } + "errcode": "M_SESSION_NOT_VALIDATED", + "error": "This validation session has not yet been completed" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" 404: description: The Session ID or client secret were not found examples: application/json: { - "errcode": "M_NO_VALID_SESSION", - "error": "No valid session was found matching that sid and client secret" - } + "errcode": "M_NO_VALID_SESSION", + "error": "No valid session was found matching that sid and client secret" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/email_associations.yaml b/api/identity/email_associations.yaml index 8431c9e8..28f5e680 100644 --- a/api/identity/email_associations.yaml +++ b/api/identity/email_associations.yaml @@ -51,10 +51,10 @@ paths: schema: type: object example: { - "client_secret": "monkeys_are_GREAT", - "email": "foo@example.com", - "send_attempt": 1 - } + "client_secret": "monkeys_are_GREAT", + "email": "foo@example.com", + "send_attempt": 1 + } properties: client_secret: type: string @@ -85,20 +85,28 @@ paths: Session created. examples: application/json: { - "sid": "1234" - } + "sid": "1234" + } schema: type: object properties: sid: type: string description: The session ID. + required: ['sid'] 400: description: | An error ocurred. Some possible errors are: - ``M_INVALID_EMAIL``: The email address provided was invalid. - ``M_EMAIL_SEND_ERROR``: The validation email could not be sent. + examples: + application/json: { + "errcode": "M_INVALID_EMAIL", + "error": "The email address is not valid" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/validate/email/submitToken": post: summary: Validate ownership of an email address. @@ -122,10 +130,10 @@ paths: schema: type: object example: { - "sid": "1234", - "client_secret": "monkeys_are_GREAT", - "token": "atoken" - } + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "token": "atoken" + } properties: sid: type: string @@ -143,14 +151,15 @@ paths: The success of the validation. examples: application/json: { - "success": true - } + "success": true + } schema: type: object properties: success: type: boolean description: Whether the validation was successful or not. + required: ['success'] get: summary: Validate ownership of an email address. description: |- diff --git a/api/identity/invitation_signing.yaml b/api/identity/invitation_signing.yaml index 982dbff7..7de62dd4 100644 --- a/api/identity/invitation_signing.yaml +++ b/api/identity/invitation_signing.yaml @@ -68,9 +68,11 @@ paths: signatures: type: object description: The signature of the mxid, sender, and token. + $ref: "../../schemas/server-signatures.yaml" token: type: string description: The token for the invitation. + required: ['mxid', 'sender', 'signatures', 'token'] examples: application/json: { "mxid": "@foo:bar.com", @@ -84,7 +86,10 @@ paths: } 404: description: Token was not found. - example: { + examples: + application/json: { "errcode": "M_UNRECOGNIZED", "error": "Didn't recognize token" } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/identity/lookup.yaml b/api/identity/lookup.yaml index bfd2153e..f04436ef 100644 --- a/api/identity/lookup.yaml +++ b/api/identity/lookup.yaml @@ -49,19 +49,18 @@ paths: The association for that 3pid, or the empty object if no association is known. 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" - } + "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: @@ -86,6 +85,15 @@ paths: 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. + $ref: "../../schemas/server-signatures.yaml" + required: + - address + - medium + - mxid + - not_before + - not_after + - ts + - signatures "/bulk_lookup": post: summary: Lookup Matrix user IDs for a list of 3pids. @@ -110,9 +118,14 @@ paths: items: type: array title: 3PID mappings + minItems: 2 + maxItems: 2 items: - type: string - title: 3PID medium or address + # TODO: Give real names to these values. Adding a `title` does not work. + #- type: 3PID Medium + #- type: 3PID Address + - type: string + - type: string description: an array of arrays containing the `3PID Types`_ with the ``medium`` in first position and the ``address`` in second position. required: - "threepids" @@ -134,9 +147,16 @@ paths: items: type: array title: 3PID mappings + minItems: 3 + maxItems: 3 items: - type: string - title: 3PID medium or address or the Matrix ID + # TODO: Give real names to these values. Adding a `title` does not work. + #- type: 3PID Medium + #- type: 3PID Address + #- type: Matrix User ID + - type: string + - type: string + - type: string description: an array of array containing the `3PID Types`_ with the ``medium`` in first position, the ``address`` in second position and Matrix ID in third position. required: - "threepids" diff --git a/api/identity/phone_associations.yaml b/api/identity/phone_associations.yaml index c2cc6cfe..f6b1bd45 100644 --- a/api/identity/phone_associations.yaml +++ b/api/identity/phone_associations.yaml @@ -51,11 +51,11 @@ paths: schema: type: object example: { - "client_secret": "monkeys_are_GREAT", - "country": "GB", - "phone_number": "07700900001", - "send_attempt": 1 - } + "client_secret": "monkeys_are_GREAT", + "country": "GB", + "phone_number": "07700900001", + "send_attempt": 1 + } properties: client_secret: type: string @@ -91,20 +91,28 @@ paths: Session created. examples: application/json: { - "sid": "1234" - } + "sid": "1234" + } schema: type: object properties: sid: type: string description: The session ID. + required: ['sid'] 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. + examples: + application/json: { + "errcode": "M_INVALID_ADDRESS", + "error": "The phone number is not valid" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/validate/msisdn/submitToken": post: summary: Validate ownership of a phone number. @@ -128,10 +136,10 @@ paths: schema: type: object example: { - "sid": "1234", - "client_secret": "monkeys_are_GREAT", - "token": "atoken" - } + "sid": "1234", + "client_secret": "monkeys_are_GREAT", + "token": "atoken" + } properties: sid: type: string @@ -149,14 +157,15 @@ paths: The success of the validation. examples: application/json: { - "success": true - } + "success": true + } schema: type: object properties: success: type: boolean description: Whether the validation was successful or not. + required: ['success'] get: summary: Validate ownership of a phone number. description: |- diff --git a/api/identity/pubkey.yaml b/api/identity/pubkey.yaml index 00796975..9cb7c74e 100644 --- a/api/identity/pubkey.yaml +++ b/api/identity/pubkey.yaml @@ -45,13 +45,25 @@ paths: The public key exists. examples: application/json: { - "public_key": "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" - } + "public_key": "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c" + } schema: type: object properties: public_key: type: string + description: Unpadded Base64 encoded public key. + required: ['public_key'] + 404: + description: + The public key was not found. + examples: + application/json: { + "errcode": "M_NOT_FOUND", + "error": "The public key was not found" + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" "/pubkey/isvalid": get: summary: Check whether a long-term public key is valid. @@ -72,14 +84,15 @@ paths: The validity of the public key. examples: application/json: { - "valid": true - } + "valid": true + } schema: type: object properties: valid: type: boolean description: Whether the public key is recognised and is currently valid. + required: ['valid'] "/pubkey/ephemeral/isvalid": get: summary: Check whether a short-term public key is valid. @@ -108,3 +121,4 @@ paths: valid: type: boolean description: Whether the public key is recognised and is currently valid. + required: ['valid'] diff --git a/api/identity/store_invite.yaml b/api/identity/store_invite.yaml index 6b847b5b..1eca7198 100644 --- a/api/identity/store_invite.yaml +++ b/api/identity/store_invite.yaml @@ -54,11 +54,11 @@ paths: schema: type: object example: { - "medium": "email", - "address": "foo@bar.baz", - "room_id": "!something:example.tld", - "sender": "@bob:example.com" - } + "medium": "email", + "address": "foo@bar.baz", + "room_id": "!something:example.tld", + "sender": "@bob:example.com" + } properties: medium: type: string @@ -90,15 +90,16 @@ paths: display_name: type: string description: The generated (redacted) display_name. + required: ['token', 'public_keys', 'display_name'] example: application/json: { - "token": "sometoken", - "public_keys": [ - "serverpublickey", - "ephemeralpublickey" - ], - "display_name": "f...@b..." - } + "token": "sometoken", + "public_keys": [ + "serverpublickey", + "ephemeralpublickey" + ], + "display_name": "f...@b..." + } 400: description: | An error has occured. @@ -108,7 +109,9 @@ paths: error code will be ``M_UNRECOGNIZED``. examples: application/json: { - "errcode": "M_THREEPID_IN_USE", - "error": "Binding already known", - "mxid": mxid - } + "errcode": "M_THREEPID_IN_USE", + "error": "Binding already known", + "mxid": mxid + } + schema: + $ref: "../client-server/definitions/errors/error.yaml" diff --git a/api/server-server/definitions/transaction.yaml b/api/server-server/definitions/transaction.yaml index 7df8b646..9833f785 100644 --- a/api/server-server/definitions/transaction.yaml +++ b/api/server-server/definitions/transaction.yaml @@ -31,7 +31,7 @@ properties: example: 1532991320875 pdus: type: array - description: List of persistent updates to rooms. + description: List of persistent updates to rooms. Must not include more than 50 PDUs. items: $ref: "pdu.yaml" required: ['origin', 'origin_server_ts', 'pdus'] diff --git a/api/server-server/events.yaml b/api/server-server/events.yaml index cf3988a2..c23163d7 100644 --- a/api/server-server/events.yaml +++ b/api/server-server/events.yaml @@ -49,7 +49,8 @@ paths: responses: 200: description: |- - The fully resolved state for the room, including the authorization + The fully resolved state for the room, prior to considering any state + changes induced by the requested event. Includes the authorization chain for the events. schema: type: object @@ -96,7 +97,8 @@ paths: responses: 200: description: |- - The fully resolved state for the room, including the authorization + The fully resolved state for the room, prior to considering any state + changes induced by the requested event. Includes the authorization chain for the events. schema: type: object diff --git a/api/server-server/public_rooms.yaml b/api/server-server/public_rooms.yaml index d162568f..b7691023 100644 --- a/api/server-server/public_rooms.yaml +++ b/api/server-server/public_rooms.yaml @@ -49,6 +49,20 @@ paths: A pagination token from a previous call to this endpoint to fetch more rooms. x-example: "GetMoreRoomsTokenHere" + - in: query + name: include_all_networks + type: boolean + description: |- + Whether or not to include all networks/protocols defined by application + services on the homeserver. Defaults to false. + x-example: false + - in: query + name: third_party_instance_id + type: string + description: |- + The specific third party network/protocol to request from the homeserver. + Can only be used if ``include_all_networks`` is false. + x-example: "irc" responses: 200: description: The public room list for the homeserver. diff --git a/api/server-server/transactions.yaml b/api/server-server/transactions.yaml index 8d810ad5..ad10ec0b 100644 --- a/api/server-server/transactions.yaml +++ b/api/server-server/transactions.yaml @@ -60,8 +60,8 @@ paths: edus: type: array description: |- - List of ephemeral messages. May be omitted if there are no ephemeral - messages to be sent. + List of ephemeral messages. May be omitted if there are no ephemeral + messages to be sent. Must not include more than 100 EDUs. items: $ref: "definitions/edu.yaml" example: { diff --git a/changelogs/application_service.rst b/changelogs/application_service.rst new file mode 100644 index 00000000..e69de29b diff --git a/changelogs/application_service/newsfragments/.gitignore b/changelogs/application_service/newsfragments/.gitignore new file mode 100644 index 00000000..b722e9e1 --- /dev/null +++ b/changelogs/application_service/newsfragments/.gitignore @@ -0,0 +1 @@ +!.gitignore \ No newline at end of file diff --git a/changelogs/application_service/pyproject.toml b/changelogs/application_service/pyproject.toml new file mode 100644 index 00000000..44d430e8 --- /dev/null +++ b/changelogs/application_service/pyproject.toml @@ -0,0 +1,30 @@ +[tool.towncrier] + filename = "../application_service.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/changelogs/client_server/newsfragments/1176.new b/changelogs/client_server/newsfragments/1176.new new file mode 100644 index 00000000..41e30799 --- /dev/null +++ b/changelogs/client_server/newsfragments/1176.new @@ -0,0 +1 @@ +Specify how to control the power level required for ``@room`` \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1359.feature b/changelogs/client_server/newsfragments/1359.feature new file mode 100644 index 00000000..5354e69e --- /dev/null +++ b/changelogs/client_server/newsfragments/1359.feature @@ -0,0 +1 @@ +Add ``.well-known`` server discovery method \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/1465.feature b/changelogs/client_server/newsfragments/1465.feature new file mode 100644 index 00000000..649cf222 --- /dev/null +++ b/changelogs/client_server/newsfragments/1465.feature @@ -0,0 +1 @@ +Share room decryption keys between devices diff --git a/changelogs/client_server/newsfragments/1547.feature b/changelogs/client_server/newsfragments/1547.feature new file mode 100644 index 00000000..76346f23 --- /dev/null +++ b/changelogs/client_server/newsfragments/1547.feature @@ -0,0 +1 @@ +Add a common standard for user, room, and group mentions in messages. diff --git a/changelogs/client_server/newsfragments/1550.feature b/changelogs/client_server/newsfragments/1550.feature new file mode 100644 index 00000000..f04fa9ae --- /dev/null +++ b/changelogs/client_server/newsfragments/1550.feature @@ -0,0 +1 @@ +Add server ACLs as an option for controlling federation in a room. diff --git a/changelogs/client_server/newsfragments/1551.clarification b/changelogs/client_server/newsfragments/1551.clarification new file mode 100644 index 00000000..06eac4da --- /dev/null +++ b/changelogs/client_server/newsfragments/1551.clarification @@ -0,0 +1 @@ +Clarify that new push rules should be enabled by default, and that unrecognised conditions should not match. diff --git a/changelogs/client_server/newsfragments/1551.feature b/changelogs/client_server/newsfragments/1551.feature new file mode 100644 index 00000000..dfce0f0a --- /dev/null +++ b/changelogs/client_server/newsfragments/1551.feature @@ -0,0 +1 @@ +Add new push rules for encrypted events and ``@room`` notifications. diff --git a/changelogs/client_server/newsfragments/1554.feature b/changelogs/client_server/newsfragments/1554.feature new file mode 100644 index 00000000..ec7ffe71 --- /dev/null +++ b/changelogs/client_server/newsfragments/1554.feature @@ -0,0 +1 @@ +Add third party network room directories, as provided by application services. diff --git a/changelogs/client_server/newsfragments/1562.clarification b/changelogs/client_server/newsfragments/1562.clarification new file mode 100644 index 00000000..c46e189d --- /dev/null +++ b/changelogs/client_server/newsfragments/1562.clarification @@ -0,0 +1 @@ +Clarify the supported HTML features for room messages. diff --git a/changelogs/client_server/newsfragments/1568.clarification b/changelogs/client_server/newsfragments/1568.clarification new file mode 100644 index 00000000..4b7a6eaf --- /dev/null +++ b/changelogs/client_server/newsfragments/1568.clarification @@ -0,0 +1 @@ +Move the ``invite_room_state`` definition under ``unsigned`` where it actually resides. diff --git a/changelogs/client_server/newsfragments/1570.clarification b/changelogs/client_server/newsfragments/1570.clarification new file mode 100644 index 00000000..dbf8a821 --- /dev/null +++ b/changelogs/client_server/newsfragments/1570.clarification @@ -0,0 +1 @@ +Clarify the object structures and defaults for Filters. diff --git a/changelogs/client_server/newsfragments/1571.clarification b/changelogs/client_server/newsfragments/1571.clarification new file mode 100644 index 00000000..2410baf3 --- /dev/null +++ b/changelogs/client_server/newsfragments/1571.clarification @@ -0,0 +1 @@ +Clarify instances of ``type: number`` in the swagger/OpenAPI schema definitions. diff --git a/changelogs/client_server/newsfragments/1572.clarification b/changelogs/client_server/newsfragments/1572.clarification new file mode 100644 index 00000000..7e84098f --- /dev/null +++ b/changelogs/client_server/newsfragments/1572.clarification @@ -0,0 +1 @@ +Clarify that left rooms also have account data in ``/sync``. diff --git a/changelogs/client_server/newsfragments/1574.clarification b/changelogs/client_server/newsfragments/1574.clarification new file mode 100644 index 00000000..8d07ef56 --- /dev/null +++ b/changelogs/client_server/newsfragments/1574.clarification @@ -0,0 +1 @@ +Fix naming of the body field in ``PUT /directory/room``. diff --git a/changelogs/client_server/newsfragments/1577.clarification b/changelogs/client_server/newsfragments/1577.clarification new file mode 100644 index 00000000..aec3248f --- /dev/null +++ b/changelogs/client_server/newsfragments/1577.clarification @@ -0,0 +1 @@ +Clarify the filter object schema used in room searching. diff --git a/changelogs/client_server/newsfragments/1590.clarification b/changelogs/client_server/newsfragments/1590.clarification new file mode 100644 index 00000000..27999193 --- /dev/null +++ b/changelogs/client_server/newsfragments/1590.clarification @@ -0,0 +1 @@ +Document the 403 error for sending state events. diff --git a/changelogs/client_server/newsfragments/1596.clarification b/changelogs/client_server/newsfragments/1596.clarification new file mode 100644 index 00000000..3dde069f --- /dev/null +++ b/changelogs/client_server/newsfragments/1596.clarification @@ -0,0 +1 @@ +specify how to handle multiple olm sessions with the same device \ No newline at end of file diff --git a/changelogs/client_server/newsfragments/780.feature b/changelogs/client_server/newsfragments/780.feature new file mode 100644 index 00000000..74725754 --- /dev/null +++ b/changelogs/client_server/newsfragments/780.feature @@ -0,0 +1 @@ +Add more presence options to the ``set_presence`` parameter of ``/sync``. (Thanks @mujx!) diff --git a/changelogs/push_gateway.rst b/changelogs/push_gateway.rst new file mode 100644 index 00000000..33a7683c --- /dev/null +++ b/changelogs/push_gateway.rst @@ -0,0 +1,6 @@ +r0.1.0 +====== + +The first release of the Push Gateway specification. This release contains +a single endpoint, ``/notify``, that pushers may use to send push notifications +to clients. diff --git a/changelogs/push_gateway/newsfragments/.gitignore b/changelogs/push_gateway/newsfragments/.gitignore new file mode 100644 index 00000000..b722e9e1 --- /dev/null +++ b/changelogs/push_gateway/newsfragments/.gitignore @@ -0,0 +1 @@ +!.gitignore \ No newline at end of file diff --git a/changelogs/push_gateway/pyproject.toml b/changelogs/push_gateway/pyproject.toml new file mode 100644 index 00000000..dad1bc04 --- /dev/null +++ b/changelogs/push_gateway/pyproject.toml @@ -0,0 +1,30 @@ +[tool.towncrier] + filename = "../push_gateway.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.forwarded_room_key b/event-schemas/examples/m.forwarded_room_key new file mode 100644 index 00000000..ef1d6180 --- /dev/null +++ b/event-schemas/examples/m.forwarded_room_key @@ -0,0 +1,14 @@ +{ + "content": { + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!Cuyf34gef24t:localhost", + "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", + "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf...", + "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU", + "sender_claimed_ed25519_key": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y", + "forwarding_curve25519_key_chain": [ + "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw" + ] + }, + "type": "m.forwarded_room_key" +} diff --git a/event-schemas/examples/m.room.member#invite_room_state b/event-schemas/examples/m.room.member#invite_room_state index 1a93b395..965669ad 100644 --- a/event-schemas/examples/m.room.member#invite_room_state +++ b/event-schemas/examples/m.room.member#invite_room_state @@ -5,22 +5,24 @@ "avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto", "displayname": "Alice Margatroid" }, - "invite_room_state": [ - { - "type": "m.room.name", - "state_key": "", - "content": { - "name": "Forest of Magic" + "unsigned": { + "invite_room_state": [ + { + "type": "m.room.name", + "state_key": "", + "content": { + "name": "Forest of Magic" + } + }, + { + "type": "m.room.join_rules", + "state_key": "", + "content": { + "join_rule": "invite" + } } - }, - { - "type": "m.room.join_rules", - "state_key": "", - "content": { - "join_rule": "invite" - } - } - ], + ] + }, "state_key": "@alice:localhost", "origin_server_ts": 1431961217939, "event_id": "$WLGTSEFSEF:localhost", diff --git a/event-schemas/examples/m.room.message#m.notice b/event-schemas/examples/m.room.message#m.notice index 978c67e6..876cbbb7 100644 --- a/event-schemas/examples/m.room.message#m.notice +++ b/event-schemas/examples/m.room.message#m.notice @@ -2,7 +2,9 @@ "age": 242352, "content": { "body": "This is an example notice", - "msgtype": "m.notice" + "msgtype": "m.notice", + "format": "org.matrix.custom.html", + "formatted_body": "This is an example notice" }, "origin_server_ts": 1431961217939, "event_id": "$WLGTSEFSEF:localhost", diff --git a/event-schemas/examples/m.room.power_levels b/event-schemas/examples/m.room.power_levels index 0c8f8bc5..2b0aaca5 100644 --- a/event-schemas/examples/m.room.power_levels +++ b/event-schemas/examples/m.room.power_levels @@ -14,7 +14,10 @@ "users": { "@example:localhost": 100 }, - "users_default": 0 + "users_default": 0, + "notifications": { + "room": 20 + } }, "state_key": "", "origin_server_ts": 1431961217939, diff --git a/event-schemas/examples/m.room.server_acl b/event-schemas/examples/m.room.server_acl new file mode 100644 index 00000000..86da2093 --- /dev/null +++ b/event-schemas/examples/m.room.server_acl @@ -0,0 +1,14 @@ +{ + "age": 242352, + "content": { + "allow_ip_literals": false, + "allow": ["*"], + "deny": ["*.evil.com", "evil.com"] + }, + "state_key": "", + "origin_server_ts": 1431961217939, + "event_id": "$WLGTSEFSEF:localhost", + "type": "m.room.server_acl", + "room_id": "!Cuyf34gef24t:localhost", + "sender": "@example:localhost" +} diff --git a/event-schemas/examples/m.room_key_request#cancel_request b/event-schemas/examples/m.room_key_request#cancel_request new file mode 100644 index 00000000..c6eb25de --- /dev/null +++ b/event-schemas/examples/m.room_key_request#cancel_request @@ -0,0 +1,8 @@ +{ + "content": { + "action": "cancel_request", + "requesting_device_id": "RJYKSTBOIE", + "request_id": "1495474790150.19" + }, + "type": "m.room_key_request" +} diff --git a/event-schemas/examples/m.room_key_request#request b/event-schemas/examples/m.room_key_request#request new file mode 100644 index 00000000..8557f08e --- /dev/null +++ b/event-schemas/examples/m.room_key_request#request @@ -0,0 +1,14 @@ +{ + "content": { + "body": { + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!Cuyf34gef24t:localhost", + "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", + "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU" + }, + "action": "request", + "requesting_device_id": "RJYKSTBOIE", + "request_id": "1495474790150.19" + }, + "type": "m.room_key_request" +} diff --git a/event-schemas/schema/core-event-schema/room_event.yaml b/event-schemas/schema/core-event-schema/room_event.yaml index a8a23f54..ebf970ad 100644 --- a/event-schemas/schema/core-event-schema/room_event.yaml +++ b/event-schemas/schema/core-event-schema/room_event.yaml @@ -16,7 +16,8 @@ properties: origin_server_ts: description: Timestamp in milliseconds on originating homeserver when this event was sent. - type: number + type: integer + format: int64 unsigned: description: Contains optional extra information about the event. properties: diff --git a/event-schemas/schema/core-event-schema/state_event.yaml b/event-schemas/schema/core-event-schema/state_event.yaml index 020e9087..71c4137b 100644 --- a/event-schemas/schema/core-event-schema/state_event.yaml +++ b/event-schemas/schema/core-event-schema/state_event.yaml @@ -11,7 +11,11 @@ properties: state_key: description: A unique key which defines the overwriting semantics for this piece of room state. This value is often a zero-length string. The presence of this - key makes this event a State Event. The key MUST NOT start with '_'. + key makes this event a State Event. + + State keys starting with an ``@`` are reserved for referencing user IDs, such + as room members. With the exception of a few events, state events set with a + given user's ID as the state key MUST only be set by that user. type: string required: - state_key diff --git a/event-schemas/schema/m.forwarded_room_key b/event-schemas/schema/m.forwarded_room_key new file mode 100644 index 00000000..f0beed2b --- /dev/null +++ b/event-schemas/schema/m.forwarded_room_key @@ -0,0 +1,59 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + This event type is used to forward 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 + 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. + sender_key: + type: string + description: |- + The Curve25519 key of the device which initiated the session originally. + 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. + sender_claimed_ed25519_key: + type: string + description: |- + The Ed25519 key of the device which initiated the session originally. + It is 'claimed' because the receiving device has no way to tell that the + original room_key actually came from a device which owns the private part of + this key unless they have done device verification. + forwarding_curve25519_key_chain: + type: array + items: + type: string + description: |- + Chain of Curve25519 keys. It starts out empty, but each time the + key is forwarded to another device, the previous sender in the chain is added + to the end of the list. For example, if the key is forwarded from A to B to + C, this field is empty between A and B, and contains A's Curve25519 key between + B and C. + required: + - algorithm + - room_id + - session_id + - session_key + - sender_claimed_ed25519_key + - forwarding_curve25519_key_chain + - sender_key + type: object + type: + enum: + - m.forwarded_room_key + type: string +type: object diff --git a/event-schemas/schema/m.room.encrypted b/event-schemas/schema/m.room.encrypted index 6825be1d..44b09c3f 100644 --- a/event-schemas/schema/m.room.encrypted +++ b/event-schemas/schema/m.room.encrypted @@ -13,8 +13,8 @@ properties: algorithm: type: string enum: - - m.olm.curve25519-aes-sha256 - - m.megolm.v1.aes-sha + - m.olm.v1.curve25519-aes-sha2 + - m.megolm.v1.aes-sha2 description: |- The encryption algorithm used to encrypt this event. The value of this field determines which other properties will be diff --git a/event-schemas/schema/m.room.member b/event-schemas/schema/m.room.member index 4f4077a7..de14644d 100644 --- a/event-schemas/schema/m.room.member +++ b/event-schemas/schema/m.room.member @@ -18,7 +18,9 @@ description: |- The ``third_party_invite`` property will be set if this invite is an ``invite`` event and is the successor of an ``m.room.third_party_invite`` event, and absent otherwise. - This event may also include an ``invite_room_state`` key **outside the** ``content`` **key**. If present, this contains an array of ``StrippedState`` Events. These events provide information on a subset of state events such as the room name. + This event may also include an ``invite_room_state`` key inside the event's ``unsigned`` data. + If present, this contains an array of ``StrippedState`` Events. These events provide information + on a subset of state events such as the room name. properties: content: properties: @@ -71,34 +73,42 @@ properties: - signed title: Invite type: object + unsigned: + type: object + title: UnsignedData + description: Contains optional extra information about the event. + properties: + invite_room_state: + description: 'A subset of the state of the room at the time of the invite, if ``membership`` is ``invite``. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for ``m.room.avatar``, ``m.room.canonical_alias``, ``m.room.join_rules``, and ``m.room.name`` SHOULD be included.' + items: + description: 'A stripped down state event, with only the ``type``, ``state_key`` and ``content`` keys.' + properties: + content: + description: The ``content`` for the event. + title: EventContent + type: object + state_key: + description: The ``state_key`` for the event. + type: string + type: + description: The ``type`` for the event. + type: string + required: + - type + - state_key + - content + title: StrippedState + type: object + type: array required: - membership title: EventContent type: object - invite_room_state: - description: 'A subset of the state of the room at the time of the invite, if ``membership`` is ``invite``. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for ``m.room.avatar``, ``m.room.canonical_alias``, ``m.room.join_rules``, and ``m.room.name`` SHOULD be included.' - items: - description: 'A stripped down state event, with only the ``type``, ``state_key`` and ``content`` keys.' - properties: - content: - description: The ``content`` for the event. - title: EventContent - type: object - state_key: - description: The ``state_key`` for the event. - type: string - type: - description: The ``type`` for the event. - type: string - required: - - type - - state_key - - content - title: StrippedState - type: object - type: array state_key: - description: The ``user_id`` this membership event relates to. + description: |- + The ``user_id`` this membership event relates to. In all cases except for when ``membership`` is + ``join``, the user ID sending the event does not need to match the user ID in the ``state_key``, + unlike other events. Regular authorisation rules still apply. type: string type: enum: diff --git a/event-schemas/schema/m.room.power_levels b/event-schemas/schema/m.room.power_levels index 13a44c70..9bb12993 100644 --- a/event-schemas/schema/m.room.power_levels +++ b/event-schemas/schema/m.room.power_levels @@ -46,10 +46,10 @@ properties: properties: ban: description: The level required to ban a user. Defaults to 50 if unspecified. - type: number + type: integer events: additionalProperties: - type: number + type: integer description: The level required to send specific event types. This is a mapping from event type to power level required. title: Event power levels type: object @@ -57,25 +57,25 @@ properties: description: |- The default level required to send message events. Can be overridden by the ``events`` key. Defaults to 0 if unspecified. - type: number + type: integer invite: description: The level required to invite a user. Defaults to 50 if unspecified. - type: number + type: integer kick: description: The level required to kick a user. Defaults to 50 if unspecified. - type: number + type: integer redact: description: The level required to redact an event. Defaults to 50 if unspecified. - type: number + type: integer state_default: description: |- The default level required to send state events. Can be overridden by the ``events`` key. Defaults to 50 if unspecified, but 0 if there is no ``m.room.power_levels`` event at all. - type: number + type: integer users: additionalProperties: - type: number + type: integer description: The power levels for specific users. This is a mapping from ``user_id`` to power level for that user. title: User power levels type: object @@ -84,7 +84,19 @@ properties: The default power level for every user in the room, unless their ``user_id`` is mentioned in the ``users`` key. Defaults to 0 if unspecified. - type: number + type: integer + notifications: + properties: + room: + type: integer + description: The level required to trigger an ``@room`` notification. Defaults to 50 if unspecified. + additionalProperties: + type: integer + description: |- + The power level requirements for specific notification types. + This is a mapping from ``key`` to power level for that notifications key. + title: Notifications + type: object type: object state_key: description: A zero-length string. diff --git a/event-schemas/schema/m.room.server_acl b/event-schemas/schema/m.room.server_acl new file mode 100644 index 00000000..86d83832 --- /dev/null +++ b/event-schemas/schema/m.room.server_acl @@ -0,0 +1,88 @@ +--- +title: Server ACL +description: |- + An event to indicate which servers are permitted to participate in the + room. Server ACLs may allow or deny groups of hosts. All servers participating + in the room, including those that are denied, are expected to uphold the + server ACL. Servers that do not uphold the ACLs MUST be added to the denied hosts + list in order for the ACLs to remain effective. + + The ``allow`` and ``deny`` lists are lists of globs supporting ``?`` and ``*`` + as wildcards. When comparing against the server ACLs, the suspect server's port + number must not be considered. Therefore ``evil.com``, ``evil.com:8448``, and + ``evil.com:1234`` would all match rules that apply to ``evil.com``, for example. + + The ACLs are applied to servers when they make requests, and are applied in + the following order: + + 1. If there is no ``m.room.server_acl`` event in the room state, allow. + #. If the server name is an IP address (v4 or v6) literal, and ``allow_ip_literals`` + is present and ``false``, deny. + #. If the server name matches an entry in the ``deny`` list, deny. + #. If the server name matches an entry in the ``allow`` list, allow. + #. Otherwise, deny. + + .. Note:: + Server ACLs do not restrict the events relative to the room DAG via authorisation + rules, but instead act purely at the network layer to determine which servers are + allowed to connect and interact with a given room. + + .. WARNING:: + Failing to provide an ``allow`` rule of some kind will prevent **all** + servers from participating in the room, including the sender. This renders + the room unusable. A common allow rule is ``[ "*" ]`` which would still + permit the use of the ``deny`` list without losing the room. + + .. WARNING:: + All compliant servers must implement server ACLs. However, legacy or noncompliant + servers exist which do not uphold ACLs, and these MUST be manually appended to + the denied hosts list when setting an ACL to prevent them from leaking events from + banned servers into a room. Currently, the only way to determine noncompliant hosts is + to check the ``prev_events`` of leaked events, therefore detecting servers which + are not upholding the ACLs. Server versions can also be used to try to detect hosts that + will not uphold the ACLs, although this is not comprehensive. Server ACLs were added + in Synapse v0.32.0, although other server implementations and versions exist in the world. +allOf: + - $ref: core-event-schema/state_event.yaml +type: object +properties: + content: + properties: + allow_ip_literals: + type: boolean + description: |- + True to allow server names that are IP address literals. False to + deny. Defaults to true if missing or otherwise not a boolean. + + This is strongly recommended to be set to ``false`` as servers running + with IP literal names are strongly discouraged in order to require + legitimate homeservers to be backed by a valid registered domain name. + allow: + type: array + description: |- + The server names to allow in the room, excluding any port information. + Wildcards may be used to cover a wider range of hosts, where ``*`` + matches zero or more characters and ``?`` matches exactly one character. + + **This defaults to an empty list when not provided, effectively disallowing + every server.** + items: + type: string + deny: + type: array + description: |- + The server names to disallow in the room, excluding any port information. + Wildcards may be used to cover a wider range of hosts, where ``*`` + matches zero or more characters and ``?`` matches exactly one character. + + This defaults to an empty list when not provided. + items: + type: string + type: object + state_key: + description: A zero-length string. + pattern: '^$' + type: string + type: + enum: ['m.room.server_acl'] + type: string diff --git a/event-schemas/schema/m.room_key_request b/event-schemas/schema/m.room_key_request new file mode 100644 index 00000000..007d0086 --- /dev/null +++ b/event-schemas/schema/m.room_key_request @@ -0,0 +1,61 @@ +--- +allOf: + - $ref: core-event-schema/event.yaml + +description: |- + This event type is used to request keys for end-to-end encryption. It is sent as an + unencrypted `to-device`_ event. +properties: + content: + properties: + body: + description: |- + Information about the requested key. Required when ``action`` is + ``request``. + properties: + algorithm: + type: string + description: |- + The encryption algorithm the requested key in this event is to be used + with. + room_id: + type: string + description: The room where the key is used. + sender_key: + type: string + description: |- + The Curve25519 key of the device which initiated the session originally. + session_id: + type: string + description: The ID of the session that the key is for. + required: + - algorithm + - room_id + - session_id + - sender_key + type: object + title: RequestedKeyInfo + action: + enum: + - request + - cancel_request + type: string + requesting_device_id: + description: ID of the device requesting the key. + type: string + request_id: + description: |- + A random string uniquely identifying the request for a key. If the key is + requested multiple times, it should be reused. It should also reused in order + to cancel a request. + type: string + required: + - action + - requesting_device_id + - request_id + type: object + type: + enum: + - m.room_key_request + type: string +type: object diff --git a/meta/releasing_a_spec.md b/meta/releasing_a_spec.md new file mode 100644 index 00000000..ac3d21fa --- /dev/null +++ b/meta/releasing_a_spec.md @@ -0,0 +1,48 @@ +# How to release a specification + +There are several specifications that belong to matrix, such as the client-server +specification, server-server specification, and identity server specification. Each +of these gets released independently of each other with their own version numbers. + +Once a specification is ready for release, a branch should be created to track the +changes in and to hold potential future hotfixes. This should be the name of the +specification (as it appears in the directory structure of this project) followed +by "release-" and the release version. For example, if the Client-Server Specification +was getting an r0.4.0 release, the branch name would be `client_server/release-r0.4.0`. + +*Note*: Historical releases prior to this process may or may not have an appropriate +release branch. Releases after this document came into place will have an appropriate +branch. + +The remainder of the process is as follows: +1. Activate your Python 3 virtual environment. +1. Having checked out the new release branch, navigate your way over to `./changelogs`. +1. Follow the release instructions provided in the README.md located there. +1. Update the changelog section of the specification you're releasing to make a reference + to the new version. +1. Update any version/link references across all specifications. +1. Ensure the `targets.yml` file lists the version correctly. +1. Commit the changes and PR them to master. +1. Tag the release with the format `client_server/r0.4.0`. +1. Add the changes to the matrix-org/matrix.org repository (for historic tracking). + * This is done by making a PR to the `unstyled_docs/spec` folder for the version and + specification you're releasing. + * Don't forget to symlink the new release as `latest`. +1. Perform a release on GitHub to tag the release. +1. Yell from the mountaintop to the world about the new release. + +### Creating a release for a brand-new specification + +Some specifications may not have ever had a release, and therefore need a bit more work +to become ready. + +1. Activate your Python 3 virtual environment. +1. Having checked out the new release branch, navigate your way over to `./changelogs`. +1. Follow the "new changelog" instructions provided in the README.md located there. +1. Open the specification RST file and make some changes: + * Using a released specification as a template, update the changelog section. + * Use the appropriate changelog variable in the RST. +1. Create/define the appropriate variables in `gendoc.py`. +1. Update `targets.yml`. +1. Update any version/link references across all specifications. +1. Follow the regular release process. diff --git a/schemas/server-signatures.yaml b/schemas/server-signatures.yaml new file mode 100644 index 00000000..a1855256 --- /dev/null +++ b/schemas/server-signatures.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. +type: object +example: { + "example.com": { + "ed25519:0": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus" + } +} +additionalProperties: + type: object + title: Server Signatures + additionalProperties: + type: string \ No newline at end of file diff --git a/scripts/gendoc.py b/scripts/gendoc.py index 16c40af5..ee3ef599 100755 --- a/scripts/gendoc.py +++ b/scripts/gendoc.py @@ -154,7 +154,7 @@ def get_rst(file_info, title_level, title_styles, spec_dir, adjust_titles): # string are file paths to RST blobs 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: + with open(os.path.join(spec_dir, file_info), "r", encoding="utf-8") as f: rst = None if adjust_titles: rst = load_with_adjusted_titles( @@ -186,7 +186,7 @@ def get_rst(file_info, title_level, title_styles, spec_dir, adjust_titles): def build_spec(target, out_filename): log("Building templated file %s" % out_filename) - with open(out_filename, "wb") as outfile: + with open(out_filename, "w", encoding="utf-8") as outfile: for file_info in target["files"]: section = get_rst( file_info=file_info, @@ -195,7 +195,7 @@ def build_spec(target, out_filename): spec_dir=spec_dir, adjust_titles=True ) - outfile.write(section.encode('UTF-8')) + outfile.write(section) """ @@ -223,8 +223,8 @@ def fix_relative_titles(target, filename, out_filename): "^[" + re.escape("".join(title_styles)) + "]{3,}$" ) current_title_style = None - with open(filename, "r") as infile: - with open(out_filename, "w") as outfile: + with open(filename, "r", encoding="utf-8") as infile: + with open(out_filename, "w", encoding="utf-8") as outfile: for line in infile.readlines(): if not relative_title_matcher.match(line): if title_matcher.match(line): @@ -263,8 +263,8 @@ def fix_relative_titles(target, filename, out_filename): def rst2html(i, o, stylesheets): log("rst2html %s -> %s" % (i, o)) - with open(i, "r") as in_file: - with open(o, "w") as out_file: + with open(i, "r", encoding="utf-8") as in_file: + with open(o, "w", encoding="utf-8") as out_file: publish_file( source=in_file, destination=out_file, @@ -280,16 +280,15 @@ def rst2html(i, o, stylesheets): def addAnchors(path): log("add anchors %s" % path) - with open(path, "rb") as f: + with open(path, "r", encoding="utf-8") as f: lines = f.readlines() replacement = r'

\n\1' - with open(path, "wb") as f: + with open(path, "w", encoding="utf-8") 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").encode('UTF-8')) + f.write(line + "\n") def run_through_template(input_files, set_verbose, substitutions): @@ -518,6 +517,14 @@ if __name__ == '__main__': "--server_release", "-s", action="store", default="unstable", help="The server-server release tag to generate, e.g. r1.2" ) + parser.add_argument( + "--appservice_release", "-a", action="store", default="unstable", + help="The appservice release tag to generate, e.g. r1.2" + ) + parser.add_argument( + "--push_gateway_release", "-p", action="store", default="unstable", + help="The push gateway release tag to generate, e.g. r1.2" + ) parser.add_argument( "--list_targets", action="store_true", help="Do not update the specification. Instead print a list of targets.", @@ -542,6 +549,8 @@ if __name__ == '__main__': "%CLIENT_MAJOR_VERSION%": "r0", "%SERVER_RELEASE_LABEL%": args.server_release, "%SERVER_MAJOR_VERSION%": extract_major(args.server_release), + "%APPSERVICE_RELEASE_LABEL%": args.appservice_release, + "%PUSH_GATEWAY_RELEASE_LABEL%": args.push_gateway_release, } exit (main(args.target or ["all"], args.dest, args.nodelete, substitutions)) diff --git a/scripts/generate-matrix-org-assets b/scripts/generate-matrix-org-assets index ed08f81d..76032850 100755 --- a/scripts/generate-matrix-org-assets +++ b/scripts/generate-matrix-org-assets @@ -8,8 +8,11 @@ cd `dirname $0`/.. mkdir -p assets -# generate specification/proposals.rst -./scripts/proposals.py +if [ "$CIRCLECI" != "true" ] +then + # generate specification/proposals.rst + ./scripts/proposals.py +fi # generate the spec docs ./scripts/gendoc.py -d assets/spec diff --git a/scripts/templating/matrix_templates/sections.py b/scripts/templating/matrix_templates/sections.py index 1a93c723..740e6b2e 100644 --- a/scripts/templating/matrix_templates/sections.py +++ b/scripts/templating/matrix_templates/sections.py @@ -31,6 +31,15 @@ class MatrixSections(Sections): def render_client_server_changelog(self): changelogs = self.units.get("changelogs") return changelogs["client_server"] + + # TODO: We should make this a generic variable instead of having to add functions all the time. + def render_push_gateway_changelog(self): + changelogs = self.units.get("changelogs") + return changelogs["push_gateway"] + + def render_application_service_changelog(self): + changelogs = self.units.get("changelogs") + return changelogs["application_service"] def _render_events(self, filterFn, sortFn): template = self.env.get_template("events.tmpl") diff --git a/scripts/templating/matrix_templates/units.py b/scripts/templating/matrix_templates/units.py index 90a87cd4..427b3a00 100644 --- a/scripts/templating/matrix_templates/units.py +++ b/scripts/templating/matrix_templates/units.py @@ -125,7 +125,7 @@ def resolve_references(path, schema): if '$ref' in schema: value = schema['$ref'] path = os.path.join(os.path.dirname(path), value) - with open(path) as f: + with open(path, encoding="utf-8") as f: ref = yaml.load(f, OrderedLoader) result = resolve_references(path, ref) del schema['$ref'] @@ -664,11 +664,11 @@ class MatrixUnits(Units): continue filepath = os.path.join(path, filename) logger.info("Reading swagger API: %s" % filepath) - with open(filepath, "r") as f: + with open(filepath, "r", encoding="utf-8") as f: # strip .yaml group_name = filename[:-5].replace("-", "_") group_name = "%s_%s" % (group_name, suffix) - api = yaml.load(f.read(), OrderedLoader) + api = yaml.load(f, OrderedLoader) api = resolve_references(filepath, api) api["__meta"] = self._load_swagger_meta( api, group_name @@ -698,11 +698,11 @@ class MatrixUnits(Units): continue filepath = os.path.join(path, filename) logger.info("Reading swagger definition: %s" % filepath) - with open(filepath, "r") as f: + with open(filepath, "r", encoding="utf-8") 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 = yaml.load(f, OrderedLoader) definition = resolve_references(filepath, definition) if 'type' not in definition: continue @@ -741,7 +741,7 @@ class MatrixUnits(Units): event_type = filename[:-5] # strip the ".yaml" logger.info("Reading event schema: %s" % filepath) - with open(filepath) as f: + with open(filepath, encoding="utf-8") as f: event_schema = yaml.load(f, OrderedLoader) schema_info = process_data_type( @@ -754,6 +754,8 @@ class MatrixUnits(Units): def load_apis(self, substitutions): cs_ver = substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable") fed_ver = substitutions.get("%SERVER_RELEASE_LABEL%", "unstable") + as_ver = substitutions.get("%APPSERVICE_RELEASE_LABEL%", "unstable") + push_gw_ver = substitutions.get("%PUSH_GATEWAY_RELEASE_LABEL%", "unstable") # we abuse the typetable to return this info to the templates return TypeTable(rows=[ @@ -766,16 +768,16 @@ class MatrixUnits(Units): fed_ver, "Federation between servers", ), TypeTableRow( - "`Application Service API `_", - "unstable", + "`Application Service API `_", + as_ver, "Privileged server plugins", ), TypeTableRow( "`Identity Service API `_", "unstable", "Mapping of third party IDs to Matrix IDs", ), TypeTableRow( - "`Push Gateway API `_", - "unstable", + "`Push Gateway API `_", + push_gw_ver, "Push notifications for Matrix events", ), ]) @@ -791,7 +793,7 @@ class MatrixUnits(Units): filepath = os.path.join(path, filename) logger.info("Reading event example: %s" % filepath) try: - with open(filepath, "r") as f: + with open(filepath, "r", encoding="utf-8") as f: example = json.load(f) examples[filename] = examples.get(filename, []) examples[filename].append(example) @@ -829,7 +831,7 @@ class MatrixUnits(Units): def read_event_schema(self, filepath): logger.info("Reading %s" % filepath) - with open(filepath, "r") as f: + with open(filepath, "r", encoding="utf-8") as f: json_schema = yaml.load(f, OrderedLoader) schema = { @@ -875,15 +877,6 @@ class MatrixUnits(Units): Units.prop(json_schema, "properties/content") ) - # This is horrible because we're special casing a key on m.room.member. - # We need to do this because we want to document a non-content object. - if schema["type"] == "m.room.member": - invite_room_state = get_tables_for_schema( - json_schema["properties"]["invite_room_state"]["items"], - ) - schema["content_fields"].extend(invite_room_state) - - # grab msgtype if it is the right kind of event msgtype = Units.prop( json_schema, "properties/content/properties/msgtype/enum" @@ -950,7 +943,7 @@ class MatrixUnits(Units): title_part = None changelog_lines = [] - with open(path, "r") as f: + with open(path, "r", encoding="utf-8") as f: lines = f.readlines() prev_line = None for line in (tc_lines + lines): diff --git a/specification/appendices/identifier_grammar.rst b/specification/appendices/identifier_grammar.rst index fc89f031..92071611 100644 --- a/specification/appendices/identifier_grammar.rst +++ b/specification/appendices/identifier_grammar.rst @@ -1,5 +1,5 @@ .. Copyright 2016 Openmarket Ltd. -.. Copyright 2017 New Vector Ltd. +.. Copyright 2017, 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. @@ -23,13 +23,37 @@ A homeserver is uniquely identified by its server name. This value is used in a 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:: +be reached by other homeservers. All valid server names are included by the +following grammar:: - server_name = host [ ":" port] - port = *DIGIT + server_name = hostname [ ":" port ] -where ``host`` is as defined by `RFC3986, section 3.2.2 -`_. + port = *DIGIT + + hostname = IPv4address / "[" IPv6address "]" / dns-name + + IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT + + IPv6address = 2*45IPv6char + + IPv6char = DIGIT / %x41-46 / %x61-66 / ":" / "." + ; 0-9, A-F, a-f, :, . + + dns-name = *255dns-char + + dns-char = DIGIT / ALPHA / "-" / "." + +— in other words, the server name is the hostname, followed by an optional +numeric port specifier. The hostname may be a dotted-quad IPv4 address literal, +an IPv6 address literal surrounded with square brackets, or a DNS name. + +IPv4 literals must be a sequence of four decimal numbers in the +range 0 to 255, separated by ``.``. IPv6 literals must be as specified by +`RFC3513, section 2.2 `_. + +DNS names for use with Matrix should follow the conventional restrictions for +internet hostnames: they should consist of a series of labels separated by +``.``, where each label consists of the alphanumeric characters or hyphens. Examples of valid server names are: @@ -40,6 +64,20 @@ Examples of valid server names are: * ``[1234:5678::abcd]`` (IPv6 literal) * ``[1234:5678::abcd]:5678`` (IPv6 literal with explicit port) +.. Note:: + + This grammar is based on the standard for internet host names, as specified + by `RFC1123, section 2.1 `_, + with an extension for IPv6 literals. + +Server names must be treated case-sensitively: in other words, +``@user:matrix.org`` is a different person from ``@user:MATRIX.ORG``. + +Some recommendations for a choice of server name follow: + +* The length of the complete server name should not exceed 230 characters. +* Server names should not use upper-case characters. + Room Versions ~~~~~~~~~~~~~ @@ -51,7 +89,7 @@ not understanding the new rules. A room version is defined as a string of characters which MUST NOT exceed 32 codepoints in length. Room versions MUST NOT be empty and SHOULD contain only -the characters ``a-z``, ``0-9``, ``.``, and ``-``. +the characters ``a-z``, ``0-9``, ``.``, and ``-``. Room versions are not intended to be parsed and should be treated as opaque identifiers. Room versions consisting only of the characters ``0-9`` and ``.`` @@ -283,3 +321,45 @@ domain). .. TODO-spec - Need to specify precise grammar for Room Aliases. https://matrix.org/jira/browse/SPEC-391 + +matrix.to navigation +++++++++++++++++++++ + +.. NOTE:: + This namespacing is in place pending a ``matrix://`` (or similar) URI scheme. + This is **not** meant to be interpreted as an available web service - see + below for more details. + +Rooms, users, aliases, and groups may be represented as a "matrix.to" URI. +This URI can be used to reference particular objects in a given context, such +as mentioning a user in a message or linking someone to a particular point +in the room's history (a permalink). + +A matrix.to URI has the following format, based upon the specification defined +in RFC 3986: + + https://matrix.to/#// + +The identifier may be a room ID, room alias, user ID, or group ID. The extra +parameter is only used in the case of permalinks where an event ID is referenced. +The matrix.to URI, when referenced, must always start with ``https://matrix.to/#/`` +followed by the identifier. + +Clients should not rely on matrix.to URIs falling back to a web server if accessed +and instead should perform some sort of action within the client. For example, if +the user were to click on a matrix.to URI for a room alias, the client may open +a view for the user to participate in the room. + +Examples of matrix.to URIs are: + +* Room alias: ``https://matrix.to/#/#somewhere:domain.com`` +* Room: ``https://matrix.to/#/!somewhere:domain.com`` +* Permalink by room: ``https://matrix.to/#/!somewhere:domain.com/$event:example.org`` +* Permalink by room alias: ``https://matrix.to/#/#somewhere:domain.com/$event:example.org`` +* User: ``https://matrix.to/#/@alice:example.org`` +* Group: ``https://matrix.to/#/+example:domain.com`` + +.. Note:: + Room ID permalinks are unroutable as there is no reliable domain to send requests + to upon receipt of the permalink. Clients should do their best route Room IDs to + where they need to go, however they should also be aware of `issue #1579 `_. \ No newline at end of file diff --git a/specification/application_service_api.rst b/specification/application_service_api.rst index 08e3b062..69d39d21 100644 --- a/specification/application_service_api.rst +++ b/specification/application_service_api.rst @@ -30,22 +30,37 @@ irrespective of the underlying homeserver implementation. .. contents:: Table of Contents .. sectnum:: -Specification version ---------------------- +Changelog +--------- + + +.. topic:: Version: unstable +{{application_service_changelog}} This version of the specification is generated from `matrix-doc `_ as of Git commit `{{git_version}} `_. +For the full historical changelog, see +https://github.com/matrix-org/matrix-doc/blob/master/changelogs/application_service.rst + +Other versions of this specification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following other versions are also available, in reverse chronological order: + +- `HEAD `_: Includes all changes since the latest versioned release. + + Application Services -------------------- -Application services are passive and can only observe events from a given -homeserver (HS). They can inject events into rooms they are participating in. +Application services are passive and can only observe events from homeserver. +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 application service. This is achieved by manually configuring the homeserver -with information about the application service (AS). +with information about the application service. Registration ~~~~~~~~~~~~ @@ -75,8 +90,7 @@ said to be interested in a given event if one of the application service's names 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 +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 services from creating/deleting entities in that namespace. Typically, @@ -90,33 +104,77 @@ regular expressions and look like: users: - exclusive: true - regex: @_irc.freenode.net_.* + regex: "@_irc.freenode.net_.*" + +Application services may define the following namespaces (with none being explicitly required): + ++------------------+-----------------------------------------------------------+ +| Name | Description | ++==================+===========================================================+ +| users | Events which are sent from certain users. | ++------------------+-----------------------------------------------------------+ +| aliases | Events which are sent in rooms with certain room aliases. | ++------------------+-----------------------------------------------------------+ +| rooms | Events which are sent in rooms with certain room IDs. | ++------------------+-----------------------------------------------------------+ + +Each individual namespace MUST declare the following fields: + ++------------------+-----------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++==================+===================================================================================================================================+ +| exclusive | **Required** A true or false value stating whether this application service has exclusive access to events within this namespace. | ++------------------+-----------------------------------------------------------------------------------------------------------------------------------+ +| regex | **Required** A regular expression defining which values this namespace includes. | ++------------------+-----------------------------------------------------------------------------------------------------------------------------------+ +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. The registration is represented by a series of key-value pairs, which this -specification will present as YAML. An example HS configuration required to pass -traffic to the AS is: +specification will present as YAML. See below for the possible options along +with their explanation: + ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| Name | Description | ++==================+====================================================================================================================================================+ +| id | **Required.** A unique, user-defined ID of the application service which will never change. | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| url | **Required.** The URL for the application service. May include a path after the domain name. Optionally set to ``null`` if no traffic is required. | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| as_token | **Required.** A unique token for application services to use to authenticate requests to Homeservers. | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| hs_token | **Required.** A unique token for Homeservers to use to authenticate requests to application services. | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| sender_localpart | **Required.** The localpart of the user associated with the application service. | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| namespaces | **Required.** A list of ``users``, ``aliases`` and ``rooms`` namespaces that the application service controls. | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| rate_limited | Whether requests from masqueraded users are rate-limited. The sender is excluded. | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ +| protocols | The external protocols which the application service provides (e.g. IRC). | ++------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+ + +An example registration file for an IRC-bridging application service is below: .. code-block:: yaml - id: - url: - as_token: - hs_token: - sender_localpart: + id: "IRC Bridge" + url: "http://127.0.0.1:1234" + as_token: "30c05ae90a248a4188e620216fa72e349803310ec83e2a77b34fe90be6081f46" + hs_token: "312df522183efd404ec1cd22d2ffa4bbc76a8c1ccf541dd692eef281356bb74e" + sender_localpart: "_irc_bot" # Will result in @_irc_bot:domain.com namespaces: - users: # Namespaces of users which should be delegated to the AS - - exclusive: - regex: - - ... - aliases: [] # Namespaces of room aliases which should be delegated to the AS - rooms: [] # Namespaces of room ids which should be delegated to the AS - -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. + users: + - exclusive: true + regex: "@_irc_bridge_.*" + aliases: + - exclusive: false + regex: "#_irc_bridge_.*" + rooms: [] .. WARNING:: If the homeserver in question has multiple application services, each @@ -126,6 +184,34 @@ to register for an application service which deals with IRC. Homeserver -> Application Service API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Legacy routes ++++++++++++++ + +Previous drafts of the application service specification had a mix of endpoints +that have been used in the wild for a significant amount of time. The application +service specification now defines a version on all endpoints to be more compatible +with the rest of the Matrix specification and the future. + +Homeservers should attempt to use the specified endpoints first when communicating +with application services. However, if the application service receives an http status +code that does not indicate success (ie: 404, 500, 501, etc) then the homeserver +should fall back to the older endpoints for the application service. + +The older endpoints have the exact same request body and response format, they +just belong at a different path. The equivalent path for each is as follows: + +* ``/_matrix/app/v1/transactions/{txnId}`` becomes ``/transactions/{txnId}`` +* ``/_matrix/app/v1/users/{userId}`` becomes ``/users/{userId}`` +* ``/_matrix/app/v1/rooms/{roomAlias}`` becomes ``/rooms/{roomAlias}`` +* ``/_matrix/app/v1/thirdparty/protocol/{protocol}`` becomes ``/_matrix/app/unstable/thirdparty/protocol/{protocol}`` +* ``/_matrix/app/v1/thirdparty/user/{user}`` becomes ``/_matrix/app/unstable/thirdparty/user/{user}`` +* ``/_matrix/app/v1/thirdparty/location/{location}`` becomes ``/_matrix/app/unstable/thirdparty/location/{location}`` +* ``/_matrix/app/v1/thirdparty/user`` becomes ``/_matrix/app/unstable/thirdparty/user`` +* ``/_matrix/app/v1/thirdparty/location`` becomes ``/_matrix/app/unstable/thirdparty/location`` + +Homeservers should periodically try again for the newer endpoints because the +application service may have been updated. + Pushing events ++++++++++++++ @@ -136,24 +222,26 @@ events. Each list of events includes a transaction ID, which works as follows: Typical HS ---> AS : Homeserver sends events with transaction ID T. - <--- : AS sends back 200 OK. + <--- : Application Service sends back 200 OK. AS ACK Lost HS ---> AS : Homeserver sends events with transaction ID T. <-/- : AS 200 OK is lost. HS ---> AS : Homeserver retries with the same transaction ID of T. - <--- : AS sends back 200 OK. If the AS had processed these events - already, it can NO-OP this request (and it knows if it is the same - events based on the transaction ID). + <--- : Application Service sends back 200 OK. If the AS had processed these + events already, it can NO-OP this request (and it knows if it is the + same events based on the transaction ID). The events sent to the application service should be linearised, as if they were from the event stream. The homeserver MUST maintain a queue of transactions to -send to the AS. If the application service cannot be reached, the homeserver -SHOULD backoff exponentially until the application service is reachable again. +send to the application service. If the application service cannot be reached, the +homeserver SHOULD backoff exponentially until the application service is reachable again. As application services cannot *modify* the events in any way, these requests can be made without blocking other aspects of the homeserver. Homeservers MUST NOT alter (e.g. add more) events they were going to send within that transaction ID -on retries, as the AS may have already processed the events. +on retries, as the application service may have already processed the events. + +{{transactions_as_http_api}} Querying ++++++++ @@ -180,13 +268,25 @@ this request (e.g. to join a room alias). {{query_room_as_http_api}} -HTTP APIs -+++++++++ +Third party networks +++++++++++++++++++++ -This contains application service APIs which are used by the homeserver. All -application services MUST implement these APIs. These APIs are defined below. +Application services may declare which protocols they support via their registration +configuration for the homeserver. These networks are generally for third party services +such as IRC that the application service is managing. Application services may populate +a Matrix room directory for their registered protocols, as defined in the Client-Server +API Extensions. -{{application_service_as_http_api}} +Each protocol may have several "locations" (also known as "third party locations" or "3PLs"). +A location within a protocol is a place in the third party network, such as an IRC channel. +Users of the third party network may also be represented by the application service. + +Locations and users can be searched by fields defined by the application service, such +as by display name or other attribute. When clients request the homeserver to search +in a particular "network" (protocol), the search fields will be passed along to the +application service for filtering. + +{{protocols_as_http_api}} .. _create the user: `sect:asapi-permissions`_ @@ -198,6 +298,9 @@ Application services can use a more powerful version of the client-server API by identifying itself as an application service to the homeserver. +Endpoints defined in this section MUST be supported by homeservers in the +client-server API as accessible only by application services. + Identity assertion ++++++++++++++++++ The client-server API infers the user ID from the ``access_token`` provided in @@ -229,26 +332,37 @@ An example request would be:: GET /_matrix/client/%CLIENT_MAJOR_VERSION%/account/whoami?user_id=@_irc_user:example.org Authorization: Bearer YourApplicationServiceTokenHere +.. TODO-TravisR: Temporarily take out timestamp massaging while we're releasing r0. + See https://github.com/matrix-org/matrix-doc/issues/1585 +.. Timestamp massaging + +++++++++++++++++++ + The application service may want to inject events at a certain time (reflecting + the time on the network they are tracking e.g. irc, xmpp). Application services + need to be able to adjust the ``origin_server_ts`` value to do this. -Timestamp massaging -+++++++++++++++++++ -The application service may want to inject events at a certain time (reflecting -the time on the network they are tracking e.g. irc, xmpp). Application services -need to be able to adjust the ``origin_server_ts`` value to do this. + Inputs: + - Application service token (``as_token``) + - Desired timestamp (in milliseconds since the unix epoch) -Inputs: - - Application service token (``as_token``) - - Desired timestamp (in milliseconds since the unix epoch) + Notes: + - This will only apply when sending events. -Notes: - - This will only apply when sending events. + :: -:: + PUT /_matrix/client/r0/rooms/!somewhere:domain.com/send/m.room.message/txnId?ts=1534535223283 + Authorization: Bearer YourApplicationServiceTokenHere - PUT /_matrix/client/r0/rooms/!somewhere:domain.com/send/m.room.message/txnId?ts=1534535223283 - Authorization: Bearer YourApplicationServiceTokenHere + Content: The event to send, as per the Client-Server API. + +Timestamp massaging ++++++++++++++++++++ - Content: The event to send, as per the Client-Server API. +Previous drafts of the Application Service API permitted application services +to alter the timestamp of their sent events by providing a ``ts`` query parameter +when sending an event. This API has been excluded from the first release due to +design concerns, however some servers may still support the feature. Please visit +`issue #1585 `_ for more +information. Server admin style permissions ++++++++++++++++++++++++++++++ @@ -266,7 +380,7 @@ users needs API changes in order to: - Have a 'passwordless' user. This involves bypassing the registration flows entirely. This is achieved by -including the AS token on a ``/register`` request, along with a login type of +including the ``as_token`` on a ``/register`` request, along with a login type of ``m.login.application_service`` to set the desired user ID without a password. :: @@ -294,13 +408,27 @@ API MUST do so with a virtual user (provide a ``user_id`` via the query string). is expected that the application service use the transactions pushed to it to handle events rather than syncing with the user implied by ``sender_localpart``. -Event fields -~~~~~~~~~~~~ +Application service room directories +++++++++++++++++++++++++++++++++++++ + +Application services can maintain their own room directories for their defined +third party protocols. These room directories may be accessed by clients through +additional parameters on the ``/publicRooms`` client-server endpoint. + +{{appservice_room_directory_cs_http_api}} + +Referencing messages from a third party network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Application services should include an ``external_url`` in the ``content`` of +events it emits to indicate where the message came from. This typically applies +to application services that bridge other networks into Matrix, such as IRC, +where an HTTP URL may be available to reference. -.. TODO-TravisR: Fix this section to be a general "3rd party networks" section +Clients should provide users with a way to access the ``external_url`` if it +is present. Clients should additionally ensure the URL has a scheme of ``https`` +or ``http`` before making use of it. -We recommend that any events that originated from a remote network should -include an ``external_url`` field in their content to provide a way for Matrix -clients to link into the 'native' client from which the event originated. -For instance, this could contain the message-ID for emails/nntp posts, or a link -to a blog comment when bridging blog comment traffic in & out of Matrix. +The presence of an ``external_url`` on an event does not necessarily mean the +event was sent from an application service. Clients should be wary of the URL +contained within, as it may not be a legitimate reference to the event's source. diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 71c60097..b377cbb8 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -189,6 +189,82 @@ headers to be returned by servers on all requests are: Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization +Server Discovery +---------------- + +In order to allow users to connect to a Matrix server without needing to +explicitly specify the homeserver's URL or other parameters, clients SHOULD use +an auto-discovery mechanism to determine the server's URL based on a user's +Matrix ID. Auto-discovery should only be done at login time. + +In this section, the following terms are used with specific meanings: + +``PROMPT`` + Retrieve the specific piece of information from the user in a way which + fits within the existing client user experience, if the client is inclined to + do so. Failure can take place instead if no good user experience for this is + possible at this point. + +``IGNORE`` + Stop the current auto-discovery mechanism. If no more auto-discovery + mechanisms are available, then the client may use other methods of + determining the required parameters, such as prompting the user, or using + default values. + +``FAIL_PROMPT`` + Inform the user that auto-discovery failed due to invalid/empty data and + ``PROMPT`` for the parameter. + +``FAIL_ERROR`` + Inform the user that auto-discovery did not return any usable URLs. Do not + continue further with the current login process. At this point, valid data + was obtained, but no homeserver is available to serve the client. No further + guess should be attempted and the user should make a conscientious decision + what to do next. + +Well-known URI +~~~~~~~~~~~~~~ + +The ``.well-known`` method uses a JSON file at a predetermined location to +specify parameter values. The flow for this method is as follows: + +1. Extract the server name from the user's Matrix ID by splitting the Matrix ID + at the first colon. +2. Extract the hostname from the server name. +3. Make a GET request to ``https://hostname/.well-known/matrix/client``. + + a. If the returned status code is 404, then ``IGNORE``. + b. If the returned status code is not 200, or the response body is empty, + then ``FAIL_PROMPT``. + c. Parse the response body as a JSON object + + i. If the content cannot be parsed, then ``FAIL_PROMPT``. + + d. Extract the ``base_url`` value from the ``m.homeserver`` property. This + value is to be used as the base URL of the homeserver. + + i. If this value is not provided, then ``FAIL_PROMPT``. + + e. Validate the homeserver base URL: + + i. Parse it as a URL. If it is not a URL, then ``FAIL_ERROR``. + ii. Clients SHOULD validate that the URL points to a valid homeserver + before accepting it by connecting to the |/_matrix/client/versions|_ + endpoint, ensuring that it does not return an error, and parsing and + validating that the data conforms with the expected response + format. If any step in the validation fails, then + ``FAIL_ERROR``. Validation is done as a simple check against + configuration errors, in order to ensure that the discovered address + points to a valid homeserver. + + f. If the ``m.identity_server`` property is present, extract the + ``base_url`` value for use as the base URL of the identity server. + Validation for this URL is done as in the step above, but using + ``/_matrix/identity/api/v1`` as the endpoint to connect to. If the + ``m.identity_server`` property is present, but does not have a + ``base_url`` value, then ``FAIL_ERROR``. + +{{wellknown_cs_http_api}} Client Authentication --------------------- @@ -1596,5 +1672,8 @@ have to wait in milliseconds before they can try again. .. |/user//account_data/| replace:: ``/user//account_data/`` .. _/user//account_data/: #put-matrix-client-%CLIENT_MAJOR_VERSION%-user-userid-account-data-type +.. |/_matrix/client/versions| replace:: ``/_matrix/client/versions`` +.. _/_matrix/client/versions: #get-matrix-client-versions + .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 .. _`3PID Types`: ../appendices.html#pid-types diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 3b037caf..7bbd8ae8 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -23,7 +23,7 @@ user identifiers. From time to time, it is useful to refer to users by other number. This identity service specification describes how mappings between third-party identifiers and Matrix user identifiers can be established, validated, and used. This description technically may apply to any 3pid, but in -practice has only been applied specifically to email addresses. +practice has only been applied specifically to email addresses and phone numbers. .. contents:: Table of Contents .. sectnum:: @@ -56,6 +56,75 @@ is left as an exercise for the client. 3PID types are described in `3PID Types`_ Appendix. +API Standards +------------- + +The mandatory baseline for identity service communication in Matrix is exchanging +JSON objects over HTTP APIs. HTTPS is required for communication, and all API calls +use a Content-Type of ``application/json``. In addition, strings MUST be encoded as +UTF-8. + +Any errors which occur at the Matrix API level MUST return a "standard error response". +This is a JSON object which looks like: + +.. code:: json + + { + "errcode": "", + "error": "" + } + +The ``error`` string will be a human-readable error message, usually a sentence +explaining what went wrong. The ``errcode`` string will be a unique string +which can be used to handle an error message e.g. ``M_FORBIDDEN``. There may be +additional keys depending on the error, but the keys ``error`` and ``errcode`` +MUST always be present. + +Some standard error codes are below: + +:``M_NOT_FOUND``: + The resource requested could not be located. + +:``M_MISSING_PARAMS``: + The request was missing one or more parameters. + +:``M_INVALID_PARAM``: + The request contained one or more invalid parameters. + +:``M_SESSION_NOT_VALIDATED``: + The session has not been validated. + +:``M_NO_VALID_SESSION``: + A session could not be located for the given parameters. + +:``M_SESSION_EXPIRED``: + The session has expired and must be renewed. + +:``M_INVALID_EMAIL``: + The email address provided was not valid. + +:``M_EMAIL_SEND_ERROR``: + There was an error sending an email. Typically seen when attempting to verify + ownership of a given email address. + +:``M_INVALID_ADDRESS``: + The provided third party address was not valid. + +:``M_SEND_ERROR``: + There was an error sending a notification. Typically seen when attempting to + verify ownership of a given third party address. + +:``M_UNRECOGNIZED``: + The request contained an unrecognised value, such as an unknown token or medium. + +:``M_THREEPID_IN_USE``: + The third party identifier is already in use by another user. Typically this + error will have an additional ``mxid`` property to indicate who owns the + third party identifier. + +:``M_UNKNOWN``: + An unknown error has occurred. + Privacy ------- diff --git a/specification/modules/account_data.rst b/specification/modules/account_data.rst index 1c031ee1..d0ed201a 100644 --- a/specification/modules/account_data.rst +++ b/specification/modules/account_data.rst @@ -31,7 +31,7 @@ The client recieves the account data as events in the ``account_data`` sections of a ``/sync``. These events can also be received in a ``/events`` response or in the -``account_data`` section of a room in ``/initialSync``. ``m.tag`` +``account_data`` section of a room in ``/sync``. ``m.tag`` events appearing in ``/events`` will have a ``room_id`` with the room the tags are for. diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst index fa461cc2..170b70f9 100644 --- a/specification/modules/end_to_end_encryption.rst +++ b/specification/modules/end_to_end_encryption.rst @@ -283,6 +283,31 @@ Device verification may reach one of several conclusions. For example: decrypted by such a device. For the Olm protocol, this is documented at https://matrix.org/git/olm/about/docs/signing.rst. +Key sharing +----------- + +If Bob has an encrypted conversation with Alice on his computer, and then logs in +through his phone for the first time, he may want to have access to the previously +exchanged messages. To address this issue, events exist for requesting and sending +keys from device to device. + +When a device is missing keys to decrypt messages, it can request the keys by +sending `m.room_key_request`_ to-device messages to other devices with +``action`` set to ``request``. If a device wishes to share the keys with that +device, it can forward the keys to the first device by sending an encrypted +`m.forwarded_room_key`_ to-device message. The first device should then send an +`m.room_key_request`_ to-device message with ``action`` set to +``cancel_request`` to the other devices that it had originally sent the key +request to; a device that receives a ``cancel_request`` should disregard any +previously-received ``request`` message with the same ``request_id`` and +``requesting_device_id``. + +.. NOTE:: + + Key sharing can be a big attack vector, thus it must be done very carefully. + A reasonable stategy is for a user's client to only send keys requested by the + verified devices of the same user. + Messaging Algorithms -------------------- @@ -391,6 +416,12 @@ 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. +If a client has multiple sessions established with another device, it should +use the session from which it last received a message. A client may expire old +sessions by defining a maximum number of olm sessions that it will maintain for +each device, and expiring sessions on a Least Recently Used basis. The maximum +number of olm sessions maintained per device should be at least 4. + ``m.megolm.v1.aes-sha2`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -464,6 +495,10 @@ Events {{m_room_key_event}} +{{m_room_key_request_event}} + +{{m_forwarded_room_key_event}} + Key management API ~~~~~~~~~~~~~~~~~~ diff --git a/specification/modules/instant_messaging.rst b/specification/modules/instant_messaging.rst index ff87f74b..079a4801 100644 --- a/specification/modules/instant_messaging.rst +++ b/specification/modules/instant_messaging.rst @@ -56,6 +56,60 @@ of message being sent. Each type has their own required and optional keys, as outlined below. If a client cannot display the given ``msgtype`` then it SHOULD display the fallback plain text ``body`` key instead. +Some message types support HTML in the event content that clients should prefer +to display if available. Currently ``m.text``, ``m.emote``, and ``m.notice`` +support an additional ``format`` parameter of ``org.matrix.custom.html``. When +this field is present, a ``formatted_body`` with the HTML must be provided. The +plain text version of the HTML should be provided in the ``body``. + +Clients should limit the HTML they render to avoid Cross-Site Scripting, HTML +injection, and similar attacks. The strongly suggested set of HTML tags to permit, +denying the use and rendering of anything else, is: ``font``, ``del``, ``h1``, +``h2``, ``h3``, ``h4``, ``h5``, ``h6``, ``blockquote``, ``p``, ``a``, ``ul``, +``ol``, ``sup``, ``sub``, ``li``, ``b``, ``i``, ``u``, ``strong``, ``em``, +``strike``, ``code``, ``hr``, ``br``, ``div``, ``table``, ``thead``, ``tbody``, +``tr``, ``th``, ``td``, ``caption``, ``pre``, ``span``, ``img``. + +Not all attributes on those tags should be permitted as they may be avenues for +other disruption attempts, such as adding ``onclick`` handlers or excessively +large text. Clients should only permit the attributes listed for the tags below. +Where ``data-mx-bg-color`` and ``data-mx-color`` are listed, clients should +translate the value (a 6-character hex color code) to the appropriate CSS/attributes +for the tag. + + +:``font``: + ``data-mx-bg-color``, ``data-mx-color`` + +:``span``: + ``data-mx-bg-color``, ``data-mx-color`` + +:``a``: + ``name``, ``target``, ``href`` (provided the value is not relative and has a scheme + matching one of: ``https``, ``http``, ``ftp``, ``mailto``, ``magnet``) + +:``img``: + ``width``, ``height``, ``alt``, ``title``, ``src`` (provided it is a `Matrix Content (MXC) URI`_) + +:``ol``: + ``start`` + +:``code``: + ``class`` (only classes which start with ``language-`` for syntax highlighting) + + +Additionally, web clients should ensure that *all* ``a`` tags get a ``rel="noopener"`` +to prevent the target page from referencing the client's tab/window. + +Tags must not be nested more than 100 levels deep. Clients should only support the subset +of tags they can render, falling back to other representations of the tags where possible. +For example, a client may not be able to render tables correctly and instead could fall +back to rendering tab-delimited text. + +.. Note:: + A future iteration of the specification will support more powerful and extensible + message formatting options, such as the proposal `MSC1225 `_. + {{msgtype_events}} @@ -297,3 +351,4 @@ Clients should sanitise **all displayed keys** for unsafe HTML to prevent Cross- Scripting (XSS) attacks. This includes room names and topics. .. _`E2E module`: `module:e2e`_ +.. _`Matrix Content (MXC) URI`: `module:content`_ \ No newline at end of file diff --git a/specification/modules/mentions.rst b/specification/modules/mentions.rst new file mode 100644 index 00000000..4501b776 --- /dev/null +++ b/specification/modules/mentions.rst @@ -0,0 +1,74 @@ +.. 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. + +User, room, and group mentions +============================== + +.. _module:mentions: + +This module allows users to mention other users, rooms, and groups within +a room message. This is achieved by including a `matrix.to URI`_ in the HTML +body of an `m.room.message`_ event. This module does not have any server-specific +behaviour to it. + +Mentions apply only to `m.room.message`_ events where the ``msgtype`` is ``m.text``, +``m.emote``, or ``m.notice``. The ``format`` for the event must be ``org.matrix.custom.html`` +and therefore requires a ``formatted_body``. + +To make a mention, reference the entity being mentioned in the ``formatted_body`` +using an anchor, like so:: + + { + "body": "Hello Alice!", + "msgtype": "m.text", + "format": "org.matrix.custom.html", + "formatted_body": "Hello Alice!" + } + + +Client behaviour +---------------- + +In addition to using the appropriate ``matrix.to URI`` for the mention, +clients should use the following guidelines when making mentions in events +to be sent: + +* When mentioning users, use the user's potentially ambigious display name for + the anchor's text. If the user does not have a display name, use the user's + ID. + +* When mentioning rooms, use the canonical alias for the room. If the room + does not have a canonical alias, prefer one of the aliases listed on the + room. If no alias can be found, fall back to the room ID. In all cases, + use the alias/room ID being linked to as the anchor's text. + +* When referencing groups, use the group ID as the anchor's text. + +The text component of the anchor should be used in the event's ``body`` where +the mention would normally be represented, as shown in the example above. + +Clients should display mentions differently from other elements. For example, +this may be done by changing the background color of the mention to indicate +that it is different from a normal link. + +If the current user is mentioned in a message (either by a mention as defined +in this module or by a push rule), the client should show that mention differently +from other mentions, such as by using a red background color to signify to the +user that they were mentioned. + +When clicked, the mention should navigate the user to the appropriate room, group, +or user information. + + +.. _`matrix.to URI`: ../appendices.html#matrix-to-navigation \ No newline at end of file diff --git a/specification/modules/push.rst b/specification/modules/push.rst index e9ee8c90..4a6dfb10 100644 --- a/specification/modules/push.rst +++ b/specification/modules/push.rst @@ -124,7 +124,7 @@ There are different "kinds" of push rules and each rule has an associated priority. Every push rule MUST have a ``kind`` and ``rule_id``. The ``rule_id`` is a unique string within the kind of rule and its' scope: ``rule_ids`` do not need to be unique between rules of the same kind on different devices. Rules may -have extra keys depending on the value of ``kind``.The different kinds of rule +have extra keys depending on the value of ``kind``. The different kinds of rule in descending order of priority are: Override Rules ``override`` @@ -369,6 +369,41 @@ Definition: } +``.m.rule.roomnotif`` +````````````````````` +Matches any message whose content is unencrypted and contains the +text ``@room``, signifying the whole room should be notified of +the event. + +Definition: + +.. code:: json + + { + "rule_id": ".m.rule.roomnotif", + "default": true, + "enabled": true, + "conditions": [ + { + "kind": "event_match", + "key": "content.body", + "pattern": "@room" + }, + { + "kind": "sender_notification_permission", + "key": "room" + } + ], + "actions": [ + "notify", + { + "set_tweak": "highlight", + "value": true + } + ] + } + + Default Content Rules ^^^^^^^^^^^^^^^^^^^^^ @@ -428,7 +463,47 @@ Definition: "value": false } ] - }, + } + +``.m.rule.encrypted_room_one_to_one`` +````````````````````````````````````` +Matches any encrypted event sent in a room with exactly two members. +Unlike other push rules, this rule cannot be matched against the content +of the event by nature of it being encrypted. This causes the rule to +be an "all or nothing" match where it either matches *all* events that +are encrypted (in 1:1 rooms) or none. + +Definition: + +.. code:: json + + { + "rule_id": ".m.rule.encrypted_room_one_to_one", + "default": true, + "enabled": true, + "conditions": [ + { + "kind": "room_member_count", + "is": "2" + } + ], + "actions": [ + "notify", + { + "set_tweak": "sound", + "value": "default" + }, + { + "set_tweak": "highlight", + "value": false + }, + { + "kind": "event_match", + "key": "type", + "pattern": "m.room.encrypted" + } + ] + } ``.m.rule.room_one_to_one`` ``````````````````````````` @@ -489,6 +564,37 @@ Definition: ] } +``.m.rule.encrypted`` +````````````````````` +Matches all encrypted events. Unlike other push rules, this rule cannot +be matched against the content of the event by nature of it being encrypted. +This causes the rule to be an "all or nothing" match where it either +matches *all* events that are encrypted (in 1:1 rooms) or none. + +Definition: + +.. code:: json + + { + "rule_id": ".m.rule.encrypted", + "default": true, + "enabled": true, + "conditions": [ + { + "kind": "event_match", + "key": "type", + "pattern": "m.room.encrypted" + } + ], + "actions": [ + "notify", + { + "set_tweak": "highlight", + "value": false + } + ] + } + Conditions ++++++++++ @@ -523,6 +629,21 @@ rule determines its behaviour. The following conditions are defined: count is strictly less than the given number and so forth. If no prefix is present, this parameter defaults to ``==``. +``sender_notification_permission`` + This takes into account the current power levels in the room, ensuring the + sender of the event has high enough power to trigger the notification. + + Parameters: + + * ``key``: A string that determines the power level the sender must have to trigger + notifications of a given type, such as ``room``. Refer to the `m.room.power_levels`_ + event schema for information about what the defaults are and how to interpret the event. + The ``key`` is used to look up the power level required to send a notification type + from the ``notifications`` object in the power level event content. + +Unrecognised conditions MUST NOT match any events, effectively making the push +rule disabled. + Push Rules: API ~~~~~~~~~~~~~~~ @@ -623,4 +744,4 @@ should send a "sync" command to instruct the client to get new events from the homeserver directly. -.. _`Push Gateway Specification`: ../push_gateway/unstable.html +.. _`Push Gateway Specification`: ../push_gateway/%PUSH_GATEWAY_RELEASE_LABEL%.html diff --git a/specification/modules/receipts.rst b/specification/modules/receipts.rst index a6d8cbf7..faba7b62 100644 --- a/specification/modules/receipts.rst +++ b/specification/modules/receipts.rst @@ -38,11 +38,10 @@ single ``event_id``. Client behaviour ---------------- -In ``/initialSync``, receipts are listed in a separate top level ``receipts`` -key. In ``/sync``, receipts are contained in the ``ephemeral`` block for a -room. New receipts that come down the event streams are deltas which update -existing mappings. Clients should replace older receipt acknowledgements based -on ``user_id`` and ``receipt_type`` pairs. For example:: +In ``/sync``, receipts are listed under the ``ephemeral`` array of events +for a given room. New receipts that come down the event streams are deltas +which update existing mappings. Clients should replace older receipt acknowledgements +based on ``user_id`` and ``receipt_type`` pairs. For example:: Client receives m.receipt: user = @alice:example.com diff --git a/specification/modules/send_to_device.rst b/specification/modules/send_to_device.rst index 232becae..86288546 100644 --- a/specification/modules/send_to_device.rst +++ b/specification/modules/send_to_device.rst @@ -63,7 +63,7 @@ If the client sends messages to users on remote domains, those messages should be sent on to the remote servers via `federation`_. -.. _`federation`: ../server_server/latest.html#send-to-device-messages +.. _`federation`: ../server_server/latest.html#send-to-device-messaging .. TODO-spec: diff --git a/specification/modules/server_acls.rst b/specification/modules/server_acls.rst new file mode 100644 index 00000000..2b2d8f35 --- /dev/null +++ b/specification/modules/server_acls.rst @@ -0,0 +1,70 @@ +.. 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. + +Server Access Control Lists (ACLs) for rooms +============================================ + +.. _module:server-acls: + +In some scenarios room operators may wish to prevent a malicious or untrusted +server from participating in their room. Sending an `m.room.server_acl`_ state +event into a room is an effective way to prevent the server from participating +in the room at the federation level. + +Server ACLs can also be used to make rooms only federate with a limited set of +servers, or retroactively make the room no longer federate with any other server, +similar to setting the ``m.federate`` value on the `m.room.create`_ event. + +{{m_room_server_acl_event}} + +.. Note:: + Port numbers are not supported because it is unclear to parsers whether a + port number should be matched or an IP address literal. Additionally, it + is unlikely that one would trust a server running on a particular domain's + port but not a different port, especially considering the server host can + easily change ports. + +.. Note:: + CIDR notation is not supported for IP addresses because Matrix does not + encourage the use of IPs for identifying servers. Instead, a blanket + ``allow_ip_literals`` is provided to cover banning them. + +Client behaviour +---------------- +Clients are not expected to perform any additional duties beyond sending the +event. Clients should describe changes to the server ACLs to the user in the +user interface, such as in the timeline. + +Clients may wish to kick affected users from the room prior to denying a server +access to the room to help prevent those servers from participating and to +provide feedback to the users that they have been excluded from the room. + +Server behaviour +---------------- +Servers MUST prevent blacklisted servers from sending events or participating +in the room when an `m.room.server_acl`_ event is present in the room state. +Which APIs are specifically affected are described in the Server-Server API +specification. + +Servers should still send events to denied servers if they are still residents +of the room. + + +Security considerations +----------------------- +Server ACLs are only effective if every server in the room honours them. Servers +that do not honour the ACLs may still permit events sent by denied servers into +the room, leaking them to other servers in the room. To effectively enforce an +ACL in a room, the servers that do not honour the ACLs should be denied in the +room as well. \ No newline at end of file diff --git a/specification/push_gateway.rst b/specification/push_gateway.rst index e4a9d6ea..e4623887 100644 --- a/specification/push_gateway.rst +++ b/specification/push_gateway.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. @@ -21,13 +22,27 @@ the homeserver. This is managed by a distinct entity called the Push Gateway. .. contents:: Table of Contents .. sectnum:: -Specification version ---------------------- +Changelog +--------- + +.. topic:: Version: %PUSH_GATEWAY_RELEASE_LABEL% +{{push_gateway_changelog}} This version of the specification is generated from `matrix-doc `_ as of Git commit `{{git_version}} `_. +For the full historical changelog, see +https://github.com/matrix-org/matrix-doc/blob/master/changelogs/push_gateway.rst + +Other versions of this specification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following other versions are also available, in reverse chronological order: + +- `HEAD `_: Includes all changes since the latest versioned release. +- `r0.1.0 `_ + Overview -------- diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index dbde8b10..983da62f 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -26,9 +26,9 @@ to communicate with each other. Homeservers use these APIs to push messages 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 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 +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: @@ -71,39 +71,41 @@ This version of the specification is generated from `matrix-doc `_ as of Git commit `{{git_version}} `_. -Server Discovery +Server discovery ---------------- -Resolving Server Names +Resolving server names ~~~~~~~~~~~~~~~~~~~~~~ Each matrix homeserver is identified by a server name consisting of a hostname -and an optional TLS port. - -.. code:: - - 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 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 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. +and an optional port, as described by the `grammar +<../appendices.html#server-name>`_. Server names should be resolved to an IP +address and port using the following process: + +* If the hostname is an IP literal, then that IP address should be used, + together with the given port number, or 8448 if no port is given. + +* Otherwise, if the port is present, then an IP address is discovered by + looking up an AAAA or A record for the hostname, and the specified port is + used. + +* If the hostname is not an IP literal and no port is given, the server is + discovered by first looking up a ``_matrix._tcp`` SRV record for the + hostname, which may give a hostname (to be looked up using AAAA or A queries) + and port. If the SRV record does not exist, then the server is discovered by + looking up an AAAA or A record on the hostname and taking the default + fallback port number of 8448. + + Homeservers may use SRV records to load balance requests between multiple TLS + endpoints or to failover to another endpoint if an endpoint fails. + +When making requests to servers, use the hostname of the target server in the +``Host`` header, regardless of any hostname given in the SRV record. For +example, if the server name is ``example.org``, and the SRV record resolves to +``matrix.example.org``, the ``Host`` header in the request should be +``example.org``. If an explicit port was given in the server name, it should be +included in the ``Host`` header; otherwise, no port number should be given in +the ``Host`` header. Server implementation ~~~~~~~~~~~~~~~~~~~~~~ @@ -121,7 +123,7 @@ Retrieving Server Keys 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/{serverName}/{keyId}`` API. Intermediate notary servers +``/_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. @@ -262,6 +264,8 @@ 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. +Transactions are limited in size; they can have at most 50 PDUs and 100 EDUs. + {{transactions_ss_http_api}} PDUs @@ -590,9 +594,9 @@ 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 +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. Similar to backfilling a room's history, a server may not have all the events @@ -669,10 +673,10 @@ homeservers, though most in practice will use just two. The first part of the handshake usually involves using the directory server to 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 +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 +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 @@ -692,7 +696,7 @@ 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. The resident server must also send the event to other servers -participating in the room. +participating in the room. {{joins_ss_http_api}} @@ -716,8 +720,8 @@ Leaving Rooms (Rejecting Invites) 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 +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. @@ -829,7 +833,7 @@ EDUs. There are no PDUs or Federation Queries involved. Servers should only send presence updates for users that the receiving server would be interested in. This can include the receiving server sharing a room -with a given user, or a user on the receiving server has added one of the +with a given user, or a user on the receiving server has added one of the sending server's users to their presence list. Clients may define lists of users that they are interested in via "Presence @@ -848,7 +852,7 @@ or ``m.presence_deny`` EDU back. {{definition_ss_event_schemas_m_presence_invite}} -{{definition_ss_event_schemas_m_presence_accept}} +{{definition_ss_event_schemas_m_presence_accept}} {{definition_ss_event_schemas_m_presence_deny}} @@ -881,11 +885,11 @@ that can be made. OpenID ------ -Third party services can exchange an access token previously generated by the +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 +Access tokens generated by the OpenID API are only good for the OpenID API and nothing else. {{openid_ss_http_api}} @@ -929,6 +933,32 @@ described in the `Client-Server API`_, being sure to use the ``allow_remote`` parameter (set to ``false``). +Server Access Control Lists (ACLs) +---------------------------------- + +Server ACLs and their purpose are described in the `Server ACLs`_ section of the +Client-Server API. + +When a remote server makes a request, it MUST be verified to be allowed by the +server ACLs. If the server is denied access to a room, the receiving server +MUST reply with a 403 HTTP status code and an ``errcode`` of ``M_FORBIDDEN``. + +The following endpoint prefixes MUST be protected: + +* ``/_matrix/federation/v1/send`` (on a per-PDU basis) +* ``/_matrix/federation/v1/make_join`` +* ``/_matrix/federation/v1/make_leave`` +* ``/_matrix/federation/v1/send_join`` +* ``/_matrix/federation/v1/send_leave`` +* ``/_matrix/federation/v1/invite`` +* ``/_matrix/federation/v1/state`` +* ``/_matrix/federation/v1/state_ids`` +* ``/_matrix/federation/v1/backfill`` +* ``/_matrix/federation/v1/event_auth`` +* ``/_matrix/federation/v1/query_auth`` +* ``/_matrix/federation/v1/get_missing_events`` + + Signing Events -------------- @@ -1081,3 +1111,4 @@ that are too long. .. _`Inviting to a room`: #inviting-to-a-room .. _`Canonical JSON`: ../appendices.html#canonical-json .. _`Unpadded Base64`: ../appendices.html#unpadded-base64 +.. _`Server ACLs`: ../client_server/unstable.html#module-server-acls diff --git a/specification/targets.yaml b/specification/targets.yaml index 5480bbc5..56e9ec34 100644 --- a/specification/targets.yaml +++ b/specification/targets.yaml @@ -13,7 +13,7 @@ targets: application_service: files: - application_service_api.rst - version_label: unstable + version_label: "%APPSERVICE_RELEASE_LABEL%" server_server: files: - server_server_api.rst @@ -25,7 +25,7 @@ targets: push_gateway: files: - push_gateway.rst - version_label: unstable + version_label: "%PUSH_GATEWAY_RELEASE_LABEL%" appendices: files: - appendices.rst @@ -67,6 +67,8 @@ groups: # reusable blobs of files when prefixed with 'group:' - modules/report_content.rst - modules/third_party_networks.rst - modules/openid.rst + - modules/server_acls.rst + - modules/mentions.rst title_styles: ["=", "-", "~", "+", "^", "`", "@", ":"]