diff --git a/api/client-server/definitions/event_batch.yaml b/api/client-server/definitions/event_batch.yaml index 21377a41..d169c355 100644 --- a/api/client-server/definitions/event_batch.yaml +++ b/api/client-server/definitions/event_batch.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,10 +14,10 @@ # limitations under the License. properties: events: - description: List of events + description: List of events. items: allOf: - - $ref: event.yaml + - $ref: event-schemas/schema/core-event-schema/event.yaml type: object type: array type: object diff --git a/api/client-server/definitions/room_event_batch.yaml b/api/client-server/definitions/room_event_batch.yaml new file mode 100644 index 00000000..7367198f --- /dev/null +++ b/api/client-server/definitions/room_event_batch.yaml @@ -0,0 +1,27 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +properties: + events: + description: List of events. + items: + allOf: + - $ref: event-schemas/schema/core-event-schema/sync_room_event.yaml + type: object + required: + - event_id + #- room_id - Not in /sync + - sender + - origin_server_ts + type: array +type: object diff --git a/api/client-server/definitions/state_event_batch.yaml b/api/client-server/definitions/state_event_batch.yaml new file mode 100644 index 00000000..db01ecb1 --- /dev/null +++ b/api/client-server/definitions/state_event_batch.yaml @@ -0,0 +1,28 @@ +# Copyright 2018 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +properties: + events: + description: List of events. + items: + allOf: + - $ref: event-schemas/schema/core-event-schema/sync_state_event.yaml + type: object + required: + - event_id + #- room_id - Not in /sync + - sender + - origin_server_ts + - state_key + type: array +type: object diff --git a/api/client-server/definitions/timeline_batch.yaml b/api/client-server/definitions/timeline_batch.yaml index ce613ac4..abf93830 100644 --- a/api/client-server/definitions/timeline_batch.yaml +++ b/api/client-server/definitions/timeline_batch.yaml @@ -1,4 +1,5 @@ # Copyright 2016 OpenMarket Ltd +# Copyright 2018 New Vector Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,14 +13,14 @@ # See the License for the specific language governing permissions and # limitations under the License. allOf: -- $ref: event_batch.yaml +- $ref: room_event_batch.yaml properties: limited: description: True if the number of events returned was limited by the ``limit`` - on the filter + on the filter. type: boolean prev_batch: description: A token that can be supplied to the ``from`` parameter of the - rooms/{roomId}/messages endpoint + rooms/{roomId}/messages endpoint. type: string type: object diff --git a/api/client-server/sync.yaml b/api/client-server/sync.yaml index 21af0d1e..bb514bbe 100644 --- a/api/client-server/sync.yaml +++ b/api/client-server/sync.yaml @@ -135,7 +135,7 @@ paths: ``timeline``, if ``since`` is not given, or ``full_state`` is true). allOf: - - $ref: "definitions/event_batch.yaml" + - $ref: "definitions/state_event_batch.yaml" timeline: title: Timeline type: object @@ -202,8 +202,35 @@ paths: the room then the current state will be given as a delta against the archived ``state`` not the ``invite_state``. - allOf: - - $ref: "definitions/event_batch.yaml" + properties: + events: + description: The StrippedState events that form the invite state. + items: + description: |- + A stripped down state event, with only the ``type``, ``state_key``, + ``sender``, and ``content`` keys. + properties: + content: + description: The ``content`` for the event. + title: EventContent + type: object + state_key: + description: The ``state_key`` for the event. + type: string + type: + description: The ``type`` for the event. + type: string + sender: + description: The ``sender`` for the event. + type: string + required: + - type + - state_key + - content + - sender + title: StrippedState + type: object + type: array leave: title: Left rooms type: object @@ -219,7 +246,7 @@ paths: description: |- The state updates for the room up to the start of the timeline. allOf: - - $ref: "definitions/event_batch.yaml" + - $ref: "definitions/state_event_batch.yaml" timeline: title: Timeline type: object @@ -270,6 +297,8 @@ paths: description: |- Information on end-to-end encryption keys, as specified in |device_lists_sync|_. + required: + - next_batch examples: application/json: { "next_batch": "s72595_4483_1934", @@ -321,7 +350,6 @@ paths: { "sender": "@alice:example.com", "type": "m.room.message", - "age": 124524, "txn_id": "1234", "content": { "body": "I am a fish", diff --git a/api/client-server/tags.yaml b/api/client-server/tags.yaml index b7bafab6..081d8a84 100644 --- a/api/client-server/tags.yaml +++ b/api/client-server/tags.yaml @@ -43,14 +43,14 @@ paths: required: true description: |- The id of the user to get tags for. The access token must be - authorized to make requests for this user id. + authorized to make requests for this user ID. x-example: "@alice:example.com" - in: path type: string name: roomId required: true description: |- - The id of the room to get tags for. + The ID of the room to get tags for. x-example: "!726s6s6q:example.com" responses: 200: @@ -60,16 +60,26 @@ paths: type: object properties: tags: - title: Tags type: object + additionalProperties: + title: Tag + type: object + properties: + order: + type: number + format: float + description: |- + A number in a range ``[0,1]`` describing a relative + position of the room under the given tag. + additionalProperties: true examples: application/json: { - "tags": { - "m.favourite": {}, - "u.Work": {"order": "1"}, - "u.Customers": {} - } + "tags": { + "m.favourite": {"order": 0.1}, + "u.Work": {"order": 0.7}, + "u.Customers": {} } + } tags: - User data "/user/{userId}/rooms/{roomId}/tags/{tag}": @@ -87,14 +97,14 @@ paths: required: true description: |- The id of the user to add a tag for. The access token must be - authorized to make requests for this user id. + authorized to make requests for this user ID. x-example: "@alice:example.com" - in: path type: string name: roomId required: true description: |- - The id of the room to add a tag to. + The ID of the room to add a tag to. x-example: "!726s6s6q:example.com" - in: path type: string @@ -102,7 +112,7 @@ paths: required: true description: |- The tag to add. - x-example: "work" + x-example: "u.work" - in: body name: body required: true @@ -110,8 +120,17 @@ paths: Extra data for the tag, e.g. ordering. schema: type: object + properties: + order: + type: number + format: float + description: |- + A number in a range ``[0,1]`` describing a relative + position of the room under the given tag. + additionalProperties: true example: { - "order": "1"} + "order": 0.25 + } responses: 200: description: @@ -119,8 +138,7 @@ paths: schema: type: object examples: - application/json: { - } + application/json: {} tags: - User data delete: @@ -137,14 +155,14 @@ paths: required: true description: |- The id of the user to remove a tag for. The access token must be - authorized to make requests for this user id. + authorized to make requests for this user ID. x-example: "@alice:example.com" - in: path type: string name: roomId required: true description: |- - The id of the room to remove a tag from. + The ID of the room to remove a tag from. x-example: "!726s6s6q:example.com" - in: path type: string @@ -152,15 +170,14 @@ paths: required: true description: |- The tag to remove. - x-example: "work" + x-example: "u.work" responses: 200: description: - The tag was successfully removed + The tag was successfully removed. schema: type: object examples: - application/json: { - } + application/json: {} tags: - User data diff --git a/api/client-server/users.yaml b/api/client-server/users.yaml index fc6d233b..98744719 100644 --- a/api/client-server/users.yaml +++ b/api/client-server/users.yaml @@ -31,8 +31,16 @@ paths: post: summary: Searches the user directory. description: |- - This API performs a server-side search over all users registered on the server. - It searches user ID and displayname case-insensitively for users that you share a room with or that are in public rooms. + Performs a search for users on the homeserver. The homeserver may + determine which subset of users are searched, however the homeserver + MUST at a minimum consider the users the requesting user shares a + room with and those who reside in public rooms (known to the homeserver). + The search MUST consider local users to the homeserver, and SHOULD + query remote users as part of the search. + + The search is performed case-insensitively on user IDs and display + names preferably using a collation determined based upon the + ``Accept-Language`` header provided in the request, if present. operationId: searchUserDirectory security: - accessToken: [] @@ -48,7 +56,7 @@ paths: example: "foo" limit: type: integer - description: The maximum number of results to return (Defaults to 10). + description: The maximum number of results to return. Defaults to 10. example: 10 required: ["search_term"] responses: @@ -56,15 +64,15 @@ paths: description: The results of the search. examples: application/json: { - "results": [ - { - "user_id": "@foo:bar.com", - "display_name": "Foo", - "avatar_url": "mxc://bar.com/foo" - } - ], - "limited": false - } + "results": [ + { + "user_id": "@foo:bar.com", + "display_name": "Foo", + "avatar_url": "mxc://bar.com/foo" + } + ], + "limited": false + } schema: type: object required: ["results", "limited"] diff --git a/api/identity/pubkey.yaml b/api/identity/pubkey.yaml index 6b17e7c6..e657c61c 100644 --- a/api/identity/pubkey.yaml +++ b/api/identity/pubkey.yaml @@ -69,7 +69,8 @@ paths: get: summary: Check whether a long-term public key is valid. description: |- - Check whether a long-term public key is valid. + Check whether a long-term public key is valid. The response should always + be the same, provided the key exists. operationId: isPubKeyValid parameters: - in: query diff --git a/api/server-server/third_party_invite.yaml b/api/server-server/third_party_invite.yaml index 5c12247c..9def527d 100644 --- a/api/server-server/third_party_invite.yaml +++ b/api/server-server/third_party_invite.yaml @@ -194,3 +194,126 @@ paths: type: object description: An empty object example: {} + "/3pid/onbind": + put: + summary: |- + Notifies the server that a third party identifier has been bound to one + of its users. + description: |- + Used by Identity Servers to notify the homeserver that one of its users + has bound a third party identifier successfully, including any pending + room invites the Identity Server has been made aware of. + operationId: onBindThirdPartyIdentifier + parameters: + - in: body + name: body + type: object + required: true + schema: + type: object + properties: + medium: + type: string + description: |- + The type of third party identifier. Currently only "email" is + a possible value. + example: "email" + address: + type: string + description: |- + The third party identifier itself. For example, an email address. + example: "alice@domain.com" + mxid: + type: string + description: The user that is now bound to the third party identifier. + example: "@alice:matrix.org" + invites: + type: array + description: |- + A list of pending invites that the third party identifier has received. + items: + type: object + title: Third Party Invite + properties: + medium: + type: string + description: |- + The type of third party invite issues. Currently only + "email" is used. + example: "email" + address: + type: string + description: |- + The third party identifier that received the invite. + example: "alice@domain.com" + mxid: + type: string + description: The now-bound user ID that received the invite. + example: "@alice:matrix.org" + room_id: + type: string + description: The room ID the invite is valid for. + example: "!somewhere:example.org" + sender: + type: string + description: The user ID that sent the invite. + example: "@bob:matrix.org" + # TODO (TravisR): Make this reusable when doing IS spec changes + # also make sure it isn't lying about anything, like the key version + signed: + type: object + title: Identity Server Signatures + description: |- + Signature from the Identity Server using a long-term private + key. + properties: + mxid: + type: string + description: |- + The user ID that has been bound to the third party + identifier. + example: "@alice:matrix.org" + token: + type: string + # TODO: What is this actually? + description: A token. + example: "Hello World" + signatures: + type: object + title: Identity Server Signature + description: |- + The signature from the identity server. The ``string`` key + is the identity server's domain name, such as vector.im + additionalProperties: + type: object + title: Identity Server Domain Signature + description: The signature for the identity server. + properties: + "ed25519:0": + type: string + description: The signature. + example: "SomeSignatureGoesHere" + required: ['ed25519:0'] + example: { + "vector.im": { + "ed25519:0": "SomeSignatureGoesHere" + } + } + required: ['mxid', 'token', 'signatures'] + required: + - medium + - address + - mxid + - room_id + - sender + - signed + required: ['medium', 'address', 'mxid', 'invites'] + responses: + 200: + description: The homeserver has processed the notification. + examples: + application/json: {} + schema: + type: object + description: An empty object + example: {} diff --git a/changelogs/application_service.rst b/changelogs/application_service.rst index e69de29b..9d098837 100644 --- a/changelogs/application_service.rst +++ b/changelogs/application_service.rst @@ -0,0 +1,7 @@ +r0.1.0 +====== + +This is the first release of the Application Service specification. It +includes support for application services being able to interact with +homeservers and bridge third party networks, such as IRC, over to Matrix +in a standard and accessible way. diff --git a/changelogs/client_server/newsfragments/1569.clarification b/changelogs/client_server/newsfragments/1569.clarification new file mode 100644 index 00000000..83185e02 --- /dev/null +++ b/changelogs/client_server/newsfragments/1569.clarification @@ -0,0 +1 @@ +Clarify the homeserver's behaviour for searching users. diff --git a/changelogs/client_server/newsfragments/1573.clarification b/changelogs/client_server/newsfragments/1573.clarification new file mode 100644 index 00000000..74efa28f --- /dev/null +++ b/changelogs/client_server/newsfragments/1573.clarification @@ -0,0 +1 @@ +Clarify the event fields used in the ``/sync`` response. diff --git a/changelogs/client_server/newsfragments/1600.feature b/changelogs/client_server/newsfragments/1600.feature new file mode 100644 index 00000000..142a67b2 --- /dev/null +++ b/changelogs/client_server/newsfragments/1600.feature @@ -0,0 +1 @@ +Recommend that servers set a Content Security Policy for the content repository. diff --git a/changelogs/client_server/newsfragments/1602.clarification b/changelogs/client_server/newsfragments/1602.clarification new file mode 100644 index 00000000..def503cb --- /dev/null +++ b/changelogs/client_server/newsfragments/1602.clarification @@ -0,0 +1 @@ +Add the other keys that redactions are expected to preserve. diff --git a/changelogs/client_server/newsfragments/1605.clarification b/changelogs/client_server/newsfragments/1605.clarification new file mode 100644 index 00000000..ce9f967d --- /dev/null +++ b/changelogs/client_server/newsfragments/1605.clarification @@ -0,0 +1 @@ +Clarify that clients should not be generating invalid HTML for formatted events. diff --git a/changelogs/client_server/newsfragments/1606.clarification b/changelogs/client_server/newsfragments/1606.clarification new file mode 100644 index 00000000..f65ed257 --- /dev/null +++ b/changelogs/client_server/newsfragments/1606.clarification @@ -0,0 +1 @@ +Clarify the room tag structure (thanks @KitsuneRal!) diff --git a/changelogs/client_server/newsfragments/1619.clarification b/changelogs/client_server/newsfragments/1619.clarification new file mode 100644 index 00000000..fa06e969 --- /dev/null +++ b/changelogs/client_server/newsfragments/1619.clarification @@ -0,0 +1 @@ +Add a note that clients may use the transaction ID to avoid flickering when doing local echo. diff --git a/event-schemas/examples/m.tag b/event-schemas/examples/m.tag index 0d61d91b..7e56b1ea 100644 --- a/event-schemas/examples/m.tag +++ b/event-schemas/examples/m.tag @@ -3,7 +3,7 @@ "type": "m.tag", "content": { "tags": { - "u.work": {"order": 1} + "u.work": {"order": 0.9} } } } diff --git a/event-schemas/schema/core-event-schema/event.yaml b/event-schemas/schema/core-event-schema/event.yaml index 7a060283..0fe5ac6c 100644 --- a/event-schemas/schema/core-event-schema/event.yaml +++ b/event-schemas/schema/core-event-schema/event.yaml @@ -10,5 +10,6 @@ properties: type: string required: - type + - content title: Event type: object diff --git a/event-schemas/schema/core-event-schema/room_event.yaml b/event-schemas/schema/core-event-schema/room_event.yaml index ebf970ad..007372a5 100644 --- a/event-schemas/schema/core-event-schema/room_event.yaml +++ b/event-schemas/schema/core-event-schema/room_event.yaml @@ -1,46 +1,14 @@ allOf: -- $ref: event.yaml +- $ref: sync_room_event.yaml description: In addition to the Event fields, Room Events have the following additional fields. properties: - event_id: - description: The globally unique event identifier. - type: string room_id: - description: The ID of the room associated with this event. - type: string - sender: - description: Contains the fully-qualified ID of the user who *sent* - this event. + description: |- + The ID of the room associated with this event. Will not be present on events + that arrive through ``/sync``, despite being required everywhere else. type: string - origin_server_ts: - description: Timestamp in milliseconds on originating homeserver - when this event was sent. - type: integer - format: int64 - unsigned: - description: Contains optional extra information about the event. - properties: - age: - description: The time in milliseconds that has elapsed since the event was - sent. This field is generated by the local homeserver, and may be incorrect - if the local time on at least one of the two servers is out of sync, which can - cause the age to either be negative or greater than it actually is. - type: integer - redacted_because: - description: Optional. The event that redacted this event, if any. - title: Event - type: object - transaction_id: - description: The client-supplied transaction ID, if the client being given - the event is the same one which sent it. - type: string - title: UnsignedData - type: object required: -- event_id - room_id -- sender -- origin_server_ts title: Room Event type: object diff --git a/event-schemas/schema/core-event-schema/state_event.yaml b/event-schemas/schema/core-event-schema/state_event.yaml index 71c4137b..37d4426f 100644 --- a/event-schemas/schema/core-event-schema/state_event.yaml +++ b/event-schemas/schema/core-event-schema/state_event.yaml @@ -1,23 +1,7 @@ allOf: - $ref: room_event.yaml +- $ref: sync_state_event.yaml description: In addition to the Room Event fields, State Events have the following additional fields. -properties: - prev_content: - description: Optional. The previous ``content`` for this event. If there is no - previous content, this key will be missing. - title: EventContent - type: object - 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. - - 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 title: State Event type: object diff --git a/event-schemas/schema/core-event-schema/sync_room_event.yaml b/event-schemas/schema/core-event-schema/sync_room_event.yaml new file mode 100644 index 00000000..8006148f --- /dev/null +++ b/event-schemas/schema/core-event-schema/sync_room_event.yaml @@ -0,0 +1,60 @@ +# 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. + +# Note: this is technically not a core event schema, however it is included here +# to keep things sane. The short story is that /sync doesn't require a room_id to +# be on events, so we give it a whole event structure as a base for room_event. +# This base doesn't declare a room_id, which instead appears in the room_event. + +allOf: +- $ref: event.yaml +description: In addition to the Event fields, Room Events have the following additional + fields. +properties: + event_id: + description: The globally unique event identifier. + type: string + sender: + description: Contains the fully-qualified ID of the user who sent this event. + type: string + origin_server_ts: + description: Timestamp in milliseconds on originating homeserver + when this event was sent. + type: integer + format: int64 + unsigned: + description: Contains optional extra information about the event. + properties: + age: + description: The time in milliseconds that has elapsed since the event was + sent. This field is generated by the local homeserver, and may be incorrect + if the local time on at least one of the two servers is out of sync, which can + cause the age to either be negative or greater than it actually is. + type: integer + redacted_because: + description: Optional. The event that redacted this event, if any. + title: Event + type: object + transaction_id: + description: The client-supplied transaction ID, if the client being given + the event is the same one which sent it. + type: string + title: UnsignedData + type: object +required: +- event_id +- sender +- origin_server_ts +title: Room Event +type: object diff --git a/event-schemas/schema/core-event-schema/sync_state_event.yaml b/event-schemas/schema/core-event-schema/sync_state_event.yaml new file mode 100644 index 00000000..1b6ce9b2 --- /dev/null +++ b/event-schemas/schema/core-event-schema/sync_state_event.yaml @@ -0,0 +1,39 @@ +# 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. + +# See sync_room_event.yaml for why this file is here. + +allOf: +- $ref: sync_room_event.yaml +description: In addition to the Room Event fields, State Events have the following + additional fields. +properties: + prev_content: + description: Optional. The previous ``content`` for this event. If there is no + previous content, this key will be missing. + title: EventContent + type: object + 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. + + 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 +title: State Event +type: object diff --git a/event-schemas/schema/m.tag b/event-schemas/schema/m.tag index 80d3f9dd..8da093bd 100644 --- a/event-schemas/schema/m.tag +++ b/event-schemas/schema/m.tag @@ -18,7 +18,15 @@ "description": "The tags on the room and their contents.", "additionalProperties": { "title": "Tag", - "type": "object" + "type": "object", + "properties": { + "order": { + "type": "number", + "format": "float", + "description": + "A number in a range ``[0,1]`` describing a relative position of the room under the given tag." + } + } } } } diff --git a/scripts/templating/matrix_templates/sections.py b/scripts/templating/matrix_templates/sections.py index 4c07649d..f4c015c7 100644 --- a/scripts/templating/matrix_templates/sections.py +++ b/scripts/templating/matrix_templates/sections.py @@ -207,6 +207,13 @@ class MatrixSections(Sections): apis = self.units.get("apis") return template.render(apis=apis) + def render_unstable_warnings(self): + rendered = {} + blocks = self.units.get("unstable_warnings") + for var, text in blocks.items(): + rendered["unstable_warning_block_" + var] = text + return rendered + def render_swagger_definition(self): rendered = {} template = self.env.get_template("schema-definition.tmpl") diff --git a/scripts/templating/matrix_templates/units.py b/scripts/templating/matrix_templates/units.py index 94b535b5..11a9d441 100644 --- a/scripts/templating/matrix_templates/units.py +++ b/scripts/templating/matrix_templates/units.py @@ -971,6 +971,22 @@ class MatrixUnits(Units): return changelogs + def load_unstable_warnings(self, substitutions): + warning = """ +.. WARNING:: + You are viewing an unstable version of this specification. Unstable + specifications may change at any time without notice. To view the + current specification, please `click here `_. +""" + warnings = {} + for var in substitutions.keys(): + key = var[1:-1] # take off the surrounding %-signs + if substitutions.get(var, "unstable") == "unstable": + warnings[key] = warning + else: + warnings[key] = "" + return warnings + def load_spec_targets(self): with open(TARGETS, "r") as f: diff --git a/specification/application_service_api.rst b/specification/application_service_api.rst index 69d39d21..8af10df8 100644 --- a/specification/application_service_api.rst +++ b/specification/application_service_api.rst @@ -16,6 +16,8 @@ Application Service API ======================= +{{unstable_warning_block_APPSERVICE_RELEASE_LABEL}} + The Matrix client-server API and server-server APIs provide the means to implement a consistent self-contained federated messaging fabric. However, they provide limited means of implementing custom server-side behaviour in Matrix diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst index 489b8e0b..009c9df0 100644 --- a/specification/client_server_api.rst +++ b/specification/client_server_api.rst @@ -15,6 +15,8 @@ Client-Server API ================= +{{unstable_warning_block_CLIENT_RELEASE_LABEL}} + The client-server API provides a simple lightweight API to let clients send messages, control rooms and synchronise conversation history. It is designed to support both lightweight clients which store no state and lazy-load data from @@ -1388,16 +1390,30 @@ the following list: - ``state_key`` - ``prev_content`` - ``content`` +- ``hashes`` +- ``signatures`` +- ``depth`` +- ``prev_events`` +- ``prev_state`` +- ``auth_events`` +- ``origin`` +- ``origin_server_ts`` +- ``membership`` + +.. Note: + Some of the keys, such as ``hashes``, will appear on the federation-formatted + event and therefore the client may not be aware of them. The content object should also be stripped of all keys, unless it is one of one of the following event types: -- ``m.room.member`` allows key ``membership`` -- ``m.room.create`` allows key ``creator`` -- ``m.room.join_rules`` allows key ``join_rule`` +- ``m.room.member`` allows key ``membership``. +- ``m.room.create`` allows key ``creator``. +- ``m.room.join_rules`` allows key ``join_rule``. - ``m.room.power_levels`` allows keys ``ban``, ``events``, ``events_default``, ``kick``, ``redact``, ``state_default``, ``users``, ``users_default``. -- ``m.room.aliases`` allows key ``aliases`` +- ``m.room.aliases`` allows key ``aliases``. +- ``m.room.history_visibility`` allows key ``history_visibility``. The server should add the event causing the redaction to the ``unsigned`` property of the redacted event, under the ``redacted_because`` key. When a diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst index 81ff0ede..33627f70 100644 --- a/specification/identity_service_api.rst +++ b/specification/identity_service_api.rst @@ -18,6 +18,8 @@ Identity Service API ==================== +{{unstable_warning_block_IDENTITY_RELEASE_LABEL}} + The Matrix client-server and server-server APIs are largely expressed in Matrix user identifiers. From time to time, it is useful to refer to users by other ("third-party") identifiers, or "3pid"s, e.g. their email address or phone @@ -179,9 +181,11 @@ An identity service has some long-term public-private keypairs. These are named in a scheme ``algorithm:identifier``, e.g. ``ed25519:0``. When signing an association, the standard `Signing JSON`_ algorithm applies. -In the event of key compromise, the identity service may revoke any of its keys. -An HTTP API is offered to get public keys, and check whether a particular key is -valid. +.. TODO: Actually allow identity services to revoke all keys + See: https://github.com/matrix-org/matrix-doc/issues/1633 +.. In the event of key compromise, the identity service may revoke any of its keys. + An HTTP API is offered to get public keys, and check whether a particular key is + valid. The identity service may also keep track of some short-term public-private keypairs, which may have different usage and lifetime characteristics than the diff --git a/specification/modules/content_repo.rst b/specification/modules/content_repo.rst index 0f1a9944..51cf999a 100644 --- a/specification/modules/content_repo.rst +++ b/specification/modules/content_repo.rst @@ -33,6 +33,10 @@ recipient's local homeserver, which must first transfer the content from the origin homeserver using the same API (unless the origin and destination homeservers are the same). +When serving content, the server SHOULD provide a ``Content-Security-Policy`` +header. The recommended policy is ``default-src 'none'; script-src 'none'; +plugin-types application/pdf; style-src 'unsafe-inline'; object-src 'self';``. + Client behaviour ---------------- diff --git a/specification/modules/instant_messaging.rst b/specification/modules/instant_messaging.rst index 079a4801..2845dba2 100644 --- a/specification/modules/instant_messaging.rst +++ b/specification/modules/instant_messaging.rst @@ -106,6 +106,12 @@ of tags they can render, falling back to other representations of the tags where For example, a client may not be able to render tables correctly and instead could fall back to rendering tab-delimited text. +In addition to not rendering unsafe HTML, clients should not emit unsafe HTML in events. +Likewise, clients should not generate HTML that is not needed, such as extra paragraph tags +surrounding text due to Rich Text Editors. HTML included in events should otherwise be valid, +such as having appropriate closing tags, appropriate attributes (considering the custom ones +defined in this specification), and generally valid structure. + .. Note:: A future iteration of the specification will support more powerful and extensible message formatting options, such as the proposal `MSC1225 `_. @@ -167,9 +173,14 @@ message which they receive from the event stream. The echo of the same message from the event stream is referred to as "remote echo". Both echoes need to be identified as the same message in order to prevent duplicate messages being displayed. Ideally this pairing would occur transparently to the user: the UI -would not flicker as it transitions from local to remote. Flickering cannot be -fully avoided in the current client-server API. Two scenarios need to be -considered: +would not flicker as it transitions from local to remote. Flickering can be +reduced through clients making use of the transaction ID they used to send +a particular event. The transaction ID used will be included in the event's +``unsigned`` data as ``transaction_id`` when it arrives through the event stream. + +Clients unable to make use of the transaction ID are more likely to experience +flickering due to the following two scenarios, however the effect can be mitigated +to a degree: - The client sends a message and the remote echo arrives on the event stream *after* the request to send the message completes. diff --git a/specification/modules/tags.rst b/specification/modules/tags.rst index f965c2e8..739ead2c 100644 --- a/specification/modules/tags.rst +++ b/specification/modules/tags.rst @@ -39,7 +39,7 @@ with an ``order`` of ``0.2`` would be displayed before a room with an ``order`` of ``0.7``. If a room has a tag without an ``order`` key then it should appear after the rooms with that tag that have an ``order`` key. -The name of a tag MUST not exceed 255 bytes. +The name of a tag MUST NOT exceed 255 bytes. The tag namespace is defined as follows: diff --git a/specification/modules/third_party_invites.rst b/specification/modules/third_party_invites.rst index 9ea0eb0b..248b9ba9 100644 --- a/specification/modules/third_party_invites.rst +++ b/specification/modules/third_party_invites.rst @@ -40,6 +40,7 @@ with ``content.membership`` = ``invite``, as well as a ``content.third_party_invite`` property which contains proof that the invitee does indeed own that third party identifier. + Events ------ @@ -55,41 +56,79 @@ A client asks a server to invite a user by their third party identifier. Server behaviour ---------------- -All homeservers MUST verify the signature in the event's -``content.third_party_invite.signed`` object. - -When a homeserver inserts an ``m.room.member`` ``invite`` event into the graph -because of an ``m.room.third_party_invite`` event, -that homesever MUST validate that the public -key used for signing is still valid, by checking ``key_validity_url`` from the ``m.room.third_party_invite``. It does -this by making an HTTP GET request to ``key_validity_url``: - -.. TODO: Link to identity server spec when it exists +Upon receipt of an ``/invite``, the server is expected to look up the third party +identifier with the provided identity server. If the lookup yields a result for +a Matrix User ID then the normal invite process can be initiated. This process +ends up looking like this: + +:: + + +---------+ +-------------+ +-----------------+ + | Client | | Homeserver | | IdentityServer | + +---------+ +-------------+ +-----------------+ + | | | + | POST /invite | | + |------------------------------------>| | + | | | + | | GET /lookup | + | |--------------------------------------------------->| + | | | + | | User ID result | + | |<---------------------------------------------------| + | | | + | | Invite process for the discovered User ID | + | |------------------------------------------ | + | | | | + | |<----------------------------------------- | + | | | + | Complete the /invite request | | + |<------------------------------------| | + | | | + + +However, if the lookup does not yield a bound User ID, the homeserver must store +the invite on the identity server and emit a valid ``m.room.third_party_invite`` +event to the room. This process ends up looking like this: + +:: + + +---------+ +-------------+ +-----------------+ + | Client | | Homeserver | | IdentityServer | + +---------+ +-------------+ +-----------------+ + | | | + | POST /invite | | + |------------------------------------>| | + | | | + | | GET /lookup | + | |-------------------------------------------------------------->| + | | | + | | "no users" result | + | |<--------------------------------------------------------------| + | | | + | | POST /store-invite | + | |-------------------------------------------------------------->| + | | | + | | Information needed for the m.room.third_party_invite | + | |<--------------------------------------------------------------| + | | | + | | Emit m.room.third_party_invite to the room | + | |------------------------------------------- | + | | | | + | |<------------------------------------------ | + | | | + | Complete the /invite request | | + |<------------------------------------| | + | | | -Schema:: - => GET $key_validity_url?public_key=$public_key - <= HTTP/1.1 200 OK - { - "valid": true|false - } - - -Example:: - - key_validity_url = https://identity.server/is_valid - public_key = ALJWLAFQfqffQHFqFfeqFUOEHf4AIHfefh4 - => GET https://identity.server/is_valid?public_key=ALJWLAFQfqffQHFqFfeqFUOEHf4AIHfefh4 - <= HTTP/1.1 200 OK - { - "valid": true - } +All homeservers MUST verify the signature in the event's +``content.third_party_invite.signed`` object. -with the querystring -?public_key=``public_key``. A JSON object will be returned. -The invitation is valid if the object contains a key named ``valid`` which is -``true``. Otherwise, the invitation MUST be rejected. This request is -idempotent and may be retried by the homeserver. +The third party user will then need to verify their identity, which results in +a call from the Identity Server to the homeserver that bound the third party +identifier to a user. The homeserver then exchanges the ``m.room.third_party_invite`` +event in the room for a complete ``m.room.member`` event for ``membership: invite`` +for the user that has bound the third party identifier. If a homeserver is joining a room for the first time because of an ``m.room.third_party_invite``, the server which is already participating in the @@ -99,29 +138,84 @@ validate that the public key used for signing is still valid, by checking No other homeservers may reject the joining of the room on the basis of ``key_validity_url``, this is so that all homeservers have a consistent view of -the room. They may, however, indicate to their clients that a member's' +the room. They may, however, indicate to their clients that a member's membership is questionable. -For example: - -#. Room R has two participating homeservers, H1, H2 - -#. User A on H1 invites a third party identifier to room R - -#. H1 asks the identity server for a binding to a Matrix user ID, and has none, - so issues an ``m.room.third_party_invite`` event to the room. - -#. When the third party user validates their identity, their homeserver H3 - is notified and attempts to issue an ``m.room.member`` event to participate - in the room. - -#. H3 validates the signature given to it by the identity server. - -#. H3 then asks H1 to join it to the room. H1 *must* validate the ``signed`` - property *and* check ``key_validity_url``. - -#. Having validated these things, H1 writes the invite event to the room, and H3 - begins participating in the room. H2 *must* accept this event. +For example, given H1, H2, and H3 as homeservers, UserA as a user of H1, and an +identity server IS, the full sequence for a third party invite would look like +the following. This diagram assumes H1 and H2 are residents of the room while +H3 is attempting to join. + +:: + + +-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+ + | UserA | | ThirdPartyUser | | H1 | | H2 | | H3 | | IS | + +-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+ + | | | | | | + | POST /invite for ThirdPartyUser | | | | + |----------------------------------->| | | | + | | | | | | + | | | GET /lookup | | | + | | |---------------------------------------------------------------------------------------------->| + | | | | | | + | | | | Lookup results (empty object) | + | | |<----------------------------------------------------------------------------------------------| + | | | | | | + | | | POST /store-invite | | | + | | |---------------------------------------------------------------------------------------------->| + | | | | | | + | | | | Token, keys, etc for third party invite | + | | |<----------------------------------------------------------------------------------------------| + | | | | | | + | | | (Federation) Emit m.room.third_party_invite | | | + | | |----------------------------------------------->| | | + | | | | | | + | Complete /invite request | | | | + |<-----------------------------------| | | | + | | | | | | + | | Verify identity | | | | + | |-------------------------------------------------------------------------------------------------------------------->| + | | | | | | + | | | | | POST /3pid/onbind | + | | | | |<---------------------------| + | | | | | | + | | | PUT /exchange_third_party_invite/:roomId | | + | | |<-----------------------------------------------------------------| | + | | | | | | + | | | Verify the request | | | + | | |------------------- | | | + | | | | | | | + | | |<------------------ | | | + | | | | | | + | | | (Federation) Emit m.room.member for invite | | | + | | |----------------------------------------------->| | | + | | | | | | + | | | | | | + | | | (Federation) Emit the m.room.member event sent to H2 | | + | | |----------------------------------------------------------------->| | + | | | | | | + | | | Complete /exchange_third_party_invite/:roomId request | | + | | |----------------------------------------------------------------->| | + | | | | | | + | | | | | Participate in the room | + | | | | |------------------------ | + | | | | | | | + | | | | |<----------------------- | + | | | | | | + + +Note that when H1 sends the ``m.room.member`` event to H2 and H3 it does not +have to block on either server's receipt of the event. Likewise, H1 may complete +the ``/exchange_third_party_invite/:roomId`` request at the same time as sending +the ``m.room.member`` event to H2 and H3. Additionally, H3 may complete the +``/3pid/onbind`` request it got from IS at any time - the completion is not shown +in the diagram. + +H1 MUST verify the request from H3 to ensure the ``signed`` property is correct +as well as the ``key_validity_url`` as still being valid. This is done by making +a request to the `Identity Server /isvalid`_ endpoint, using the provided URL +rather than constructing a new one. The query string and response for the provided +URL must match the Identity Server specification. The reason that no other homeserver may reject the event based on checking ``key_validity_url`` is that we must ensure event acceptance is deterministic. @@ -158,3 +252,6 @@ There is some risk of denial of service attacks by flooding homeservers or identity servers with many requests, or much state to store. Defending against these is left to the implementer's discretion. + + +.. _`Identity Server /isvalid`: ../identity_service/unstable.html#get-matrix-identity-api-v1-pubkey-isvalid diff --git a/specification/push_gateway.rst b/specification/push_gateway.rst index e4623887..a77d43db 100644 --- a/specification/push_gateway.rst +++ b/specification/push_gateway.rst @@ -16,6 +16,8 @@ Push Gateway API ================ +{{unstable_warning_block_PUSH_GATEWAY_RELEASE_LABEL}} + Clients may want to receive push notifications when events are received at the homeserver. This is managed by a distinct entity called the Push Gateway. diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst index 76aed91d..36d7d5d4 100644 --- a/specification/server_server_api.rst +++ b/specification/server_server_api.rst @@ -308,6 +308,8 @@ creating a new event in this room should populate the new event's | E4 +.. _`auth events selection`: + The ``auth_events`` field of a PDU identifies the set of events which give the sender permission to send the event. The ``auth_events`` for the ``m.room.create`` event in a room is empty; for other events, it should be the @@ -315,8 +317,16 @@ following subset of the room state: - The ``m.room.create`` event. - The current ``m.room.power_levels`` event, if any. -- The current ``m.room.join_rules`` event, if any. - The sender's current ``m.room.member`` event, if any. +- If type is ``m.room.member``: + + - The target's current ``m.room.member`` event, if any. + - If ``membership`` is ``join`` or ``invite``, the current + ``m.room.join_rules`` event, if any. + - If membership is ``invite`` and ``content`` contains a + ``third_party_invite`` property, the current + ``m.room.third_party_invite`` event with ``state_key`` matching + ``content.third_party_invite.signed.token``, if any. {{definition_ss_pdu}} @@ -358,117 +368,170 @@ be inserted. The types of state events that affect authorization are: - ``m.room.member`` - ``m.room.join_rules`` - ``m.room.power_levels`` +- ``m.room.third_party_invite`` -Servers should not create new events that reference unauthorized events. -However, any event that does reference an unauthorized event is not itself -automatically considered unauthorized. +The rules are as follows: -Unauthorized events that appear in the event graph do *not* have any effect on -the state of the room. +1. If type is ``m.room.create``: -.. Note:: This is in contrast to redacted events which can still affect the - state of the room. For example, a redacted ``join`` event will still - result in the user being considered joined. + a. If it has any previous events, reject. + b. If the domain of the ``room_id`` does not match the domain of the + ``sender``, reject. + c. If ``content.room_version`` is present and is not a recognised version, + reject. + d. If ``content`` has no ``creator`` field, reject. + e. Otherwise, allow. -The rules are as follows: +#. Reject if event has ``auth_events`` that: + + a. have duplicate entries for a given ``type`` and ``state_key`` pair + #. have entries whose ``type`` and ``state_key`` don't match those + specified by the `auth events selection`_ algorithm described above. + +#. If event does not have a ``m.room.create`` in its ``auth_events``, reject. + +#. If type is ``m.room.aliases``: + + a. If event has no ``state_key``, reject. + b. If sender's domain doesn't matches ``state_key``, reject. + c. Otherwise, allow. + +#. If type is ``m.room.member``: + + a. If no ``state_key`` key or ``membership`` key in ``content``, reject. + + #. If ``membership`` is ``join``: + + i. If the only previous event is an ``m.room.create`` + and the ``state_key`` is the creator, allow. + + #. If the ``sender`` does not match ``state_key``, reject. + + #. If the ``sender`` is banned, reject. + + #. If the ``join_rule`` is ``invite`` then allow if membership state + is ``invite`` or ``join``. + + #. If the ``join_rule`` is ``public``, allow. + + #. Otherwise, reject. + + #. If ``membership`` is ``invite``: -1. If type is ``m.room.create``, allow if and only if it has no - previous events - *i.e.* it is the first event in the room. + i. If ``content`` has ``third_party_invite`` key: -2. If type is ``m.room.member``: + #. If *target user* is banned, reject. - a. If ``membership`` is ``join``: + #. If ``content.third_party_invite`` does not have a + ``signed`` key, reject. - i. If the only previous event is an ``m.room.create`` - and the ``state_key`` is the creator, allow. + #. If ``signed`` does not have ``mxid`` and ``token`` keys, reject. - #. If the ``sender`` does not match ``state_key``, reject. + #. If ``mxid`` does not match ``state_key``, reject. - #. If the user's current membership state is ``invite`` or ``join``, - allow. + #. If there is no ``m.room.third_party_invite`` event in the + current room state with ``state_key`` matching ``token``, reject. - #. If the ``join_rule`` is ``public``, allow. + #. If ``sender`` does not match ``sender`` of the + ``m.room.third_party_invite``, reject. - #. Otherwise, reject. + #. If any signature in ``signed`` matches any public key in the + ``m.room.third_party_invite`` event, allow. The public keys are + in ``content`` of ``m.room.third_party_invite`` as: - b. If ``membership`` is ``invite``: + #. A single public key in the ``public_key`` field. + #. A list of public keys in the ``public_keys`` field. - i. If the ``sender``'s current membership state is not ``join``, reject. + #. Otherwise, reject. - #. If *target user*'s current membership state is ``join`` or ``ban``, - reject. + #. If the ``sender``'s current membership state is not ``join``, reject. - #. If the ``sender``'s power level is greater than or equal to the *invite - level*, allow. + #. If *target user*'s current membership state is ``join`` or ``ban``, + reject. - #. Otherwise, reject. + #. If the ``sender``'s power level is greater than or equal to the *invite + level*, allow. - c. If ``membership`` is ``leave``: + #. Otherwise, reject. - i. If the ``sender`` matches ``state_key``, allow if and only if that user's - current membership state is ``invite`` or ``join``. + #. If ``membership`` is ``leave``: - #. If the ``sender``'s current membership state is not ``join``, reject. + i. If the ``sender`` matches ``state_key``, allow if and only if that user's + current membership state is ``invite`` or ``join``. - #. If the *target user*'s current membership state is ``ban``, and the - ``sender``'s power level is less than the *ban level*, reject. + #. If the ``sender``'s current membership state is not ``join``, reject. - #. If the ``sender``'s power level is greater than or equal to the *kick - level*, and the *target user*'s power level is less than the - ``sender``'s power level, allow. + #. If the *target user*'s current membership state is ``ban``, and the + ``sender``'s power level is less than the *ban level*, reject. - #. Otherwise, reject. + #. If the ``sender``'s power level is greater than or equal to the *kick + level*, and the *target user*'s power level is less than the + ``sender``'s power level, allow. - d. If ``membership`` is ``ban``: + #. Otherwise, reject. - i. If the ``sender``'s current membership state is not ``join``, reject. + #. If ``membership`` is ``ban``: - #. If the ``sender``'s power level is greater than or equal to the *ban - level*, and the *target user*'s power level is less than the - ``sender``'s power level, allow. + i. If the ``sender``'s current membership state is not ``join``, reject. - #. Otherwise, reject. + #. If the ``sender``'s power level is greater than or equal to the *ban + level*, and the *target user*'s power level is less than the + ``sender``'s power level, allow. - e. Otherwise, the membership is unknown. Reject. + #. Otherwise, reject. -3. If the ``sender``'s current membership state is not ``join``, reject. + #. Otherwise, the membership is unknown. Reject. -4. If the event type's *required power level* is greater than the ``sender``'s power +#. If the ``sender``'s current membership state is not ``join``, reject. + +#. If type is ``m.room.third_party_invite``: + + a. Allow if and only if ``sender``'s current power level is greater than + or equal to the *invite level*. + +#. If the event type's *required power level* is greater than the ``sender``'s power level, reject. -5. If type is ``m.room.power_levels``: +#. If the event has a ``state_key`` that starts with an ``@`` and does not match + the ``sender``, reject. + +#. If type is ``m.room.power_levels``: + + a. If ``users`` key in ``content`` is not a dictionary with keys that are + valid user IDs with values that are integers (or a string that is an + integer), reject. - a. If there is no previous ``m.room.power_levels`` event in the room, allow. + #. If there is no previous ``m.room.power_levels`` event in the room, allow. - b. For each of the keys ``users_default``, ``events_default``, - ``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as - each entry being changed under the ``events`` or ``users`` keys: + #. For each of the keys ``users_default``, ``events_default``, + ``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as + each entry being changed under the ``events`` or ``users`` keys: - i. If the current value is higher than the ``sender``'s current power level, - reject. + i. If the current value is higher than the ``sender``'s current power level, + reject. - #. If the new value is higher than the ``sender``'s current power level, - reject. + #. If the new value is higher than the ``sender``'s current power level, + reject. - c. For each entry being changed under the ``users`` key, other than the - ``sender``'s own entry: + #. For each entry being changed under the ``users`` key, other than the + ``sender``'s own entry: - i. If the current value is equal to the ``sender``'s current power level, - reject. + i. If the current value is equal to the ``sender``'s current power level, + reject. - d. Otherwise, allow. + #. Otherwise, allow. -6. If type is ``m.room.redaction``: +#. If type is ``m.room.redaction``: - a. If the ``sender``'s power level is greater than or equal to the *redact - level*, allow. + a. If the ``sender``'s power level is greater than or equal to the *redact + level*, allow. - #. If the ``sender`` of the event being redacted is the same as the - ``sender`` of the ``m.room.redaction``, allow. + #. If the domain of the ``event_id`` of the event being redacted is the same + as the domain of the ``event_id`` of the ``m.room.redaction``, allow. - #. Otherwise, reject. + #. Otherwise, reject. -7. Otherwise, allow. +#. Otherwise, allow. .. NOTE:: @@ -482,9 +545,30 @@ The rules are as follows: the kick *and* ban levels, *and* greater than the target user's power level. -.. TODO-spec - I think there is some magic about 3pid invites too. +Rejection ++++++++++ + +If an event is rejected it should neither be relayed to clients nor be included +as a prev event in any new events generated by the server. Subsequent events +from other servers that reference rejected events should be allowed if they +still pass the auth rules. The state used in the checks should be calculated as +normal, except not updating with the rejected event where it is a state event. + +If an event in an incoming transaction is rejected, this should not cause the +transaction request to be responded to with an error response. + +.. NOTE:: + + This means that events may be included in the room DAG even though they + should be rejected. + +.. NOTE:: + + This is in contrast to redacted events which can still affect the + state of the room. For example, a redacted ``join`` event will still + result in the user being considered joined. + Retrieving event authorization information ++++++++++++++++++++++++++++++++++++++++++ @@ -752,6 +836,10 @@ event to other servers in the room. Third-party invites ------------------- +.. NOTE:: + More information about third party invites is available in the `Client-Server API`_ + under the Third Party Invites module. + When an user wants to invite another user in a room but doesn't know the Matrix ID to invite, they can do so using a third-party identifier (e.g. an e-mail or a phone number).