Merge branch 'master' into matthew/msc1779

pull/977/head
Matthew Hodgson 6 years ago committed by GitHub
commit efcbf2f807
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -98,8 +98,10 @@ the ``newsfragments`` directory. The ``type`` can be one of the following:
* ``deprecation`` - Used when deprecating something
All news fragments must have a brief summary explaining the change in the contents
of the file. The summary must end in a full stop to be in line with the style guide.
All news fragments must have a brief summary explaining the change in the
contents of the file. The summary must end in a full stop to be in line with
the style guide and and formatting must be done using `Restructured Text
<http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html>`_.
Changes that do not change the spec, such as changes to the build script, formatting,
CSS, etc should not get a news fragment.

@ -75,6 +75,12 @@ def check_response(filepath, request, code, response):
filepath, request, code
))
check_schema(filepath, example, schema)
except jsonschema.SchemaError as error:
for suberror in sorted(error.context, key=lambda e: e.schema_path):
print(list(suberror.schema_path), suberror.message, sep=", ")
raise ValueError("Error validating JSON schema for %r %r" % (
request, code
), e)
except Exception as e:
raise ValueError("Error validating JSON schema for %r %r" % (
request, code

@ -43,8 +43,8 @@ paths:
name: userId
required: true
description: |-
The id of the user to set account_data for. The access token must be
authorized to make requests for this user id.
The ID of the user to set account_data for. The access token must be
authorized to make requests for this user ID.
x-example: "@alice:example.com"
- in: path
type: string
@ -69,6 +69,41 @@ paths:
The account_data was successfully added.
tags:
- User data
get:
summary: Get some account_data for the user.
description: |-
Get some account_data for the client. This config is only visible to the user
that set the account_data.
operationId: getAccountData
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
required: true
description: |-
The ID of the user to get account_data for. The access token must be
authorized to make requests for this user ID.
x-example: "@alice:example.com"
- in: path
type: string
name: type
required: true
description: |-
The event type of the account_data to get. Custom types should be
namespaced to avoid clashes.
x-example: "org.example.custom.config"
responses:
200:
description:
The account data content for the given type.
schema:
type: object
example: {
"custom_account_data_key": "custom_config_value"}
tags:
- User data
"/user/{userId}/rooms/{roomId}/account_data/{type}":
put:
summary: Set some account_data for the user.
@ -85,15 +120,15 @@ paths:
name: userId
required: true
description: |-
The id of the user to set account_data for. The access token must be
authorized to make requests for this user id.
The ID of the user to set account_data for. The access token must be
authorized to make requests for this user ID.
x-example: "@alice:example.com"
- in: path
type: string
name: roomId
required: true
description: |-
The id of the room to set account_data on.
The ID of the room to set account_data on.
x-example: "!726s6s6q:example.com"
- in: path
type: string
@ -118,3 +153,45 @@ paths:
The account_data was successfully added.
tags:
- User data
get:
summary: Get some account_data for the user.
description: |-
Get some account_data for the client on a given room. This config is only
visible to the user that set the account_data.
operationId: getAccountDataPerRoom
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
required: true
description: |-
The ID of the user to set account_data for. The access token must be
authorized to make requests for this user ID.
x-example: "@alice:example.com"
- in: path
type: string
name: roomId
required: true
description: |-
The ID of the room to get account_data for.
x-example: "!726s6s6q:example.com"
- in: path
type: string
name: type
required: true
description: |-
The event type of the account_data to get. Custom types should be
namespaced to avoid clashes.
x-example: "org.example.custom.room.config"
responses:
200:
description:
The account data content for the given type.
schema:
type: object
example: {
"custom_account_data_key": "custom_config_value"}
tags:
- User data

@ -0,0 +1,112 @@
# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Client-Server Capabiltiies API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/capabilities":
get:
summary: Gets information about the server's capabilities.
description: |-
Gets information about the server's supported feature set
and other relevant capabilities.
operationId: getCapabilities
security:
- accessToken: []
parameters: []
responses:
200:
description:
The capabilities of the server.
examples:
application/json: {
"capabilities": {
"m.change_password": {
"enabled": false
},
"m.room_versions": {
"default": "1",
"available": {
"1": "stable",
"2": "stable",
"3": "unstable",
"test-version": "unstable"
}
},
"com.example.custom.ratelimit": {
"max_requests_per_hour": 600
}
}
}
schema:
type: object
required: ["capabilities"]
properties:
capabilities:
type: object
title: Capabilities
description: |-
The custom capabilities the server supports, using the
Java package naming convention.
additionalProperties:
type: object
properties:
"m.change_password":
type: object
description: |-
Capability to indicate if the user can change their password.
title: ChangePasswordCapability
properties:
enabled:
type: boolean
description: |-
True if the user can change their password, false otherwise.
example: false
required: ['enabled']
"m.room_versions":
type: object
description: The room versions the server supports.
title: RoomVersionsCapability
properties:
default:
type: string
description: |-
The default room version the server is using for new rooms.
example: "1"
available:
type: object
description: |-
A detailed description of the room versions the server supports.
additionalProperties:
type: string
title: RoomVersionStability
enum: [stable, unstable]
description: The stability of the room version.
required: ['default', 'available']
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- Capabilities

@ -244,6 +244,12 @@ paths:
invalid: for example, the user's ``power_level`` is set below
that necessary to set the room name (``errcode`` set to
``M_INVALID_ROOM_STATE``).
- The homeserver doesn't support the requested room version, or
one or more users being invited to the new room are residents
of a homeserver which does not support the requested room version.
The ``errcode`` will be ``M_UNSUPPORTED_ROOM_VERSION`` in these
cases.
schema:
"$ref": "definitions/errors/error.yaml"
tags:

@ -0,0 +1,39 @@
# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
title: Discovery Information
description: |-
Used by clients to determine the homeserver, identity server, and other
optional components they should be interacting with.
type: object
properties:
"m.homeserver":
$ref: "homeserver.yaml"
"m.identity_server":
$ref: "identity_server.yaml"
additionalProperties:
type: object
description: Application-dependent keys using Java package naming convention.
required:
- m.homeserver
example: {
"m.homeserver": {
"base_url": "https://matrix.example.com"
},
"m.identity_server": {
"base_url": "https://identity.example.com"
},
"org.example.custom.property": {
"app_url": "https://custom.app.example.org"
}
}

@ -82,6 +82,20 @@ paths:
}
schema:
type: object
400:
description: |-
The request is invalid. A meaningful ``errcode`` and description
error text will be returned. Example reasons for rejection include:
- The request body is malformed (``errcode`` set to ``M_BAD_JSON``
or ``M_NOT_JSON``).
- One or more users being invited to the room are residents of a
homeserver which does not support the requested room version. The
``errcode`` will be ``M_UNSUPPORTED_ROOM_VERSION`` in these cases.
schema:
"$ref": "definitions/errors/error.yaml"
403:
description: |-
You do not have permission to invite the user to the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are:

@ -139,7 +139,15 @@ paths:
application/json: {
"user_id": "@cheeky_monkey:matrix.org",
"access_token": "abc123",
"device_id": "GHTYAJCE"
"device_id": "GHTYAJCE",
"well_known": {
"m.homeserver": {
"base_url": "https://example.org"
},
"m.identity_server": {
"base_url": "https://id.example.org"
}
}
}
schema:
type: object
@ -166,6 +174,14 @@ paths:
description: |-
ID of the logged-in device. Will be the same as the
corresponding parameter in the request, if one was specified.
well_known:
type: object
description: |-
Optional client configuration provided by the server. If present,
clients SHOULD use the provided object to reconfigure themselves,
optionally validating the URLs within. This object takes the same
form as the one returned from .well-known autodiscovery.
"$ref": "definitions/wellknown/full.yaml"
400:
description: |-
Part of the request was invalid. For example, the login type may not be recognised.

@ -136,103 +136,3 @@ paths:
"$ref": "definitions/errors/error.yaml"
tags:
- Presence
"/presence/list/{userId}":
post:
summary: Add or remove users from this presence list.
description: |-
Adds or removes users from this presence list.
operationId: modifyPresenceList
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
description: The user whose presence list is being modified.
required: true
x-example: "@alice:example.com"
- in: body
name: presence_diff
description: The modifications to make to this presence list.
required: true
schema:
type: object
example: {
"invite": [
"@bob:matrix.org"
],
"drop": [
"@alice:matrix.org"
]
}
properties:
invite:
type: array
description: A list of user IDs to add to the list.
items:
type: string
description: A list of user IDs.
drop:
type: array
description: A list of user IDs to remove from the list.
items:
type: string
description: A list of user IDs.
responses:
200:
description: The list was updated.
examples:
application/json: {
}
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- Presence
get:
summary: Get presence events for this presence list.
description: |-
Retrieve a list of presence events for every user on this list.
operationId: getPresenceForList
parameters:
- in: path
type: string
name: userId
description: The user whose presence list should be retrieved.
required: true
x-example: "@alice:example.com"
responses:
200:
description: A list of presence events for this list.
examples:
application/json: [
{
"content": {
"last_active_ago": 395,
"presence": "offline",
"user_id": "@alice:matrix.org"
},
"type": "m.presence"
},
{
"content": {
"last_active_ago": 16874,
"presence": "online",
"user_id": "@marisa:matrix.org",
"currently_active": true
},
"type": "m.presence"
}
]
schema:
type: array
items:
type: object
title: PresenceEvent
allOf:
- "$ref": "definitions/event-schemas/schema/core-event-schema/event.yaml"
tags:
- Presence

@ -0,0 +1,93 @@
# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Client-Server Room Upgrades API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/rooms/{roomId}/upgrade":
post:
summary: Upgrades a room to a new room version.
description: |-
Upgrades the given room to a particular room version.
operationId: upgradeRoom
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
required: true
description: The ID of the room to upgrade.
x-example: "!oldroom:example.org"
- in: body
name: body
required: true
description: The request body
schema:
type: object
properties:
new_version:
type: string
description: The new version for the room.
example: {"new_version": "2"}
required: [new_version]
responses:
200:
description: The room was successfully upgraded.
examples:
application/json: {
"replacement_room": "!newroom:example.org"
}
schema:
type: object
properties:
replacement_room:
type: string
description: The ID of the new room.
required: [replacement_room]
400:
description: |-
The request was invalid. One way this can happen is if the room version
requested is not supported by the homeserver.
examples:
application/json: {
"errcode": "M_UNSUPPORTED_ROOM_VERSION",
"error": "This server does not support that room version"
}
schema:
"$ref": "definitions/errors/error.yaml"
403:
description: |-
The user is not permitted to upgrade the room.
examples:
application/json: {
"errcode": "M_FORBIDDEN",
"error": "You cannot upgrade this room"
}
schema:
"$ref": "definitions/errors/error.yaml"
tags:
- Room ugprades

@ -33,13 +33,29 @@ paths:
Only the latest ``Z`` value will be reported for each supported ``X.Y`` value.
i.e. if the server implements ``r0.0.0``, ``r0.0.1``, and ``r1.2.0``, it will report ``r0.0.1`` and ``r1.2.0``.
The server may additionally advertise experimental features it supports
through ``unstable_features``. These features should be namespaced and
may optionally include version information within their name if desired.
Features listed here are not for optionally toggling parts of the Matrix
specification and should only be used to advertise support for a feature
which has not yet landed in the spec. For example, a feature currently
undergoing the proposal process may appear here and eventually be taken
off this list once the feature lands in the spec and the server deems it
reasonable to do so. Servers may wish to keep advertising features here
after they've been released into the spec to give clients a chance to
upgrade appropriately. Additionally, clients should avoid using unstable
features in their stable releases.
operationId: getVersions
responses:
200:
description: The versions supported by the server.
examples:
application/json: {
"versions": ["r0.0.1"]
"versions": ["r0.0.1"],
"unstable_features": {
"org.example.my_feature": true
}
}
schema:
type: object
@ -50,5 +66,15 @@ paths:
items:
type: string
description: The supported versions
unstable_features:
type: object
description: |-
Experimental features the server supports. Features not listed here,
or the lack of this property all together, indicate that a feature is
not supported.
additionalProperties:
type: boolean
description: Whether or not the namespaced feature is supported.
required: ['versions']
tags:
- Server administration

@ -38,28 +38,9 @@ paths:
responses:
200:
description: Server discovery information.
examples:
application/json: {
"m.homeserver": {
"base_url": "https://matrix.example.com"
},
"m.identity_server": {
"base_url": "https://identity.example.com"
}
}
schema:
type: object
properties:
m.homeserver:
description: Information about the homeserver to connect to.
"$ref": "definitions/wellknown/homeserver.yaml"
m.identity_server:
description: Optional. Information about the identity server to connect to.
"$ref": "definitions/wellknown/identity_server.yaml"
additionalProperties:
description: Application-dependent keys using Java package naming convention.
required:
- m.homeserver
"$ref": "definitions/wellknown/full.yaml"
404:
description: No server discovery information available.
tags:

@ -65,22 +65,6 @@ paths:
event(s), up to the given limit.
schema:
$ref: "definitions/transaction.yaml"
# Override the example to show the response of the request a bit better
examples:
application/json: {
"$ref": "examples/transaction.json",
"pdus": [
{
"$ref": "pdu.json",
"room_id": "!SomeRoom:matrix.org",
"event_id": "$abc123:matrix.org"
},
{
"$ref": "pdu.json",
"room_id": "!SomeRoom:matrix.org"
},
]
}
"/get_missing_events/{roomId}":
post:
summary: Retrieves events that the sender is missing
@ -114,14 +98,14 @@ paths:
earliest_events:
type: array
description: |-
The latest events that the sender already has. These are skipped when retrieving
The latest event IDs that the sender already has. These are skipped when retrieving
the previous events of ``latest_events``.
items:
type: string
example: ["$missing_event:example.org"]
latest_events:
type: array
description: The events to retrieve the previous events for.
description: The event IDs to retrieve the previous events for.
items:
type: string
example: ["$event_that_has_the_missing_event_as_a_previous_event:example.org"]
@ -136,13 +120,16 @@ paths:
properties:
events:
type: array
description: The missing events.
description: |-
The missing events. The event format varies depending on the room version - check
the `room version specification`_ for precise event formats.
items:
$ref: definitions/pdu.yaml
type: object
title: PDU
required: ['events']
examples:
application/json: {
"events": [
{"$ref": "examples/pdu.json"}
{"$ref": "examples/minimal_pdu.json"}
]
}

@ -21,7 +21,7 @@ description: |-
# FIXME: It's very unclear why we have this API surface for synchronising
# device lists, rather than just using a room (which could also be used for
# presence lists, profile info, etc). But documenting what we have for now...
# profile info, etc). But documenting what we have for now...
allOf:
- $ref: ../edu.yaml

@ -1,46 +0,0 @@
# Copyright 2018 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: m.presence_accept
description: |-
An EDU representing approval for the observing user to subscribe to the
presence of the the observed user.
allOf:
- $ref: ../edu.yaml
- type: object
properties:
edu_type:
type: enum
enum: ['m.presence_accept']
description: The string ``m.presence_accept``
example: "m.presence_accept"
content:
type: object
description: The invite information.
title: Invite Information
properties:
observed_user:
type: string
description: |-
The user ID that has approved the ``observer_user`` to
subscribe to their presence.
example: "@alice:elsewhere.com"
observer_user:
type: string
description: |-
The user ID that requested to subscribe to the presence of
the ``observed_user``.
example: "@john:matrix.org"
required: ['observer_user', 'observed_user']

@ -1,55 +0,0 @@
# Copyright 2018 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: m.presence_deny
description: |-
An EDU representing a declination or revocation for the observing user
to subscribe to the presence of the observed user.
example: {
"origin": "example.org",
"destination": "elsewhere.org",
"edu_type": "m.presence_deny",
"content": {
"observed_user": "@alice:elsewhere.org",
"observer_user": "@john:example.org"
}
}
allOf:
- $ref: ../edu.yaml
- type: object
properties:
edu_type:
type: enum
enum: ['m.presence_deny']
description: The string ``m.presence_deny``
example: "m.presence_deny"
content:
type: object
description: The invite information.
title: Invite Information
properties:
observed_user:
type: string
description: |-
The user ID that has declined or revoked the ``observer_user`` from
subscribing to their presence.
example: "@alice:elsewhere.com"
observer_user:
type: string
description: |-
The user ID that requested to subscribe to the presence of
the ``observed_user``.
example: "@john:matrix.org"
required: ['observer_user', 'observed_user']

@ -1,45 +0,0 @@
# Copyright 2018 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: m.presence_invite
description: |-
An EDU representing a request to subscribe to a user's presence.
allOf:
- $ref: ../edu.yaml
- type: object
properties:
edu_type:
type: enum
enum: ['m.presence_invite']
description: The string ``m.presence_invite``
example: "m.presence_invite"
content:
type: object
description: The invite information.
title: Invite Information
properties:
observed_user:
type: string
description: |-
The user ID the ``observer_user`` would like to subscribe
to the presence of.
example: "@alice:elsewhere.com"
observer_user:
type: string
description: |-
The user ID that is wishing to subscribe to the presence of
the ``observed_user``.
example: "@john:matrix.org"
required: ['observer_user', 'observed_user']

@ -1,4 +1,4 @@
# Copyright 2018 New Vector Ltd
# Copyright 2018-2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -13,15 +13,14 @@
# limitations under the License.
type: object
title: Invite Event
description: An invite event
description: |-
An invite event. Note that events have a different format depending on the
room version - check the `room version specification`_ for precise event formats.
allOf:
- $ref: "pdu.yaml"
- type: object
properties:
# Note: we override a bunch of parameters to change their descriptions
sender:
type: string
# TODO: Verify/clarify this - it doesn't seem right, given this is a 'regular' invite
description: |-
The matrix ID of the user who sent the original ``m.room.third_party_invite``.
example: "@someone:example.org"
@ -46,7 +45,7 @@ allOf:
type: object
title: Membership Event Content
description: |-
The content of the event, matching what is available in the
The content of the event, matching what is available in the
`Client-Server API`_. Must include a ``membership`` of ``invite``.
example: {"membership": "invite"}
properties:
@ -55,33 +54,10 @@ allOf:
description: The value ``invite``.
example: "invite"
required: ['membership']
auth_events:
type: array
description: |-
An event reference list containing the authorization events that would
allow the member to be invited to the room.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "abase64encodedsha256hashshouldbe43byteslong"
}
properties:
sha256:
type: string
description: The event hash.
example: abase64encodedsha256hashshouldbe43byteslong
required: ['sha256']
redacts:
type: string
description: Not used.
required:
# Every other field is already flagged as required by the $ref
- state_key
- sender
- origin
- origin_server_ts
- type
- content

@ -25,9 +25,9 @@ properties:
verify_keys:
type: object
description: |-
Public keys of the homeserver for verifying digital signatures.
The object's key is the algorithm and version combined (``ed25519`` being the
Public keys of the homeserver for verifying digital signatures.
The object's key is the algorithm and version combined (``ed25519`` being the
algorithm and ``abc123`` being the version in the example below). Together,
this forms the Key ID. The version must have characters matching the regular
expression ``[a-zA-Z0-9_]``.
@ -49,9 +49,9 @@ properties:
old_verify_keys:
type: object
description: |-
The public keys that the server used to use and when it stopped using them.
The object's key is the algorithm and version combined (``ed25519`` being the
The public keys that the server used to use and when it stopped using them.
The object's key is the algorithm and version combined (``ed25519`` being the
algorithm and ``0ldK3y`` being the version in the example below). Together,
this forms the Key ID. The version must have characters matching the regular
expression ``[a-zA-Z0-9_]``.
@ -90,17 +90,6 @@ properties:
additionalProperties:
type: string
name: Encoded Signature Verification Key
tls_fingerprints:
type: array
description: Hashes of X.509 TLS certificates used by this server.
items:
type: object
title: TLS Fingerprint
properties:
sha256:
type: string
description: The `Unpadded Base64`_ encoded fingerprint.
example: "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw"
valid_until_ts:
type: integer
format: int64

@ -13,7 +13,7 @@
# limitations under the License.
type: object
title: Persistent Data Unit
description: A persistent data unit (event)
description: A persistent data unit (event) for room versions 1 and 2.
example:
$ref: "../examples/pdu.json"
allOf:
@ -26,13 +26,13 @@ allOf:
description: |-
Content hashes of the PDU, following the algorithm specified in `Signing Events`_.
example: {
"sha256": "thishashcoversallfieldsincasethisisredacted"
"sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted"
}
properties:
sha256:
type: string
description: The hash.
example: thishashcoversallfieldsincasthisisredacted
example: ThisHashCoversAllFieldsInCaseThisIsRedacted
required: ['sha256']
signatures:
type: object
@ -40,7 +40,7 @@ allOf:
Signatures for the PDU, following the algorithm specified in `Signing Events`_.
example: {
"example.com": {
"ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus"
"ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent"
}
}
additionalProperties:

@ -0,0 +1,73 @@
# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: Persistent Data Unit
description: A persistent data unit (event) for room version 3 and beyond.
example:
$ref: "../examples/pdu_v3.json"
allOf:
- $ref: "unsigned_pdu_base.yaml"
- type: object
properties:
auth_events:
type: array
items:
type: string
description: Event ID.
description: |-
Event IDs for the authorization events that would
allow this event to be in the room.
example: ["$base64EncodedHash", "$AnotherEvent"]
prev_events:
type: array
items:
type: string
description: Event ID.
description: |-
Event IDs for the most recent events in the room
that the homeserver was aware of when it made this event.
example: ["$base64EncodedHash", "$AnotherEvent"]
hashes:
type: object
title: Event Hash
description: |-
Content hashes of the PDU, following the algorithm specified in `Signing Events`_.
example: {
"sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted"
}
properties:
sha256:
type: string
description: The hash.
example: ThisHashCoversAllFieldsInCaseThisIsRedacted
required: ['sha256']
signatures:
type: object
description: |-
Signatures for the PDU, following the algorithm specified in `Signing Events`_.
example: {
"example.com": {
"ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent"
}
}
additionalProperties:
type: object
title: Server Signatures
additionalProperties:
type: string
required:
- auth_events
- prev_events
- hashes
- signatures

@ -26,12 +26,22 @@ properties:
type: integer
format: int64
description: |-
POSIX timestamp in milliseconds on originating homeserver when this
POSIX timestamp in milliseconds on originating homeserver when this
transaction started.
example: 1532991320875
pdus:
type: array
description: List of persistent updates to rooms. Must not include more than 50 PDUs.
description: |-
List of persistent updates to rooms. Must not include more than 50 PDUs. Note that
events have a different format depending on the room version - check the
`room version specification`_ for precise event formats.
items:
$ref: "pdu.yaml"
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ contained in the transaction. The event format varies depending
on the room version - check the `room version specification`_ for precise event formats.
properties: []
example:
$ref: "../examples/minimal_pdu.json"
required: ['origin', 'origin_server_ts', 'pdus']

@ -1,4 +1,4 @@
# Copyright 2018 New Vector Ltd
# Copyright 2018-2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -16,140 +16,13 @@ title: Unsigned Persistent Data Unit
description: An unsigned persistent data unit (event)
example:
$ref: "../examples/unsigned_pdu.json"
properties:
event_id:
type: string
description: The event ID for the PDU.
example: "$a4ecee13e2accdadf56c1025:example.com"
room_id:
type: string
description: Room identifier.
example: "!abc123:matrix.org"
sender:
type: string
description: The ID of the user sending the event.
example: "@someone:matrix.org"
origin:
type: string
description: The ``server_name`` of the homeserver that created this event.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: Timestamp in milliseconds on origin homeserver when this event was created.
example: 1234567890
type:
type: string
description: Event type
example: "m.room.message"
state_key:
type: string
description: |-
If this key is present, the event is a state event, and it will replace previous events
with the same ``type`` and ``state_key`` in the room state.
example: "my_key"
content:
type: object
description: The content of the event.
example: {"key": "value"}
prev_events:
type: array
description: |-
Event IDs and reference hashes for the most recent events in the room
that the homeserver was aware of when it made this event.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "abase64encodedsha256hashshouldbe43byteslong"
}
properties:
sha256:
type: string
description: The event hash.
example: abase64encodedsha256hashshouldbe43byteslong
required: ['sha256']
depth:
type: integer
description: |-
The maximum depth of the ``prev_events``, plus one. Must be less than the
maximum value for an integer (2^63 - 1). If the room's depth is already at
the limit, the depth must be set to the limit.
example: 12
auth_events:
type: array
description: |-
Event IDs and reference hashes for the authorization events that would
allow this event to be in the room.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "abase64encodedsha256hashshouldbe43byteslong"
}
properties:
sha256:
type: string
description: The event hash.
example: abase64encodedsha256hashshouldbe43byteslong
required: ['sha256']
redacts:
type: string
description: For redaction events, the ID of the event being redacted.
example: "$def456:matrix.org"
unsigned:
type: object
title: Example Unsigned Data
description: |-
Additional data added by the origin server but not covered by the ``signatures``. More
keys than those defined here may be used.
example: {"key": "value"}
allOf:
- $ref: "unsigned_pdu_base.yaml"
- type: object
properties:
age:
type: integer
description: The number of milliseconds that have passed since this message was sent.
example: 4612
replaces_state:
event_id:
type: string
description: The event ID of the state event this event replaces.
example: "$state_event:example.org"
prev_sender:
type: string
description: The sender of the replaced state event.
example: "@someone:example.org"
prev_content:
type: object
description: The content of the replaced state event.
example: {
"membership": "join",
"displayname": "Bob"
}
redacted_because:
type: string
description: A reason for why the event was redacted.
example: "Inappropriate content"
required:
- event_id
- room_id
- sender
- origin
- origin_server_ts
- type
- content
- prev_events
- depth
- auth_events
description: The event ID for the PDU.
example: "$a4ecee13e2accdadf56c1025:example.com"
required:
- event_id

@ -0,0 +1,151 @@
# Copyright 2018 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: Unsigned Persistent Data Unit
description: An unsigned persistent data unit (event)
example:
$ref: "../examples/unsigned_pdu_base.json"
properties:
room_id:
type: string
description: Room identifier.
example: "!abc123:matrix.org"
sender:
type: string
description: The ID of the user sending the event.
example: "@someone:matrix.org"
origin:
type: string
description: The ``server_name`` of the homeserver that created this event.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: Timestamp in milliseconds on origin homeserver when this event was created.
example: 1234567890
type:
type: string
description: Event type
example: "m.room.message"
state_key:
type: string
description: |-
If this key is present, the event is a state event, and it will replace previous events
with the same ``type`` and ``state_key`` in the room state.
example: "my_key"
content:
type: object
description: The content of the event.
example: {"key": "value"}
prev_events:
type: array
description: |-
Event IDs and reference hashes for the most recent events in the room
that the homeserver was aware of when it made this event.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "Base64EncodedSha256HashesShouldBe43BytesLong"
}
properties:
sha256:
type: string
description: The event hash.
example: Base64EncodedSha256HashesShouldBe43BytesLong
required: ['sha256']
depth:
type: integer
description: |-
The maximum depth of the ``prev_events``, plus one. Must be less than the
maximum value for an integer (2^63 - 1). If the room's depth is already at
the limit, the depth must be set to the limit.
example: 12
auth_events:
type: array
description: |-
Event IDs and reference hashes for the authorization events that would
allow this event to be in the room.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "Base64EncodedSha256HashesShouldBe43BytesLong"
}
properties:
sha256:
type: string
description: The event hash.
example: Base64EncodedSha256HashesShouldBe43BytesLong
required: ['sha256']
redacts:
type: string
description: For redaction events, the ID of the event being redacted.
example: "$def456:matrix.org"
unsigned:
type: object
title: Example Unsigned Data
description: |-
Additional data added by the origin server but not covered by the ``signatures``. More
keys than those defined here may be used.
example: {"key": "value"}
properties:
age:
type: integer
description: The number of milliseconds that have passed since this message was sent.
example: 4612
replaces_state:
type: string
description: The event ID of the state event this event replaces.
example: "$state_event:example.org"
prev_sender:
type: string
description: The sender of the replaced state event.
example: "@someone:example.org"
prev_content:
type: object
description: The content of the replaced state event.
example: {
"membership": "join",
"displayname": "Bob"
}
redacted_because:
type: string
description: A reason for why the event was redacted.
example: "Inappropriate content"
required:
- event_id
- room_id
- sender
- origin
- origin_server_ts
- type
- content
- prev_events
- depth
- auth_events

@ -20,7 +20,7 @@ host: localhost:8448
schemes:
- https
basePath: /_matrix/federation/v1
consumes:
consumes:
- application/json
produces:
- application/json
@ -58,10 +58,19 @@ paths:
type: array
description: |-
The full set of authorization events that make up the state of
the room, and their authorization events, recursively.
the room, and their authorization events, recursively. Note that
events have a different format depending on the room version -
check the `room version specification`_ for precise event formats.
items:
$ref: "definitions/pdu.yaml"
example: [{"$ref": "examples/pdu.json"}]
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ contained in the auth chain. The event format
varies depending on the room version - check the `room version specification`_
for precise event formats.
properties: []
example:
$ref: "examples/minimal_pdu.json"
required: ['auth_chain']
"/query_auth/{roomId}/{eventId}":
post:
@ -98,10 +107,20 @@ paths:
properties:
auth_chain:
type: array
description: The auth chain (the "remote auth").
description: |-
The auth chain (the "remote auth"). Note that events have a different
format depending on the room version - check the `room version specification`_
for precise event formats.
items:
$ref: "definitions/pdu.yaml"
example: [{"$ref": "examples/pdu.json"}]
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ contained in the auth chain. The event format
varies depending on the room version - check the `room version specification`_
for precise event formats.
properties: []
example:
$ref: "examples/minimal_pdu.json"
missing:
type: array
description: |-
@ -142,14 +161,23 @@ paths:
type: array
description: |-
The auth chain the receiver has, and used to determine the auth
chain differences (the "local auth").
chain differences (the "local auth"). Note that events have a different
format depending on the room version - check the `room version specification`_
for precise event formats.
items:
$ref: "definitions/pdu.yaml"
example: [{"$ref": "examples/pdu.json"}]
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ contained in the auth chain. The event format
varies depending on the room version - check the `room version specification`_
for precise event formats.
properties: []
example:
$ref: "examples/minimal_pdu.json"
missing:
type: array
description: |-
The list of event IDs that the receiver believes it is missing,
The list of event IDs that the receiver believes it is missing,
after comparing the "remote auth" and "local auth" chains.
items:
type: string

@ -59,17 +59,35 @@ paths:
type: array
description: |-
The full set of authorization events that make up the state
of the room, and their authorization events, recursively.
of the room, and their authorization events, recursively. Note that
events have a different format depending on the room version -
check the `room version specification`_ for precise event formats.
items:
$ref: "definitions/pdu.yaml"
example: [{"$ref": "examples/pdu.json"}]
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ contained in the auth chain. The event format
varies depending on the room version - check the `room version specification`_
for precise event formats.
properties: []
example:
$ref: "examples/minimal_pdu.json"
pdus:
type: array
description: |-
The fully resolved state of the room at the given event.
The fully resolved state of the room at the given event. Note that
events have a different format depending on the room version -
check the `room version specification`_ for precise event formats.
items:
$ref: "definitions/pdu.yaml"
example: [{"$ref": "examples/pdu.json"}]
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ for the fully resolved state of the room. The event format
varies depending on the room version - check the `room version specification`_
for precise event formats.
properties: []
example:
$ref: "examples/minimal_pdu.json"
required: ['auth_chain', 'pdus']
"/state_ids/{roomId}":
get:

@ -0,0 +1,7 @@
{
"type": "m.room.minimal_pdu",
"room_id": "!somewhere:example.org",
"content": {
"see_room_version_spec": "The event format changes depending on the room version."
}
}

@ -0,0 +1,19 @@
{
"$ref": "unsigned_pdu_base.json",
"hashes": {
"sha256": "thishashcoversallfieldsincasethisisredacted"
},
"signatures": {
"example.com": {
"ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus"
}
},
"auth_events": [
"$base64encodedeventid",
"$adifferenteventid"
],
"prev_events": [
"$base64encodedeventid",
"$adifferenteventid"
]
}

@ -16,8 +16,5 @@
"ed25519:auto2": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU"
}
},
"tls_fingerprints": [{
"sha256": "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw"
}],
"valid_until_ts": 1652262000000
}
}

@ -1,5 +1,7 @@
{
"origin": "matrix.org",
"origin_server_ts": 1234567890,
"pdus": [{"$ref": "pdu.json"}]
}
"pdus": [{
"$ref": "minimal_pdu.json"
}]
}

@ -1,27 +1,4 @@
{
"room_id": "!UcYsUzyxTGDxLBEvLy:example.org",
"sender": "@alice:example.com",
"origin": "example.com",
"event_id": "$a4ecee13e2accdadf56c1025:example.com",
"origin_server_ts": 1404838188000,
"depth": 12,
"auth_events": [
[
"$af232176:example.org",
{"sha256": "abase64encodedsha256hashshouldbe43byteslong"}
]
],
"type": "m.room.message",
"prev_events": [
[
"$af232176:example.org",
{"sha256": "abase64encodedsha256hashshouldbe43byteslong"}
]
],
"content": {
"key": "value"
},
"unsigned": {
"age": 4612
}
}
"$ref": "unsigned_pdu_base.json",
"event_id": "$a4ecee13e2accdadf56c1025:example.com"
}

@ -0,0 +1,26 @@
{
"room_id": "!UcYsUzyxTGDxLBEvLy:example.org",
"sender": "@alice:example.com",
"origin": "example.com",
"origin_server_ts": 1404838188000,
"depth": 12,
"auth_events": [
[
"$af232176:example.org",
{"sha256": "abase64encodedsha256hashshouldbe43byteslong"}
]
],
"type": "m.room.message",
"prev_events": [
[
"$af232176:example.org",
{"sha256": "abase64encodedsha256hashshouldbe43byteslong"}
]
],
"content": {
"key": "value"
},
"unsigned": {
"age": 4612
}
}

@ -1,4 +1,4 @@
# Copyright 2018 New Vector Ltd
# Copyright 2018-2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ host: localhost:8448
schemes:
- https
basePath: /_matrix/federation/v1
consumes:
consumes:
- application/json
produces:
- application/json
@ -32,9 +32,18 @@ paths:
summary: Invites a remote user to a room
description: |-
Invites a remote user to a room. Once the event has been signed by both the inviting
homeserver and the invited homeserver, it can be sent to all of the servers in the
homeserver and the invited homeserver, it can be sent to all of the servers in the
room by the inviting homeserver.
operationId: sendInvite
Servers should prefer to use the v2 API for invites instead of the v1 API. Servers
which receive a v1 invite request must assume that the room version is either ``"1"``
or ``"2"``.
Note that events have a different format depending on the room version - check the
`room version specification`_ for precise event formats. **The request and response
bodies here describe the common event fields in more detail and may be missing other
required fields for a PDU.**
operationId: sendInviteV1
security:
- signedRequest: []
parameters:
@ -103,9 +112,12 @@ paths:
}
]
example: {
"$ref": "examples/pdu.json",
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@joe:elsewhere.com",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"unsigned": {
"invite_room_state": [
{
@ -139,7 +151,8 @@ paths:
200:
description: |-
The event with the invited server's signature added. All other fields of the events
should remain untouched.
should remain untouched. Note that events have a different format depending on the
room version - check the `room version specification`_ for precise event formats.
schema:
type: array
minItems: 2
@ -160,9 +173,12 @@ paths:
200,
{
"event": {
"$ref": "examples/pdu.json",
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"unsigned": {
"invite_room_state": [
{

@ -0,0 +1,246 @@
# Copyright 2018-2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Federation Invite User To Room API"
version: "1.0.0"
host: localhost:8448
schemes:
- https
basePath: /_matrix/federation/v2
consumes:
- application/json
produces:
- application/json
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/invite/{roomId}/{eventId}":
put:
summary: Invites a remote user to a room
description: |-
.. Note::
This API is nearly identical to the v1 API with the exception of the request
body being different, and the response format fixed.
Invites a remote user to a room. Once the event has been signed by both the inviting
homeserver and the invited homeserver, it can be sent to all of the servers in the
room by the inviting homeserver.
This endpoint is preferred over the v1 API as it is more useful for servers. Senders
which receive a 400 or 404 response to this endpoint should retry using the v1
API as the server may be older, if the room version is "1" or "2".
Note that events have a different format depending on the room version - check the
`room version specification`_ for precise event formats. **The request and response
bodies here describe the common event fields in more detail and may be missing other
required fields for a PDU.**
operationId: sendInviteV2
security:
- signedRequest: []
parameters:
- in: path
name: roomId
type: string
description: The room ID that the user is being invited to.
required: true
x-example: "!abc123:matrix.org"
- in: path
name: eventId
type: string
description: The event ID for the invite event, generated by the inviting server.
required: true
x-example: "$abc123:example.org"
- in: body
name: body
type: object
required: true
schema:
type: object
properties:
room_version:
type: string
description: The version of the room where the user is being invited to.
example: "2"
event:
$ref: "definitions/invite_event.yaml"
invite_room_state:
type: array
description: |-
An optional list of simplified events to help the receiver of the invite
identify the room. The recommended events to include are the join rules,
canonical alias, avatar, and name of the room.
items:
type: object
title: Invite Room State Event
properties:
type:
type: string
description: The type of event.
example: "m.room.join_rules"
state_key:
type: string
description: The state key for the event. May be an empty string.
example: ""
content:
type: object
description: The content for the event.
sender:
type: string
description: The sender of the event.
example: "@someone:matrix.org"
required: ['type', 'state_key', 'content', 'sender']
example: [
{
"type": "m.room.join_rules",
"sender": "@someone:matrix.org",
"state_key": "",
"content": {
"join_rule": "public"
}
}
]
required: ['room_version', 'event']
example: {
"room_version": "2",
"event": {
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@joe:elsewhere.com",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "invite"
},
"signatures": {
"example.com": {
"ed25519:key_version": "SomeSignatureHere"
},
}
},
"invite_room_state": [
{
"type": "m.room.join_rules",
"sender": "@someone:matrix.org",
"state_key": "",
"content": {
"join_rule": "public"
}
},
{
"type": "m.room.name",
"sender": "@someone:matrix.org",
"state_key": "",
"content": {
"name": "Cool New Room"
}
}
]
}
responses:
200:
description: |-
The event with the invited server's signature added. All other fields of the events
should remain untouched. Note that events have a different format depending on the
room version - check the `room version specification`_ for precise event formats.
schema:
type: object
description: An object containing the signed invite event.
title: Event Container
properties:
event:
$ref: "definitions/invite_event.yaml"
required: ['event']
examples:
application/json: {
"event": {
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"unsigned": {
"invite_room_state": [
{
"type": "m.room.join_rules",
"sender": "@someone:matrix.org",
"state_key": "",
"content": {
"join_rule": "public"
}
},
{
"type": "m.room.name",
"sender": "@someone:matrix.org",
"state_key": "",
"content": {
"name": "Cool New Room"
}
}
]
},
"content": {
"membership": "invite"
},
"signatures": {
"example.com": {
"ed25519:key_version": "SomeSignatureHere"
},
"elsewhere.com": {
"ed25519:k3y_versi0n": "SomeOtherSignatureHere"
}
}
}
}
403:
description: |-
The invite is not allowed. This could be for a number of reasons, including:
* The sender is not allowed to send invites to the target user/homeserver.
* The homeserver does not permit anyone to invite its users.
* The homeserver refuses to participate in the room.
schema:
$ref: "../client-server/definitions/errors/error.yaml"
examples:
application/json: {
"errcode": "M_FORBIDDEN",
"error": "User cannot invite the target user."
}
400:
description: |-
The request is invalid or the room the server is attempting
to join has a version that is not listed in the ``ver``
parameters.
The error should be passed through to clients so that they
may give better feedback to users.
schema:
allOf:
- $ref: "../client-server/definitions/errors/error.yaml"
- type: object
properties:
room_version:
type: string
description: |-
The version of the room. Required if the ``errcode``
is ``M_INCOMPATIBLE_ROOM_VERSION``.
examples:
application/json: {
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
"error": "Your homeserver does not support the features required to join this room",
"room_version": "3"
}

@ -61,100 +61,80 @@ paths:
responses:
200:
description: |-
A template to be used for the rest of the `Joining Rooms`_ handshake.
A template to be used for the rest of the `Joining Rooms`_ handshake. Note that
events have a different format depending on the room version - check the
`room version specification`_ for precise event formats. **The response body
here describes the common event fields in more detail and may be missing other
required fields for a PDU.**
schema:
type: object
properties:
room_version:
type: string
description: |-
The version of the room where the server is trying to join. If not provided,
the room version is assumed to be either "1" or "2".
example: "2"
event:
allOf:
- $ref: "definitions/unsigned_pdu.yaml"
- description: |-
An unsigned template event.
description: |-
An unsigned template event. Note that events have a different format
depending on the room version - check the `room version specification`_
for precise event formats.
type: object
title: Event Template
properties:
sender:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
origin:
type: string
description: The name of the resident homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the resident homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "join"}
properties:
# Note: we override a bunch of parameters to change their descriptions
sender:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
origin:
type: string
description: The name of the resident homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the resident homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "join"}
properties:
membership:
type: string
description: The value ``join``.
example: "join"
required: ['membership']
depth:
type: integer
description: This field must be present but is ignored; it may be 0.
example: 12
auth_events:
type: array
description: |-
An event reference list containing the authorization events that would
allow the member to join the room. This should normally be the
``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules``
events.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "abase64encodedsha256hashshouldbe43byteslong"
}
properties:
sha256:
type: string
description: The event hash.
example: abase64encodedsha256hashshouldbe43byteslong
required: ['sha256']
redacts:
membership:
type: string
description: Not used.
required:
# Every other field is already flagged as required by the $ref
- state_key
description: The value ``join``.
example: "join"
required: ['membership']
required:
- state_key
- origin
- origin_server_ts
- type
- content
- sender
examples:
application/json: {
event: {
"$ref": "examples/unsigned_pdu.json",
"room_version": "2",
"event": {
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "join"
},
"auth_events": [
["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}]
]
}
}
}
400:
@ -186,7 +166,12 @@ paths:
summary: Submit a signed join event to a resident server
description: |-
Submits a signed join event to the resident server for it
to accept it into the room's graph.
to accept it into the room's graph. Note that events have
a different format depending on the room version - check
the `room version specification`_ for precise event formats.
**The request and response body here describes the common
event fields in more detail and may be missing other required
fields for a PDU.**
operationId: sendJoin
security:
- signedRequest: []
@ -208,81 +193,54 @@ paths:
type: object
required: true
schema:
allOf:
- $ref: "definitions/pdu.yaml"
- type: object
type: object
properties:
sender:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
origin:
type: string
description: The name of the joining homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the joining homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "join"}
properties:
# Note: we override a bunch of parameters to change their descriptions
sender:
membership:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
origin:
type: string
description: The name of the joining homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the joining homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the joining member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "join"}
properties:
membership:
type: string
description: The value ``join``.
example: "join"
required: ['membership']
depth:
type: integer
description: This field must be present but is ignored; it may be 0.
example: 12
auth_events:
type: array
description: |-
An event reference list containing the authorization events that would
allow the member to join the room.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "abase64encodedsha256hashshouldbe43byteslong"
}
properties:
sha256:
type: string
description: The event hash.
example: abase64encodedsha256hashshouldbe43byteslong
required: ['sha256']
redacts:
type: string
description: Not used.
required:
# Every other field is already flagged as required by the $ref
- state_key
description: The value ``join``.
example: "join"
required: ['membership']
required:
- state_key
- sender
- origin
- origin_server_ts
- type
- content
example: {
"$ref": "examples/pdu.json",
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "join"
}
@ -308,25 +266,44 @@ paths:
description: The resident server's DNS name.
auth_chain:
type: array
description: The auth chain.
description: |-
The auth chain. Note that events have a different format depending on
the room version - check the `room version specification`_ for precise
event formats.
items:
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ that make up the auth chain. The event format varies depending
on the room version - check the `room version specification`_ for precise event formats.
schema:
$ref: "definitions/pdu.yaml"
type: object
properties: []
example:
$ref: "examples/minimal_pdu.json"
state:
type: array
description: The room state.
description: |-
The room state. The event format varies depending on the room version -
check the `room version specification`_ for precise event formats.
items:
type: object
title: PDU
description: |-
The `PDUs <#pdus>`_ for the fully resolved state of the room. The event format varies depending
on the room version - check the `room version specification`_ for precise event formats.
schema:
$ref: "definitions/pdu.yaml"
type: object
properties: []
example:
$ref: "examples/minimal_pdu.json"
required: ["auth_chain", "state", "origin"]
examples:
application/json: [
200,
{
"origin": "matrix.org",
"auth_chain": [{"$ref": "examples/pdu.json"}],
"state": [{"$ref": "examples/pdu.json"}]
"auth_chain": [{"$ref": "examples/minimal_pdu.json"}],
"state": [{"$ref": "examples/minimal_pdu.json"}]
}
]

@ -27,7 +27,7 @@ paths:
get:
summary: Get the homeserver's public key(s)
description: |-
Gets the homeserver's published TLS fingerprints and signing keys.
Gets the homeserver's published signing keys.
The homeserver may have any number of active keys and may have a
number of old keys.
@ -49,7 +49,7 @@ paths:
type: string
description: |-
**Deprecated**. Servers should not use this parameter and instead
opt to return all keys, not just the requested one. The key ID to
opt to return all keys, not just the requested one. The key ID to
look up.
required: false
x-example: "ed25519:abc123"

@ -52,97 +52,81 @@ paths:
responses:
200:
description: |-
A template to be used to call ``/send_leave``.
A template to be used to call ``/send_leave``. Note that
events have a different format depending on the room version - check the
`room version specification`_ for precise event formats. **The response body
here describes the common event fields in more detail and may be missing other
required fields for a PDU.**
schema:
schema:
type: object
properties:
room_version:
type: string
description: |-
The version of the room where the server is trying to leave. If not provided,
the room version is assumed to be either "1" or "2".
example: "2"
event:
allOf:
- $ref: "definitions/unsigned_pdu.yaml"
- description: |-
An unsigned template event.
description: |-
An unsigned template event. Note that events have a different format
depending on the room version - check the `room version specification`_
for precise event formats.
type: object
title: Event Template
properties:
sender:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
origin:
type: string
description: The name of the resident homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the resident homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "leave"}
properties:
# Note: we override a bunch of parameters to change their descriptions
sender:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
origin:
type: string
description: The name of the resident homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the resident homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "leave"}
properties:
membership:
type: string
description: The value ``leave``.
example: "leave"
required: ['membership']
auth_events:
type: array
description: |-
An event reference list containing the authorization events that would
allow the member to leave the room. This should normally be the
``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules``
events.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "abase64encodedsha256hashshouldbe43byteslong"
}
properties:
sha256:
type: string
description: The event hash.
example: abase64encodedsha256hashshouldbe43byteslong
required: ['sha256']
redacts:
membership:
type: string
description: Not used.
required:
# Every other field is already flagged as required by the $ref
- state_key
description: The value ``leave``.
example: "leave"
required: ['membership']
required:
- state_key
- sender
- origin
- origin_server_ts
- type
- content
examples:
application/json: {
"room_version": "2",
"event": {
"$ref": "examples/unsigned_pdu.json",
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "leave"
},
"auth_events": [
["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}]
]
}
}
}
403:
@ -160,7 +144,12 @@ paths:
summary: Submit a signed leave event to a resident server
description: |-
Submits a signed leave event to the resident server for it
to accept it into the room's graph.
to accept it into the room's graph. Note that events have
a different format depending on the room version - check
the `room version specification`_ for precise event formats.
**The request and response body here describes the common
event fields in more detail and may be missing other required
fields for a PDU.**
operationId: sendLeave
security:
- signedRequest: []
@ -182,81 +171,59 @@ paths:
type: object
required: true
schema:
allOf:
- $ref: "definitions/pdu.yaml"
- type: object
type: object
properties:
sender:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
origin:
type: string
description: The name of the leaving homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the leaving homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "leave"}
properties:
# Note: we override a bunch of parameters to change their descriptions
sender:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
origin:
type: string
description: The name of the leaving homeserver.
example: "matrix.org"
origin_server_ts:
type: integer
format: int64
description: A timestamp added by the leaving homeserver.
example: 1234567890
type:
type: string
description: The value ``m.room.member``.
example: "m.room.member"
state_key:
membership:
type: string
description: The user ID of the leaving member.
example: "@someone:example.org"
content:
type: object
title: Membership Event Content
description: The content of the event.
example: {"membership": "leave"}
properties:
membership:
type: string
description: The value ``leave``.
example: "leave"
required: ['membership']
depth:
type: integer
description: This field must be present but is ignored; it may be 0.
example: 12
auth_events:
type: array
description: |-
An event reference list containing the authorization events that would
allow the member to leave the room.
items:
type: array
maxItems: 2
minItems: 2
items:
- type: string
title: Event ID
example: "$abc123:matrix.org"
- type: object
title: Event Hash
example: {
"sha256": "abase64encodedsha256hashshouldbe43byteslong"
}
properties:
sha256:
type: string
description: The event hash.
example: abase64encodedsha256hashshouldbe43byteslong
required: ['sha256']
redacts:
type: string
description: Not used.
required:
# Every other field is already flagged as required by the $ref
- state_key
description: The value ``leave``.
example: "leave"
required: ['membership']
depth:
type: integer
description: This field must be present but is ignored; it may be 0.
example: 12
required:
- state_key
- sender
- origin
- origin_server_ts
- type
- depth
- content
example: {
"$ref": "examples/pdu.json",
"$ref": "examples/minimal_pdu.json",
"type": "m.room.member",
"state_key": "@someone:example.org",
"origin": "example.org",
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "leave"
}

@ -33,7 +33,7 @@ paths:
User ID of the owner.
operationId: exchangeOpenIdToken
parameters:
- in: path
- in: query
name: access_token
type: string
description: |-

@ -85,6 +85,7 @@ paths:
third_party_invite:
type: object
description: The third party invite
title: Third Party Invite
properties:
display_name:
type: string
@ -97,6 +98,7 @@ paths:
description: |-
A block of content which has been signed, which servers can use to
verify the event.
title: Invite Signatures
properties:
signatures:
type: object

@ -37,6 +37,9 @@ paths:
The sending server must wait and retry for a 200 OK response before sending a
transaction with a different ``txnId`` to the receiving server.
Note that events have a different format depending on the room version - check
the `room version specification`_ for precise event formats.
operationId: sendTransaction
security:
- signedRequest: []

@ -0,0 +1,52 @@
# Copyright 2019 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Federation Server Discovery API"
version: "1.0.0"
host: localhost:443
schemes:
- https
basePath: /.well-known
produces:
- application/json
paths:
"/matrix/server":
get:
summary: Gets information about the delegated server for server-server communication.
description: |-
Gets information about the delegated server for server-server communication
between Matrix homeservers. Servers should follow 30x redirects, carefully
avoiding redirect loops, and use normal X.509 certificate validation.
responses:
200:
description:
The delegated server information. The ``Content-Type`` for this response SHOULD
be ``application/json``, however servers parsing the response should assume that
the body is JSON regardless of type. Failures parsing the JSON or invalid data
provided in the resulting parsed JSON should not result in discovery failure -
consult the server discovery process for information on how to continue.
examples:
application/json: {
"m.server": "delegated.example.com:1234"
}
schema:
type: object
properties:
"m.server":
type: string
description: |-
The server name to delegate server-server communciations to, with optional
port. The delegated server name uses the same grammar as
`server names in the appendices <../appendices.html#server-name>`_.

@ -0,0 +1 @@
Add support for advertising experimental features to clients.

@ -0,0 +1 @@
Add a mechanism for servers to redirect clients to an alternative homeserver after logging in.

@ -0,0 +1 @@
Remove references to presence lists.

@ -0,0 +1 @@
Support optional features by having clients query for capabilities.

@ -0,0 +1 @@
Fix various spelling mistakes throughout the specification.

@ -0,0 +1 @@
Fix various spelling mistakes throughout the specification.

@ -0,0 +1 @@
Fix various spelling mistakes throughout the specification.

@ -0,0 +1 @@
``GET /account_data`` routes.

@ -0,0 +1 @@
Add ``M_RESOURCE_LIMIT_EXCEEDED`` as an error code for when homeservers exceed limits imposed on them.

@ -0,0 +1 @@
Support optional features by having clients query for capabilities.

@ -0,0 +1 @@
Add the missing `m.push_rules` event schema.

@ -0,0 +1 @@
Emit ``M_UNSUPPORTED_ROOM_VERSION`` error codes where applicable on ``/createRoom`` and ``/invite`` APIs.

@ -0,0 +1 @@
Fix various spelling mistakes throughout the specification.

@ -0,0 +1 @@
Fix various spelling mistakes throughout the specification.

@ -0,0 +1,16 @@
r0.1.1
======
Spec Clarifications
-------------------
- Remove legacy references to TLS fingerprints. (`#1844 <https://github.com/matrix-org/matrix-doc/issues/1844>`_)
- Clarify that servers should not fail to contact servers if ``/.well-known`` fails. (`#1855 <https://github.com/matrix-org/matrix-doc/issues/1855>`_)
r0.1.0
======
This is the first release of the Server Server (Federation) specification.
It includes support for homeservers being able to interact with other
homeservers in a decentralized and standard way.

@ -0,0 +1 @@
Fix the `access_token` parameter in the open_id endpoint.

@ -0,0 +1,192 @@
{
"$ref": "core/event.json",
"type": "m.push_rules",
"content": {
"global": {
"content": [
{
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight"
}
],
"default": true,
"enabled": true,
"pattern": "alice",
"rule_id": ".m.rule.contains_user_name"
}
],
"override": [
{
"actions": [
"dont_notify"
],
"conditions": [],
"default": true,
"enabled": false,
"rule_id": ".m.rule.master"
},
{
"actions": [
"dont_notify"
],
"conditions": [
{
"key": "content.msgtype",
"kind": "event_match",
"pattern": "m.notice"
}
],
"default": true,
"enabled": true,
"rule_id": ".m.rule.suppress_notices"
}
],
"room": [],
"sender": [],
"underride": [
{
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "ring"
},
{
"set_tweak": "highlight",
"value": false
}
],
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.call.invite"
}
],
"default": true,
"enabled": true,
"rule_id": ".m.rule.call"
},
{
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight"
}
],
"conditions": [
{
"kind": "contains_display_name"
}
],
"default": true,
"enabled": true,
"rule_id": ".m.rule.contains_display_name"
},
{
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight",
"value": false
}
],
"conditions": [
{
"is": "2",
"kind": "room_member_count"
}
],
"default": true,
"enabled": true,
"rule_id": ".m.rule.room_one_to_one"
},
{
"actions": [
"notify",
{
"set_tweak": "sound",
"value": "default"
},
{
"set_tweak": "highlight",
"value": false
}
],
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
},
{
"key": "content.membership",
"kind": "event_match",
"pattern": "invite"
},
{
"key": "state_key",
"kind": "event_match",
"pattern": "@alice:example.com"
}
],
"default": true,
"enabled": true,
"rule_id": ".m.rule.invite_for_me"
},
{
"actions": [
"notify",
{
"set_tweak": "highlight",
"value": false
}
],
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.member"
}
],
"default": true,
"enabled": true,
"rule_id": ".m.rule.member_event"
},
{
"actions": [
"notify",
{
"set_tweak": "highlight",
"value": false
}
],
"conditions": [
{
"key": "type",
"kind": "event_match",
"pattern": "m.room.message"
}
],
"default": true,
"enabled": true,
"rule_id": ".m.rule.message"
}
]
}
}
}

@ -5,6 +5,10 @@
"content": {
"creator": "@example:example.org",
"room_version": "1",
"m.federate": true
"m.federate": true,
"predecessor": {
"event_id": "$something:example.org",
"room_id": "!oldroom:example.org"
}
}
}

@ -0,0 +1,9 @@
{
"$ref": "core/state_event.json",
"type": "m.room.tombstone",
"state_key": "",
"content": {
"body": "This room has been replaced",
"replacement_room": "!newroom:example.org"
}
}

@ -0,0 +1,21 @@
---
allOf:
- $ref: core-event-schema/event.yaml
description: Describes all push rules for this user.
properties:
content:
properties:
global:
type: object
title: Ruleset
description: The global ruleset
allOf:
- $ref: ../../api/client-server/definitions/push_ruleset.yaml
type: object
type:
enum:
- m.push_rules
type: string
title: Push rules
type: object

@ -14,6 +14,18 @@ properties:
room_version:
description: The version of the room. Defaults to ``"1"`` if the key does not exist.
type: string
predecessor:
description: A reference to the room this room replaces, if the previous room was upgraded.
type: object
title: Previous Room
properties:
room_id:
type: string
description: The ID of the old room.
event_id:
type: string
description: The event ID of the last known event in the old room.
required: [room_id, event_id]
required:
- creator
type: object

@ -0,0 +1,27 @@
---
allOf:
- $ref: core-event-schema/state_event.yaml
description: 'A state event signifying that a room has been upgraded to a different room version, and that clients should go there.'
properties:
content:
properties:
body:
type: string
description: A server-defined message.
replacement_room:
type: string
description: The new room the client should be visiting.
required:
- replacement_room
- body
type: object
state_key:
description: A zero-length string.
pattern: '^$'
type: string
type:
enum:
- m.room.tombstone
type: string
title: Indication that the room has been upgraded.
type: object

@ -18,16 +18,18 @@ The remainder of the process is as follows:
1. Activate your Python 3 virtual environment.
1. Having checked out the new release branch, navigate your way over to `./changelogs`.
1. Follow the release instructions provided in the README.md located there.
1. Update the changelog section of the specification you're releasing to make a reference
to the new version.
1. Update any version/link references across all specifications.
1. Ensure the `targets.yml` file lists the version correctly.
1. Commit the changes and PR them to master.
1. Tag the release with the format `client_server/r0.4.0`.
1. Add the changes to the matrix-org/matrix.org repository (for historic tracking).
1. Generate the specification using `./scripts/gendoc.py`, specifying all the
API versions at the time of generation. For example: `./scripts/gendoc.py -c r0.4.0 -s r0.1.0 -i r0.1.0 #etc`
1. PR the changes to the matrix-org/matrix.org repository (for historic tracking).
* This is done by making a PR to the `unstyled_docs/spec` folder for the version and
specification you're releasing.
* Don't forget to symlink the new release as `latest`.
* For the client-server API, don't forget to generate the swagger JSON by using
`./scripts/dump-swagger.py -c r0.4.0`. This will also need symlinking to `latest`.
1. Commit the changes and PR them to master. **Wait for review from the spec core team.**
* Link to your matrix-org/matrix.org so both can be reviewed at the same time.
1. Tag the release with the format `client_server/r0.4.0`.
1. Perform a release on GitHub to tag the release.
1. Yell from the mountaintop to the world about the new release.

@ -48,7 +48,7 @@ When this is called, the server:
"state_key": "",
"room_id": "!QtykxKocfsgujksjgd:matrix.org",
"content": {
"version": "2",
"room_version": "2",
"predecessor": {
"room_id": "!cURbaf:matrix.org",
"event_id": "$1235135aksjgdkg:matrix.org"

@ -0,0 +1,130 @@
# Changing Event IDs to be Hashes
## Motivation
Having event IDs separate from the hashes leads to issues when a server receives
multiple events with the same event ID but different reference hashes. While
APIs could be changed to better support dealing with this situation, it is
easier and nicer to simply drop the idea of a separate event ID entirely, and
instead use the reference hash of an event as its ID.
## Identifier Format
Currently hashes in our event format include the hash name, allowing servers to
choose which hash functions to use. The idea here was to allow a gradual change
between hash functions without the need to globally coordinate shifting from one
hash function to another.
However now that room versions exist, changing hash functions can be achieved by
bumping the room version. Using this method would allow using a simple string as
the event ID rather than a full structure, significantly easing their usage.
One side effect of this would be that there would be no indication about which
hash function was actually used, and it would need to be inferred from the room
version. To aid debuggability it may be worth encoding the hash function into
the ID format.
**Conclusion:** Don't encode the hash function, since the hash will depend on
the version specific redaction algorithm anyway.
The proposal is therefore that the event IDs are a sha256 hash, encoded using
[unpadded
Base64](https://matrix.org/docs/spec/appendices.html#unpadded-base64), and
prefixed with `$` (to aid distinguishing different types of identifiers). For
example, an event ID might be: `$CD66HAED5npg6074c6pDtLKalHjVfYb2q4Q3LZgrW6o`.
The hash is calculated in the same way as previous event reference hashes were,
which is:
1. Redact the event
2. Remove `signatures` field from the event
3. Serialize the event to canonical JSON
4. Compute the hash of the JSON bytes
Event IDs will no longer be included as part of the event, and so must be
calculated by servers receiving the event.
## Changes to Event Formats
As well as changing the format of event IDs, we also change the format of the
`auth_events` and `prev_events` keys in events to simply be lists of event IDs
(rather than being lists of tuples).
A full event would therefore look something like (note that this is just an
illustrative example, and that the hashes are not correct):
```json
{
"auth_events": [
"$5hdALbO+xIhzcLTxCkspx5uqry9wO8322h/OI9ApnHE",
"$Ga0DBIICBsWIZbN292ATv8fTHIGGimwjb++w+zcHLRo",
"$zc4ip/DpPI9FZVLM1wN9RLqN19vuVBURmIqAohZ1HXg",
],
"content": {
"body": "Here is the message content",
"msgtype": "m.message"
},
"depth": 6,
"hashes": {
"sha256": "M6/LmcMMJKc1AZnNHsuzmf0PfwladVGK2Xbz+sUTN9k"
},
"origin": "localhost:8800",
"origin_server_ts": 1548094046693,
"prev_events": [
"$MoOzCuB/sacqHAvgBNOLICiGLZqGT4zB16MSFOuiO0s",
],
"room_id": "!eBrhCHJWOgqrOizwwW:localhost:8800",
"sender": "@anon-20190121_180719-33:localhost:8800",
"signatures": {
"localhost:8800": {
"ed25519:a_iIHH": "N7hwZjvHyH6r811ebZ4wwLzofKhJuIAtrQzaD3NZbf4WQNijXl5Z2BNB047aWIQCS1JyFOQKPVom4et0q9UOAA"
}
},
"type": "m.room.message"
}
```
## Changes to existing APIs
All APIs that accept event IDs must accept event IDs in the new format.
For S2S API, whenever a server needs to parse an event from a request or
response they must either already know the room version *or* be told the room
version in the request/response. There are separate MSCs to update APIs where
necessary.
For C2S API, the only change clients will see is that the event IDs have changed
format. Clients should already be treating event IDs as opaque strings, so no
changes should be required. Servers must add the `event_id` when sending the
event to clients, however.
Note that the `auth_events` and `prev_events` fields aren't sent to clients, and
so the changes proposed above won't affect clients.
## Protocol Changes
The `auth_events` and `prev_events` fields on an event need to be changed from a
list of tuples to a list of strings, i.e. remove the old event ID and simply
have the list of hashes.
The auth rules also need to change:
- The event no longer needs to be signed by the domain of the event ID (but
still needs to be signed by the senders domain)
- We currently allow redactions if the domain of the redaction event ID
matches the domain of the event ID it is redacting; which allows self
redaction. This check is removed and redaction events are always accepted.
Instead, the redaction event only takes effect and is sent down to clients
if/when the original event is received, and the domain of the events'
senders match. (While this is clearly suboptimal, it is the only practical
suggestion)
## Room Version
There will be a new room version v3 that is the same as v2 except uses the new
event format proposed above. v3 will be marked as 'stable' as defined in [MSC1804](https://github.com/matrix-org/matrix-doc/blob/travis/msc/room-version-client-advertising/proposals/1804-advertising-capable-room-versions.md)

@ -43,9 +43,9 @@ certificate validation, and following 30x redirects (being careful to avoid
redirect loops). If the request does not return a 200, continue to step 4,
otherwise:
The response must have a `Content-Type` of `application/json`, and must be
valid JSON which follows the structure documented below. Otherwise, the
request is aborted.
The response must be valid JSON which follows the structure documented
below. Otherwise, continue to the next step in the discovery process. It is
NOT necessary for the response to have a `Content-Type` of `application/json`.
If the response is valid, the `m.server` property is parsed as
`<delegated_server_name>[:<delegated_port>]`, and processed as follows:

@ -40,8 +40,10 @@ As a starting point, a single capability identifier is proposed:
change the user's password via the `POST /_matrix/client/r0/account/password`
API.
The value of the `capabilities` object in the response should be the empty
object.
The value of the `capabilities` object in the response should contain a single
boolean flag, `enabled`, to indicate whether a password change is possible. If
the capability is not listed, the client should assume that password changes
are possible.
### Fallback behaviour

@ -0,0 +1,78 @@
# MSC 1794 - Federation v2 Invite API
This proposal adds a new `/invite` API to federation that supports different
room versions.
## Motivation
It is planned for future room versions to be able to change the format of events
in various ways. To support this, all servers must know the room version
whenever they need to parse an event. Currently the `/invite` API does not
include the room version, so the target server would be unable to parse the event included in the payload.
## Proposal
Add a new version of the invite API under the prefix `/_matrix/federation/v2`,
which has a payload of:
```
PUT /_matrix/federation/v2/invite/<room_id>/<event_id>
{
"room_version": <room_version>,
"event": { ... },
"invite_room_state": [ ... ]
}
```
The differences between this and `v1` are:
1. The payload in `v1` is the event, while in `v2` the event is instead placed
under an `"event"` key. This is for consistency with other APIs, and to allow
extra data to be added to the request payload separately from the event.
2. A required field called `"room_version"` is added that specifies the room
version.
3. The `"invite_room_state"` is moved from the `unsigned` section of the event
to a top level key. The key `invite_room_state` being in the `event.unsigned`
was a hack due to point 1. above.
The response is identical to `v1`, except that:
1. If the receiving server does not support the given room version the
appropriate incompatible-room-version error is returned, in the same way
as e.g. for `/make_join` APIs.
2. The response payload is no longer in the format of `[200, { ... }]`, and is
instead simply the `{ ... }` portion. This fixes a historical accident to
bring the invite API into line with the rest of the federation API.
If a call to `v2` `/invite` results in an unrecognised request exception **AND**
the room version is `1` or `2` then the sending server should retry the request
with the `v1` API.
## Alternatives
### Reusing V1 API
One alternative is to add a `room_version` query string parameter to the `v1`
`/invite` API in a similar way as for the `/make_join` APIs. However, older
servers would ignore the query string parameter while processing an incoming
`/invite` request, resulting in the server attempting to parse the event in the
old `v1` format. This would likely result in either a `400` or `500` response,
which the sending server could interpret as the receiving server not supporting
the room version.
This method, however, is fragile and could easily mask legitimate `400` and
`500` errors that are not due to not supporting the room version.
### Using V1 API for V1 room versions
Instead of all servers attempting to use the new API and falling back if the API
is not found, servers could instead always use the current API for V1 and V2
rooms.
However, this would not allow us to deprecate the `v1` API.

@ -0,0 +1,50 @@
# Proposal for advertising capable room versions to clients
Currently clients need to guess at which room versions the server supports, if any. This is particularly
difficult to do as it generally requires knowing the state of the ecosystem and what versions are
available and how keen users are to upgrade their servers and clients. The impossible judgement call
for when to encourage people to upgrade shouldn't be impossible, or even a judgement call.
## Proposal
Building off of [MSC1753](https://github.com/matrix-org/matrix-doc/pull/1753) (capabilities API) and
the [recommendations laid out for room versions](https://github.com/matrix-org/matrix-doc/pull/1773/files#diff-1436075794bb304492ca6953a6692cd0R463),
this proposal suggests a `m.room_versions` capability be introduced like the following:
```json
{
"capabilities": {
"m.room_versions": {
"default": "1",
"available": {
"1": "stable",
"2": "stable",
"state-res-v2-test": "unstable",
"event-ids-as-hashes": "unstable",
"3": "future-scifi-label"
}
}
}
}
```
Clients are encouraged to make use of this capability to determine if the server supports a given
version, and at what level of stability. Anything not flagged explicitly as `stable` should be treated
as `unstable` (ie: `future-scifi-label` is the same as `unstable`).
The default version is the version that the server is using to create new rooms with. Clients can
make the assumption that the default version is a stable version.
Clients should encourage people with sufficient permissions to perform an upgrade to upgrade their
rooms to the `default` room version when the room is using an `unstable` version.
## Potential issues
Changes aren't pushed to the client, which means clients may want to poll this endpoint on some
heuristic instead. For example, clients may want to poll the endpoint weekly or when the user relaunches
the client. Clients may also wish to provide users a way to upgrade without considering the capabilities
of the server, expecting that the server may not support the user-provided version - the intention
being such a feature would be used by eager room administrators which do not want to relaunch their
client, for example.

@ -0,0 +1,21 @@
# MSC 1813 - Federation Make Membership Room Version
This proposal adds a new `room_version` field to the responses of `/make_leave`
and `/make_join` APIs.
## Motivation
It is planned for future room versions to be able to change the format of events
in various ways. To support this, all servers must know the room version
whenever they need to parse an event. Currently the `/make_*` APIs do not
include the room version in the response, so the requesting server is unable to
parse the event included in the response body.
## Proposal
Add a new `room_version` field to the response body of the `v1` `/make_join` and
`/make_leave` APIs, which describes the room version.
For backwards compatibility servers must correctly handle responses that do not
include the new field. In which case the room version is assumed to be one of
either `1` or `2`.

@ -0,0 +1,55 @@
# Remove references to presence lists
[Presence](https://matrix.org/docs/spec/client_server/r0.4.0.html#id107) lists
allow a given user the ability to subscribe to other users and be alerted to
their current presence status.
While spec'd, no established client has implemented support and the only server
side implementation raises privacy concerns.
The proposal is to simply remove references to presence lists with a view to
revisiting the same ideas in the future.
This MSC addresses
[#1810](https://github.com/matrix-org/matrix-doc/issues/1810)
## Proposal
Presence lists seem like a good fit for ['MSC1769: Extensible profiles as
rooms'](https://github.com/matrix-org/matrix-doc/pull/1769) proposal, meaning
that the current design will most likely be superceded.
Additionally, no major client has implemented the behaviour to date and the
only server implementation of presence lists (Synapse) auto-accepts invites
leading to privacy concerns in the wild.
With this in mind the most pragmatic solution is to remove presence lists ahead
of the r0 release.
Specifically:-
CS API: Remove
* [POST
/_matrix/client/r0/presence/list/{userId}](https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-presence-list-userid)
* [GET
/_matrix/client/r0/presence/list/{userId}](https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-presence-list-userid)
SS API: Remove
* [m.presence_invite](https://matrix.org/docs/spec/server_server/unstable.html#m-presence-invite-schema)
* [m.presence_accept](https://matrix.org/docs/spec/server_server/unstable.html#m-presence-accept-schema)
* [m.presence_deny](https://matrix.org/docs/spec/server_server/unstable.html#m-presence-deny-schema)
## Tradeoffs
Ideally this proposal would also come with an alternative design for this
functionality. Out of pragmatism the proposal only covers removal of what is
there today.
## Conclusions
This is a common sense attempt to remove unused portions of the spec ahead of
an r0 release. It does not suggest that the ability to subscribe to the
presence of others is undesirable and assumes that this behvaiour will return
again in some form.

@ -0,0 +1,25 @@
# Proposal to do SRV lookups after .well-known to discover homeservers
Currently there is a logistical error proposed by [MSC1708](https://github.com/matrix-org/matrix-doc/pull/1708)
which results in some homeservers unable to migrate to the new functionality
proposed by [MSC1711](https://github.com/matrix-org/matrix-doc/pull/1711). This
can happen if the delegated homeserver cannot obtain a valid TLS certificate for
the domain, and an SRV record is used for backwards compatibility reasons.
Specifically, in order to be compatible with requests from both Synapse 0.34 and 1.0,
servers can have both a SRV and a .well-known file, with Synapse presenting a certificate
corresponding to the target of the .well-known. Synapse 0.34 is then happy because it
will follow the SRV (and won't care about the incorrect certificate); Synapse 1.0 is
happy because it will follow the .well-known (and will see the correct cert).
## Proposal
We change the order of operations to perform a .well-known lookup before falling
back to resolving the SRV record. This allows for domains to delegate to other
hostnames and maintains backwards compatibility with older homeservers.
## Tradeoffs
More HTTP hits will be made due to the .well-known lookup being first. This is
somewhat mitigated by servers caching the responses appropriately, and using
connection pools where possible.

@ -0,0 +1,14 @@
# MSC 1866 - Unsupported Room Version Error Code for Invites
It is currently unspecified what error code should be relayed to clients when
they attempt to invite a user on a remote server that does not support the room
version.
The proposal is to reuse the `M_UNSUPPORTED_ROOM_VERSION` error code that is
currently returned by the create room API.
Strictly, the error returned by the create room API would mean the local server
didn't support the room version, while for the invite API it would mean the
remote server didn't. However, there is sufficient overlap that it makes sense
to reuse the same error code and rely on the context to differentiate the two
cases.

@ -0,0 +1,107 @@
# MSC 1915 - Add unbind 3PID APIs
Note that this is a simplified version of MSC1194.
## Motivation
Currently we do not have a reasonable route for a user to unbind/remove a 3PID
from their account, particularly when deactivating their account. Users have an
expectation to be able to do this, and thus we should have an API to provide it.
This is meant as a simple extension to the current APIs, and so this explicitly
does not try and solve any existing usability concerns.
## API Changes
### Client-Server 3PID Delete API
Add an `id_server` param to `POST /_matrix/client/r0/account/3pid/delete` API,
which matches the 3PID creation APIs.
The new `id_server` parameter is optional and if missing the server will attempt
to unbind from the identity server used when originally binding the 3pid (if
known by the homeserver).
The 200 response is a JSON object with an `id_server_unbind_result` field whose
value is either `success` or `no-support`, where the latter indicates that the
identity server (IS) does not support unbinding 3PIDs directly. If the identity
server returns an error then that should be returned to the client.
Example:
```
POST /_matrix/client/r0/account/3pid/delete HTTP/1.1
{
"medium": "email",
"address": "foobar@example.com",
"id_server": "https://matrix.org
}
HTTP/1.1 200 OK
{
"id_server_unbind_result": "success"
}
```
### Client-Server Deactivate account API
Add an `id_server` param to `POST /_matrix/client/r0/account/deactivate` API,
with the same semantics as above. This is used to unbind any bound threepids
from the given identity server.
### Identity Server 3PID Unbind API
Add `POST /_matrix/identity/api/v1/unbind` with `mxid` and `threepid` fields.
The `mxid` is the user's `user_id` and `threepid` is a dict with the usual
`medium` and `address` fields.
If the server returns a 400, 404 or 501 HTTP error code then the homeserver
should assume that the identity server doesn't support the `/unbind` API, unless
it returns a specific matrix error response (i.e. the body is a JSON object with
`error` and `errcode` fields).
The identity server should authenticate the request in one of two ways:
1. The request is signed by the homeserver which controls the `user_id`.
2. The request includes the `sid` and `client_secret` params (as per `/bind`),
which proves ownership of the given 3PID.
Example:
```
POST /_matrix/identity/api/v1/unbind HTTP/1.1
{
"mxid": "@foobar:example.com",
"threepid": {
"medium": "email",
"address": "foobar@example.com"
}
}
HTTP/1.1 200 OK
{}
```
# Trade-offs
A homeserver can unbind any 3PID associated with one of its users, and
specifically does not require a re-validation of control of the 3PID. This means
that users have to trust that their homeserver will not arbitrarily remove valid
3PIDs, however users must already trust their homeserver to a large extent. The
flip side is that this provides a mechanism for homeservers and users to remove
3PIDs directed at their user IDs that they no longer (or never did) have control
over.
Removing a 3PID does not require user interactive auth (UIA), which opens a
potential attack whereby a logged in device can remove all associated 3PIDs and
then log out all devices. If the user has forgotten their password they would no
longer be able to reset their password via a 3PID (e.g. email), resulting in
losing access to their account. However, given that clients and servers have
implemented these APIs in the wild this is considered a sufficient edge case
that adding UIA is unlikely to be worthwhile.

@ -457,7 +457,7 @@ def main(targets, dest_dir, keep_intermediates, substitutions):
rst_file = os.path.join(tmp_dir, "spec_%s.rst" % (target_name,))
if version_label:
d = os.path.join(dest_dir, target_name)
d = os.path.join(dest_dir, target_name.split('@')[0])
if not os.path.exists(d):
os.mkdir(d)
html_file = os.path.join(d, "%s.html" % version_label)

@ -17,8 +17,11 @@ from batesian.sections import Sections
import inspect
import json
import os
import logging
logger = logging.getLogger(__name__)
class MatrixSections(Sections):
# pass through git ver so it'll be dropped in the input file
@ -28,26 +31,14 @@ class MatrixSections(Sections):
def render_git_rev(self):
return self.units.get("git_version")["revision"]
def render_client_server_changelog(self):
changelogs = self.units.get("changelogs")
return changelogs["client_server"]
# TODO: We should make this a generic variable instead of having to add functions all the time.
def render_push_gateway_changelog(self):
changelogs = self.units.get("changelogs")
return changelogs["push_gateway"]
def render_identity_service_changelog(self):
changelogs = self.units.get("changelogs")
return changelogs["identity_service"]
def render_server_server_changelog(self):
changelogs = self.units.get("changelogs")
return changelogs["server_server"]
def render_application_service_changelog(self):
def render_changelogs(self):
rendered = {}
changelogs = self.units.get("changelogs")
return changelogs["application_service"]
for spec, changelog_text in changelogs.items():
spec_var = "%s_changelog" % spec
logger.info("Rendering changelog for spec: %s" % spec)
rendered[spec_var] = changelog_text
return rendered
def _render_events(self, filterFn, sortFn):
template = self.env.get_template("events.tmpl")

@ -774,7 +774,7 @@ class MatrixUnits(Units):
"Privileged server plugins",
), TypeTableRow(
"`Identity Service API <identity_service/"+is_ver+".html>`_",
"unstable",
is_ver,
"Mapping of third party IDs to Matrix IDs",
), TypeTableRow(
"`Push Gateway API <push_gateway/"+push_gw_ver+".html>`_",
@ -903,74 +903,112 @@ class MatrixUnits(Units):
return schema
def load_changelogs(self):
def load_changelogs(self, substitutions):
"""Loads the changelog unit for later rendering in a section.
Args:
substitutions: dict of variable name to value. Provided by the gendoc script.
Returns:
A dict of API name ("client_server", for example) to changelog.
"""
changelogs = {}
for f in os.listdir(CHANGELOG_DIR):
if not f.endswith(".rst"):
continue
path = os.path.join(CHANGELOG_DIR, f)
name = f[:-4]
# If there's a directory with the same name, we'll try to generate
# a towncrier changelog and prepend it to the general changelog.
tc_path = os.path.join(CHANGELOG_DIR, name)
tc_lines = []
if os.path.isdir(tc_path):
logger.info("Generating towncrier changelog for: %s" % name)
p = subprocess.Popen(
['towncrier', '--version', 'Unreleased Changes', '--name', name, '--draft'],
cwd=tc_path,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
stdout, stderr = p.communicate()
if p.returncode != 0:
# Something broke - dump as much information as we can
logger.error("Towncrier exited with code %s" % p.returncode)
logger.error(stdout.decode('UTF-8'))
logger.error(stderr.decode('UTF-8'))
raw_log = ""
else:
raw_log = stdout.decode('UTF-8')
# The APIs and versions we'll prepare changelogs for. We use the substitutions
# to ensure that we pick up the right version for generated documentation. This
# defaults to "unstable" as a version for incremental generated documentation (CI).
prepare_versions = {
"server_server": substitutions.get("%SERVER_RELEASE_LABEL%", "unstable"),
"client_server": substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable"),
"identity_service": substitutions.get("%IDENTITY_RELEASE_LABEL%", "unstable"),
"push_gateway": substitutions.get("%PUSH_GATEWAY_RELEASE_LABEL%", "unstable"),
"application_service": substitutions.get("%APPSERVICE_RELEASE_LABEL%", "unstable"),
}
# This is a bit of a hack, but it does mean that the log at least gets *something*
# to tell us it broke
if not raw_log.startswith("Unreleased Changes"):
logger.error("Towncrier appears to have failed to generate a changelog")
logger.error(raw_log)
raw_log = ""
tc_lines = raw_log.splitlines()
# Changelogs are split into two places: towncrier for the unstable changelog and
# the RST file for historical versions. If the prepare_versions dict above has
# a version other than "unstable" specified for an API, we'll use the historical
# changelog and otherwise generate the towncrier log in-memory.
title_part = None
for api_name, target_version in prepare_versions.items():
logger.info("Generating changelog for %s at %s" % (api_name, target_version,))
changelog_lines = []
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()
if target_version == 'unstable':
# generate towncrier log
changelog_lines = self._read_towncrier_changelog(api_name)
else:
# read in the existing RST changelog
changelog_lines = self._read_rst_changelog(api_name)
# Parse the changelog lines to find the header we're looking for and therefore
# the changelog body.
prev_line = None
for line in (tc_lines + lines):
title_part = None
changelog_body_lines = []
for line in changelog_lines:
if prev_line is None:
prev_line = line
continue
if not title_part:
# find the title underline (at least 3 =)
if re.match("^[=]{3,}$", line.strip()):
title_part = prev_line
continue
prev_line = line
else: # have title, get body (stop on next title or EOF)
if re.match("^[=]{3,}$", line.strip()):
# we added the title in the previous iteration, pop it
# then bail out.
changelog_lines.pop()
break
# Don't generate subheadings (we'll keep the title though)
if re.match("^[-]{3,}$", line.strip()):
continue
changelog_lines.append(" " + line + '\n')
changelogs[name] = "".join(changelog_lines)
if re.match("^[=]{3,}$", line.strip()):
# the last line was a header - use that as our new title_part
title_part = prev_line.strip()
continue
if re.match("^[-]{3,}$", line.strip()):
# the last line is a subheading - drop this line because it's the underline
# and that causes problems with rendering. We'll keep the header text though.
continue
if line.strip().startswith(".. "):
# skip comments
continue
if title_part == target_version:
# if we made it this far, append the line to the changelog body. We indent it so
# that it renders correctly in the section. We also add newlines so that there's
# intentionally blank lines that make rst2html happy.
changelog_body_lines.append(" " + line + '\n')
if len(changelog_body_lines) > 0:
changelogs[api_name] = "".join(changelog_body_lines)
else:
raise ValueError("No changelog for %s at %s" % (api_name, target_version,))
# return our `dict[api_name] => changelog` as the last step.
return changelogs
def _read_towncrier_changelog(self, api_name):
tc_path = os.path.join(CHANGELOG_DIR, api_name)
if os.path.isdir(tc_path):
logger.info("Generating towncrier changelog for: %s" % api_name)
p = subprocess.Popen(
['towncrier', '--version', 'unstable', '--name', api_name, '--draft'],
cwd=tc_path,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
stdout, stderr = p.communicate()
if p.returncode != 0:
# Something broke - dump as much information as we can
logger.error("Towncrier exited with code %s" % p.returncode)
logger.error(stdout.decode('UTF-8'))
logger.error(stderr.decode('UTF-8'))
raw_log = ""
else:
raw_log = stdout.decode('UTF-8')
# This is a bit of a hack, but it does mean that the log at least gets *something*
# to tell us it broke
if not raw_log.startswith("unstable"):
logger.error("Towncrier appears to have failed to generate a changelog")
logger.error(raw_log)
raw_log = ""
return raw_log.splitlines()
return []
def _read_rst_changelog(self, api_name):
logger.info("Reading changelog RST for %s" % api_name)
rst_path = os.path.join(CHANGELOG_DIR, "%s.rst" % api_name)
with open(rst_path, 'r', encoding="utf-8") as f:
return f.readlines()
def load_unstable_warnings(self, substitutions):
warning = """
.. WARNING::

@ -52,6 +52,6 @@ Examples of strings encoded using unpadded Base64::
UNPADDED_BASE64("foobar") = "Zm9vYmFy"
When decoding Base64, implementations SHOULD accept input with or without
padding characters whereever possible, to ensure maximum interoperability.
padding characters wherever possible, to ensure maximum interoperability.
.. _`RFC 4648`: https://tools.ietf.org/html/rfc4648

@ -16,6 +16,12 @@
Identifier Grammar
------------------
Some identifiers are specific to given room versions, please refer to the
`room versions specification`_ for more information.
.. _`room versions specification`: index.html#room-versions
Server Name
~~~~~~~~~~~
@ -28,7 +34,7 @@ following grammar::
server_name = hostname [ ":" port ]
port = *DIGIT
port = 1*5DIGIT
hostname = IPv4address / "[" IPv6address "]" / dns-name
@ -78,38 +84,6 @@ Some recommendations for a choice of server name follow:
* The length of the complete server name should not exceed 230 characters.
* Server names should not use upper-case characters.
Room Versions
~~~~~~~~~~~~~
Room versions are used to change properties of rooms that may not be compatible
with other servers. For example, changing the rules for event authorization would
cause older servers to potentially end up in a split-brain situation due to them
not understanding the new rules.
A room version is defined as a string of characters which MUST NOT exceed 32
codepoints in length. Room versions MUST NOT be empty and SHOULD contain only
the characters ``a-z``, ``0-9``, ``.``, and ``-``.
Room versions are not intended to be parsed and should be treated as opaque
identifiers. Room versions consisting only of the characters ``0-9`` and ``.``
are reserved for future versions of the Matrix protocol.
The complete grammar for a legal room version is::
room_version = 1*room_version_char
room_version_char = DIGIT
/ %x61-7A ; a-z
/ "-" / "."
Examples of valid room versions are:
* ``1`` (would be reserved by the Matrix protocol)
* ``1.2`` (would be reserved by the Matrix protocol)
* ``1.2-beta``
* ``com.example.version``
Common Identifier Format
~~~~~~~~~~~~~~~~~~~~~~~~
@ -229,7 +203,7 @@ a homeserver creating a user ID for a new user based on the username passed to
Implementations are free to do this mapping however they choose. Since the user
ID is opaque except to the implementation which created it, the only
requirement is that the implemention can perform the mapping
requirement is that the implementation can perform the mapping
consistently. However, we suggest the following algorithm:
1. Encode character strings as UTF-8.
@ -260,18 +234,17 @@ A room has exactly one room ID. A room ID has the format::
!opaque_id:domain
An event has exactly one event ID. An event ID has the format::
An event has exactly one event ID. The format of an event ID depends upon the
`room version specification <index.html#room-versions>`_.
$opaque_id:domain
The ``domain`` of a room/event ID is the `server name`_ of the homeserver which
The ``domain`` of a room ID is the `server name`_ of the homeserver which
created the room/event. The domain is used only for namespacing to avoid the
risk of clashes of identifiers between different homeservers. There is no
implication that the room or event in question is still available at the
corresponding homeserver.
Event IDs and Room IDs are case-sensitive. They are not meant to be human
readable.
readable. They are intended to be treated as fully opaque strings by clients.
.. TODO-spec
What is the grammar for the opaque part? https://matrix.org/jira/browse/SPEC-389
@ -327,7 +300,7 @@ matrix.to navigation
.. NOTE::
This namespacing is in place pending a ``matrix://`` (or similar) URI scheme.
This is **not** meant to be interpreted as an available web service - see
This is **not** meant to be interpreted as an available web service - see
below for more details.
Rooms, users, aliases, and groups may be represented as a "matrix.to" URI.
@ -343,21 +316,34 @@ in RFC 3986:
The identifier may be a room ID, room alias, user ID, or group ID. The extra
parameter is only used in the case of permalinks where an event ID is referenced.
The matrix.to URI, when referenced, must always start with ``https://matrix.to/#/``
followed by the identifier.
followed by the identifier.
Clients should not rely on matrix.to URIs falling back to a web server if accessed
and instead should perform some sort of action within the client. For example, if
the user were to click on a matrix.to URI for a room alias, the client may open
a view for the user to participate in the room.
The components of the matrix.to URI (``<identifier>`` and ``<extra parameter>``)
are to be percent-encoded as per RFC 3986.
Examples of matrix.to URIs are:
* Room alias: ``https://matrix.to/#/#somewhere:example.org``
* Room: ``https://matrix.to/#/!somewhere:example.org``
* Permalink by room: ``https://matrix.to/#/!somewhere:example.org/$event:example.org``
* Permalink by room alias: ``https://matrix.to/#/#somewhere:example.org/$event:example.org``
* User: ``https://matrix.to/#/@alice:example.org``
* Group: ``https://matrix.to/#/+example:example.org``
* Room alias: ``https://matrix.to/#/%23somewhere%3Aexample.org``
* Room: ``https://matrix.to/#/!somewhere%3Aexample.org``
* Permalink by room: ``https://matrix.to/#/!somewhere%3Aexample.org/%24event%3Aexample.org``
* Permalink by room alias: ``https://matrix.to/#/#somewhere:example.org/%24event%3Aexample.org``
* User: ``https://matrix.to/#/%40alice%3Aexample.org``
* Group: ``https://matrix.to/#/%2Bexample%3Aexample.org``
.. Note::
Historically, clients have not produced URIs which are fully encoded. Clients should
try to interpret these cases to the best of their ability. For example, an unencoded
room alias should still work within the client if possible.
.. Note::
Clients should be aware that decoding a matrix.to URI may result in extra slashes
appearing due to some `room versions <index.html#room-versions>`_. These slashes
should normally be encoded when producing matrix.to URIs, however.
.. Note::
Room ID permalinks are unroutable as there is no reliable domain to send requests

@ -73,7 +73,7 @@ MUST be encoded as UTF-8. Clients are authenticated using opaque
``access_token`` strings (see `Client Authentication`_ for details), passed as a
query string parameter on all requests.
The names of the API endponts for the HTTP transport follow a convention of
The names of the API endpoints for the HTTP transport follow a convention of
using underscores to separate words (for example ``/delete_devices``). The key
names in JSON objects passed over the API also follow this convention.
@ -158,7 +158,7 @@ Other error codes the client might encounter are:
Sent when the room alias given to the ``createRoom`` API is already in use.
:``M_INVALID_ROOM_STATE``:
Sent when the intial state given to the ``createRoom`` API is invalid.
Sent when the initial state given to the ``createRoom`` API is invalid.
:``M_THREEPID_IN_USE``:
Sent when a threepid given to an API cannot be used because the same threepid is already in use.
@ -210,10 +210,18 @@ Other error codes the client might encounter are:
The resource being requested is reserved by an application service, or the
application service making the request has not created the resource.
:``M_RESOURCE_LIMIT_EXCEEDED``:
The request cannot be completed because the homeserver has reached a resource
limit imposed on it. For example, a homeserver held in a shared hosting environment
may reach a resource limit if it starts using too much memory or disk space. The
error MUST have an ``admin_contact`` field to provide the user receiving the error
a place to reach out to. Typically, this error will appear on routes which attempt
to modify state (eg: sending messages, account data, etc) and not routes which only
read state (eg: ``/sync``, get account data, etc).
.. TODO: More error codes (covered by other issues)
.. * M_CONSENT_NOT_GIVEN - GDPR: https://github.com/matrix-org/matrix-doc/issues/1512
.. * M_CANNOT_LEAVE_SERVER_NOTICE_ROOM - GDPR: https://github.com/matrix-org/matrix-doc/issues/1254
.. * M_RESOURCE_LIMIT_EXCEEDED - Limits: https://github.com/matrix-org/matrix-doc/issues/1504
.. _sect:txn_ids:
@ -636,7 +644,7 @@ To use this authentication type, clients should submit an auth dict as follows:
where the ``identifier`` property is a user identifier object, as described in
`Identifier types`_.
For example, to authenticate using the user's Matrix ID, clients whould submit:
For example, to authenticate using the user's Matrix ID, clients would submit:
.. code:: json
@ -650,7 +658,7 @@ For example, to authenticate using the user's Matrix ID, clients whould submit:
"session": "<session ID>"
}
Alternatively reply using a 3pid bound to the user's account on the homeserver
Alternatively reply using a 3PID bound to the user's account on the homeserver
using the |/account/3pid|_ API rather then giving the ``user`` explicitly as
follows:
@ -667,7 +675,7 @@ follows:
"session": "<session ID>"
}
In the case that the homeserver does not know about the supplied 3pid, the
In the case that the homeserver does not know about the supplied 3PID, the
homeserver must respond with 403 Forbidden.
Google ReCaptcha
@ -928,10 +936,10 @@ Third-party ID
:Type:
``m.id.thirdparty``
:Description:
The user is identified by a third-party identifer in canonicalised form.
The user is identified by a third-party identifier in canonicalised form.
A client can identify a user using a 3pid associated with the user's account on
the homeserver, where the 3pid was previously associated using the
A client can identify a user using a 3PID associated with the user's account on
the homeserver, where the 3PID was previously associated using the
|/account/3pid|_ API. See the `3PID Types`_ Appendix for a list of Third-party
ID media.
@ -987,7 +995,7 @@ request as follows:
"password": "<password>"
}
Alternatively, a client can use a 3pid bound to the user's account on the
Alternatively, a client can use a 3PID bound to the user's account on the
homeserver using the |/account/3pid|_ API rather then giving the ``user``
explicitly, as follows:
@ -1002,7 +1010,7 @@ explicitly, as follows:
"password": "<password>"
}
In the case that the homeserver does not know about the supplied 3pid, the
In the case that the homeserver does not know about the supplied 3PID, the
homeserver must respond with ``403 Forbidden``.
To log in using a login token, clients should submit a ``/login`` request as
@ -1070,6 +1078,107 @@ Current account information
{{whoami_cs_http_api}}
Capabilities negotiation
------------------------
A homeserver may not support certain operations and clients must be able to
query for what the homeserver can and can't offer. For example, a homeserver
may not support users changing their password as it is configured to perform
authentication against an external system.
The capabilities advertised through this system are intended to advertise
functionality which is optional in the API, or which depend in some way on
the state of the user or server. This system should not be used to advertise
unstable or experimental features - this is better done by the ``/versions``
endpoint.
Some examples of what a reasonable capability could be are:
* Whether the server supports user presence.
* Whether the server supports optional features, such as the user or room
directories.
* The rate limits or file type restrictions imposed on clients by the server.
Some examples of what should **not** be a capability are:
* Whether the server supports a feature in the ``unstable`` specification.
* Media size limits - these are handled by the ``/media/%CLIENT_MAJOR_VERSION%/config``
API.
* Optional encodings or alternative transports for communicating with the
server.
Capabilities prefixed with ``m.`` are reserved for definition in the Matrix
specification while other values may be used by servers using the Java package
naming convention. The capabilities supported by the Matrix specification are
defined later in this section.
{{capabilities_cs_http_api}}
``m.change_password`` capability
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This capability has a single flag, ``enabled``, which indicates whether or not
the user can use the ``/account/password`` API to change their password. If not
present, the client should assume that password changes are possible via the
API. When present, clients SHOULD respect the capability's ``enabled`` flag
and indicate to the user if they are unable to change their password.
An example of the capability API's response for this capability is::
{
"capabilities": {
"m.change_password": {
"enabled": false
}
}
}
``m.room_versions`` capability
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This capability describes the default and available room versions a server
supports, and at what level of stability. Clients should make use of this
capability to determine if users need to be encouraged to upgrade their rooms.
An example of the capability API's response for this capability is::
{
"capabilities": {
"m.room_versions": {
"default": "1",
"available": {
"1": "stable",
"2": "stable",
"3": "unstable",
"custom-version": "unstable"
}
}
}
}
This capability mirrors the same restrictions of `room versions`_ to describe
which versions are stable and unstable. Clients should assume that the ``default``
version is ``stable``. Any version not explicitly labelled as ``stable`` in the
``available`` versions is to be treated as ``unstable``. For example, a version
listed as ``future-stable`` should be treated as ``unstable``.
The ``default`` version is the version the server is using to create new rooms.
Clients should encourage users with sufficient permissions in a room to upgrade
their room to the ``default`` version when the room is using an ``unstable``
version.
When this capability is not listed, clients should use ``"1"`` as the default
and only stable ``available`` room version.
.. _`room versions`: ../index.html#room-versions
Pagination
----------
@ -1166,7 +1275,21 @@ point in time::
[E0]->[E1]->[E2]->[E3]->[E4]->[E5]
.. WARNING::
The format of events can change depending on room version. Check the
`room version specification`_ for specific details on what to expect for
event formats. Examples contained within the client-server specification
are expected to be compatible with all specified room versions, however
some differences may still apply.
For this version of the specification, clients only need to worry about
the event ID format being different depending on room version. Clients
should not be parsing the event ID, and instead be treating it as an
opaque string. No changes should be required to support the currently
available room versions.
.. _`room version specification`: ../index.html#room-versions
Types of room events
~~~~~~~~~~~~~~~~~~~~

@ -22,10 +22,10 @@ Identity Service API
The Matrix client-server and server-server APIs are largely expressed in Matrix
user identifiers. From time to time, it is useful to refer to users by other
("third-party") identifiers, or "3pid"s, e.g. their email address or phone
("third-party") identifiers, or "3PID"s, e.g. their email address or phone
number. This Identity Service Specification describes how mappings between
third-party identifiers and Matrix user identifiers can be established,
validated, and used. This description technically may apply to any 3pid, but in
validated, and used. This description technically may apply to any 3PID, but in
practice has only been applied specifically to email addresses and phone numbers.
.. contents:: Table of Contents
@ -150,9 +150,9 @@ Identity is a privacy-sensitive issue. While the identity server exists to
provide identity information, access should be restricted to avoid leaking
potentially sensitive data. In particular, being able to construct large-scale
connections between identities should be avoided. To this end, in general APIs
should allow a 3pid to be mapped to a Matrix user identity, but not in the other
direction (i.e. one should not be able to get all 3pids associated with a Matrix
user ID, or get all 3pids associated with a 3pid).
should allow a 3PID to be mapped to a Matrix user identity, but not in the other
direction (i.e. one should not be able to get all 3PIDs associated with a Matrix
user ID, or get all 3PIDs associated with a 3PID).
Web browser clients
-------------------
@ -204,10 +204,10 @@ Establishing associations
The flow for creating an association is session-based.
Within a session, one may prove that one has ownership of a 3pid.
Within a session, one may prove that one has ownership of a 3PID.
Once this has been established, the user can form an association between that
3pid and a Matrix user ID. Note that this association is only proved one way;
a user can associate *any* Matrix user ID with a validated 3pid,
3PID and a Matrix user ID. Note that this association is only proved one way;
a user can associate *any* Matrix user ID with a validated 3PID,
i.e. I can claim that any email address I own is associated with
@billg:microsoft.com.
@ -255,11 +255,11 @@ General
Invitation storage
------------------
An identity server can store pending invitations to a user's 3pid, which will
be retrieved and can be either notified on or look up when the 3pid is
An identity server can store pending invitations to a user's 3PID, which will
be retrieved and can be either notified on or look up when the 3PID is
associated with a Matrix user ID.
At a later point, if the owner of that particular 3pid binds it with a Matrix user
At a later point, if the owner of that particular 3PID binds it with a Matrix user
ID, the identity server will attempt to make an HTTP POST to the Matrix user's
homeserver via the `/3pid/onbind`_ endpoint. The request MUST be signed with a
long-term private key for the identity server.
@ -279,4 +279,4 @@ this isn't possible.
.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
.. _`3PID Types`: ../appendices.html#pid-types
.. _`Signing JSON`: ../appendices.html#signing-json
.. _`/3pid/onbind`: ../server_server/unstable.html#put-matrix-federation-v1-3pid-onbind
.. _`/3pid/onbind`: ../server_server/r0.1.1.html#put-matrix-federation-v1-3pid-onbind

@ -40,10 +40,15 @@ The specification consists of the following parts:
{{apis}}
Additionally, this introduction page contains the key baseline information required to
understand the specific APIs, including the sections on `room versions`_
and `overall architecture <#architecture>`_.
The `Appendices <appendices.html>`_ contain supplemental information not specific to
one of the above APIs.
The `Matrix Client-Server API Swagger Viewer <https://matrix.org/docs/api/client-server/>`_ is useful for browsing the Client-Server API.
The `Matrix Client-Server API Swagger Viewer <https://matrix.org/docs/api/client-server/>`_
is useful for browsing the Client-Server API.
Introduction to the Matrix APIs
-------------------------------
@ -128,6 +133,8 @@ To propose a change to the Matrix Spec, see the explanations at `Proposals
for Spec Changes to Matrix <proposals>`_.
.. _`architecture`:
Architecture
------------
@ -189,7 +196,7 @@ allocated the account and has the form::
@localpart:domain
See `'Identifier Grammar' the appendices <appendices.html#identifier-grammar>`_ for full details of
See `'Identifier Grammar' in the appendices <appendices.html#identifier-grammar>`_ for full details of
the structure of user IDs.
Devices
@ -315,8 +322,8 @@ The following conceptual diagram shows an
| Content: { JSON object } |
|....................................|
Federation maintains *shared data structures* per-room between multiple home
servers. The data is split into ``message events`` and ``state events``.
Federation maintains *shared data structures* per-room between multiple
homeservers. The data is split into ``message events`` and ``state events``.
Message events:
These describe transient 'once-off' activity in a room such as an
@ -418,6 +425,75 @@ dedicated API. The API is symmetrical to managing Profile data.
Would it really be overengineered to use the same API for both profile &
private user data, but with different ACLs?
.. _`room versions`:
Room Versions
-------------
Rooms are central to how Matrix operates, and have strict rules for what
is allowed to be contained within them. Rooms can also have various
algorithms that handle different tasks, such as what to do when two or
more events collide in the underlying DAG. To allow rooms to be improved
upon through new algorithms or rules, "room versions" are employed to
manage a set of expectations for each room. New room versions are assigned
as needed.
There is no implicit ordering or hierarchy to room versions, and their principles
are immutable once placed in the specification. Although there is a recommended
set of versions, some rooms may benefit from features introduced by other versions.
Rooms move between different versions by "upgrading" to the desired version. Due
to versions not being ordered or hierarchical, this means a room can "upgrade"
from version 2 to version 1, if it is so desired.
Room version grammar
~~~~~~~~~~~~~~~~~~~~
Room versions are used to change properties of rooms that may not be compatible
with other servers. For example, changing the rules for event authorization would
cause older servers to potentially end up in a split-brain situation due to not
understanding the new rules.
A room version is defined as a string of characters which MUST NOT exceed 32
codepoints in length. Room versions MUST NOT be empty and SHOULD contain only
the characters ``a-z``, ``0-9``, ``.``, and ``-``.
Room versions are not intended to be parsed and should be treated as opaque
identifiers. Room versions consisting only of the characters ``0-9`` and ``.``
are reserved for future versions of the Matrix protocol.
The complete grammar for a legal room version is::
room_version = 1*room_version_char
room_version_char = DIGIT
/ %x61-7A ; a-z
/ "-" / "."
Examples of valid room versions are:
* ``1`` (would be reserved by the Matrix protocol)
* ``1.2`` (would be reserved by the Matrix protocol)
* ``1.2-beta``
* ``com.example.version``
Complete list of room versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Room versions are divided into two distinct groups: stable and unstable. Stable
room versions may be used by rooms safely. Unstable room versions are everything
else which is either not listed in the specification or flagged as unstable for
some other reason. Versions can switch between stable and unstable periodically
for a variety of reasons, including discovered security vulnerabilities and age.
Clients should not ask room administrators to upgrade their rooms if the room is
running a stable version. Servers SHOULD use room version 1 as the default room
version when creating new rooms.
The available room versions are:
* `Version 1 <rooms/v1.html>`_ - **Stable**. The current version of most rooms.
* `Version 2 <rooms/v2.html>`_ - **Stable**. Implements State Resolution Version 2.
* `Version 3 <rooms/v3.html>`_ - **Stable**. Introduces events whose IDs are the event's hash.
Specification Versions
----------------------

@ -27,7 +27,7 @@ The account_data may be either global or scoped to a particular rooms.
Events
------
The client recieves the account data as events in the ``account_data`` sections
The client receives the account data as events in the ``account_data`` sections
of a ``/sync``.
These events can also be received in a ``/events`` response or in the

@ -291,8 +291,8 @@ v string **Required.** Version of the encrypted attachments
========= ========= ============================================================
Parameter Type Description
========= ========= ============================================================
key string **Required.** Key type. Must be ``oct``.
key_opts [string] **Required.** Key operations. Must at least contain
kty string **Required.** Key type. Must be ``oct``.
key_ops [string] **Required.** Key operations. Must at least contain
``encrypt`` and ``decrypt``.
alg string **Required.** Algorithm. Must be ``A256CTR``.
k string **Required.** The key, encoded as urlsafe unpadded base64.
@ -450,7 +450,7 @@ previously-received ``request`` message with the same ``request_id`` and
.. NOTE::
Key sharing can be a big attack vector, thus it must be done very carefully.
A reasonable stategy is for a user's client to only send keys requested by the
A reasonable strategy is for a user's client to only send keys requested by the
verified devices of the same user.
Key exports
@ -496,18 +496,9 @@ passphrase, and is created as follows:
Key export format
<<<<<<<<<<<<<<<<<
The exported sessions are formatted as a JSON object of type ``ExportData``
The exported sessions are formatted as a JSON array of ``SessionData`` objects
described as follows:
``ExportData``
=============== ================= ==============================================
Parameter Type Description
=============== ================= ==============================================
sessions ``[SessionData]`` Required. The sessions that are being
exported.
=============== ================= ==============================================
``SessionData``
.. table::
@ -529,7 +520,7 @@ sessions ``[SessionData]`` Required. The sessions that are being
device which initiated the session
originally.
sender_claimed_keys {string: Required. The Ed25519 key of the
integer} device which initiated the session
string} device which initiated the session
originally.
session_id string Required. The ID of the session.
session_key string Required. The key for the session.
@ -696,7 +687,7 @@ An event encrypted using Megolm has the following format:
"sender_key": "<sender_curve25519_key>",
"device_id": "<sender_device_id>",
"session_id": "<outbound_group_session_id>",
"ciphertext": "<encypted_payload_base_64>"
"ciphertext": "<encrypted_payload_base_64>"
}
}

@ -185,25 +185,14 @@ reduced through clients making use of the transaction ID they used to send
a particular event. The transaction ID used will be included in the event's
``unsigned`` data as ``transaction_id`` when it arrives through the event stream.
Clients unable to make use of the transaction ID are more likely to experience
flickering due to the following two scenarios, however the effect can be mitigated
to a degree:
- The client sends a message and the remote echo arrives on the event stream
*after* the request to send the message completes.
- The client sends a message and the remote echo arrives on the event stream
*before* the request to send the message completes.
In the first scenario, the client will receive an event ID when the request to
send the message completes. This ID can be used to identify the duplicate event
when it arrives on the event stream. However, in the second scenario, the event
arrives before the client has obtained an event ID. This makes it impossible to
identify it as a duplicate event. This results in the client displaying the
message twice for a fraction of a second before the the original request to send
the message completes. Once it completes, the client can take remedial actions
to remove the duplicate event by looking for duplicate event IDs. A future version
of the client-server API will resolve this by attaching the transaction ID of the
sending request to the event itself.
Clients unable to make use of the transaction ID are likely to experience
flickering when the remote echo arrives on the event stream *before*
the request to send the message completes. In that case the event
arrives before the client has obtained an event ID, making it impossible to
identify it as a remote echo. This results in the client displaying the message
twice for some time (depending on the server responsiveness) before the original
request to send the message completes. Once it completes, the client can take
remedial actions to remove the duplicate event by looking for duplicate event IDs.
Calculating the display name for a user

@ -44,7 +44,7 @@ In addition to using the appropriate ``matrix.to URI`` for the mention,
clients should use the following guidelines when making mentions in events
to be sent:
* When mentioning users, use the user's potentially ambigious display name for
* When mentioning users, use the user's potentially ambiguous display name for
the anchor's text. If the user does not have a display name, use the user's
ID.

@ -26,14 +26,8 @@ Each user has the concept of presence information. This encodes:
This information is collated from both per-device (``online``, ``idle``,
``last_active``) and per-user (status) data, aggregated by the user's homeserver
and transmitted as an ``m.presence`` event. This is one of the few events which
are sent *outside the context of a room*. Presence events are sent to all users
who subscribe to this user's presence through a presence list or by sharing
membership of a room.
A presence list is a list of user IDs whose presence the user wants to follow.
To be added to this list, the user being added must be invited by the list owner
who must accept the invitation.
and transmitted as an ``m.presence`` event. Presence events are sent to
interested parties where users share a room membership.
User's presence state is represented by the ``presence`` key, which is an enum
of one of the following:
@ -53,17 +47,10 @@ Events
Client behaviour
----------------
Clients can manually set/get their presence/presence list using the HTTP APIs
listed below.
Clients can manually set/get their presence using the HTTP APIs listed below.
{{presence_cs_http_api}}
Server behaviour
----------------
Each user's homeserver stores a "presence list" per user. Once a user accepts
a presence list, both user's HSes must track the subscription.
Last active ago
~~~~~~~~~~~~~~~
The server maintains a timestamp of the last time it saw a pro-active event from

@ -660,6 +660,8 @@ When a user changes their push rules a ``m.push_rules`` event is sent to all
clients in the ``account_data`` section of their next ``/sync`` request. The
content of the event is the current push rules for the user.
{{m_push_rules_event}}
Examples
++++++++

@ -0,0 +1,78 @@
.. Copyright 2019 New Vector Ltd
..
.. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License.
.. You may obtain a copy of the License at
..
.. http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing, software
.. distributed under the License is distributed on an "AS IS" BASIS,
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
.. See the License for the specific language governing permissions and
.. limitations under the License.
Room Upgrades
=============
.. _module:room-upgrades:
From time to time, a room may need to be upgraded to a different room version for a
variety for reasons. This module defines a way for rooms to upgrade to a different
room version when needed.
Events
------
{{m_room_tombstone_event}}
Client behaviour
----------------
Clients which understand ``m.room.tombstone`` events and the ``predecessor`` field on
``m.room.create`` events should communicate to the user that the room was upgraded.
One way of accomplishing this would be hiding the old room from the user's room list
and showing banners linking between the old and new room - ensuring that permalinks
work when referencing the old room. Another approach may be to virtually merge the
rooms such that the old room's timeline seamlessly continues into the new timeline
without the user having to jump between the rooms.
{{room_upgrades_cs_http_api}}
Server behaviour
----------------
When the client requests to upgrade a known room to a known version, the server:
1. Checks that the user has permission to send ``m.room.tombstone`` events in the room.
2. Creates a replacement room with a ``m.room.create`` event containing a ``predecessor``
field and the applicable ``room_version``.
3. Replicates transferable state events to the new room. The exact details for what is
transferred is left as an implementation detail, however the recommended state events
to transfer are:
* ``m.room.server_acl``
* ``m.room.encryption``
* ``m.room.name``
* ``m.room.avatar``
* ``m.room.topic``
* ``m.room.guest_access``
* ``m.room.history_visibility``
* ``m.room.join_rules``
* ``m.room.power_levels``
Membership events should not be transferred to the new room due to technical limitations
of servers not being able to impersonate people from other homeservers. Additionally,
servers should not transfer state events which are sensitive to who sent them, such as
events outside of the Matrix namespace where clients may rely on the sender to match
certain criteria.
4. Moves any local aliases to the new room.
5. Sends a ``m.room.tombstone`` event to the old room to indicate that it is not intended
to be used any further.
6. If possible, the power levels in the old room should also be modified to prevent sending
of events and inviting new users. For example, setting ``events_default`` and ``invite``
to the greater of ``50`` and ``users_default + 1``.
When a user joins the new room, the server should automatically transfer/replicate some of
the user's personalized settings such as notifications, tags, etc.

@ -48,7 +48,7 @@ The tag namespace is defined as follows:
* The namespace ``u.*`` is reserved for user-defined tags. The portion of the string after the ``u.``
is defined to be the display name of this tag. No other semantics should be inferred from tags in
this namespace.
* A client or app willing to use special tags for advanced functionnality should namespace them similarly to state keys: ``tld.name.*``
* A client or app willing to use special tags for advanced functionality should namespace them similarly to state keys: ``tld.name.*``
* Any tag in the ``tld.name.*`` form but not matching the namespace of the current client should be ignored
* Any tag not matching the above rules should be interpreted as a user tag from the ``u.*`` namespace, as if
the name had already had ``u.`` stripped from the start (ie. the name of the tag is used as the

@ -229,7 +229,7 @@ verification must still be performed, so the attack surface here is minimized.
Security considerations
-----------------------
There are a number of privary and trust implications to this module.
There are a number of privacy and trust implications to this module.
It is important for user privacy that leaking the mapping between a matrix user
ID and a third party identifier is hard. In particular, being able to look up

@ -324,7 +324,7 @@ Name GitHub Label Description
=============================== ============================= ====================================
Proposal Drafting and Feedback N/A A proposal document which is still work-in-progress but is being shared to incorporate feedback. Please prefix your proposal's title with ``[WIP]`` to make it easier for reviewers to skim their notifications list.
Proposal In Review proposal-in-review A proposal document which is now ready and waiting for review by the Spec Core Team and community
Proposed Final Comment Period proposed-final-comment-period Currently awaiting signoff of a majority of team members in order to enter the final comment period
Proposed Final Comment Period proposed-final-comment-period Currently awaiting signoff of a 75% majority of team members in order to enter the final comment period
Final Comment Period final-comment-period A proposal document which has reached final comment period either for merge, closure or postponement
Final Commment Period Complete finished-final-comment-period The final comment period has been completed. Waiting for a demonstration implementation
Spec PR Missing spec-pr-missing The proposal has been agreed, and proven with a demonstration implementation. Waiting for a PR against the Spec

@ -84,7 +84,7 @@ This describes the format used by "HTTP" pushers to send notifications of
events to Push Gateways. If the endpoint returns an HTTP error code, the
homeserver SHOULD retry for a reasonable amount of time using exponential backoff.
When pushing notifications for events, the hoemserver is expected to include all of
When pushing notifications for events, the homeserver is expected to include all of
the event-related fields in the ``/notify`` request. When the homeserver is performing
a push where the ``format`` is ``"event_id_only"``, only the ``event_id``, ``room_id``,
``counts``, and ``devices`` are required to be populated.

@ -0,0 +1,297 @@
.. Copyright 2017,2019 New Vector Ltd
..
.. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License.
.. You may obtain a copy of the License at
..
.. http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing, software
.. distributed under the License is distributed on an "AS IS" BASIS,
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
.. See the License for the specific language governing permissions and
.. limitations under the License.
Room Version 1
==============
This room version is the first ever version for rooms, and contains the building
blocks for other room versions.
.. contents:: Table of Contents
.. sectnum::
Server implementation components
--------------------------------
.. WARNING::
The information contained in this section is strictly for server implementors.
Applications which use the Client-Server API are generally unaffected by the
details contained here, and can safely ignore their presence.
The algorithms defined here should only apply to version 1 rooms. Other algorithms
may be used by other room versions, and as such servers should be aware of which
version room they are dealing with prior to executing a given algorithm.
.. WARNING::
Although room version 1 is the most popular room version, it is known to have
undesirable effects. Servers implementing support for room version 1 should be
aware that restrictions should be generally relaxed and that inconsistencies
may occur until room version 2 (or later) is ready and adopted.
State resolution
~~~~~~~~~~~~~~~~
.. WARNING::
This section documents the state resolution algorithm as implemented by
Synapse as of December 2017 (and therefore the de-facto Matrix protocol).
However, this algorithm is known to have some problems.
The room state :math:`S'(E)` after an event :math:`E` is defined in terms of
the room state :math:`S(E)` before :math:`E`, and depends on whether
:math:`E` is a state event or a message event:
* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except
that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key``
is replaced by :math:`E`'s ``event_id``.
The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of
states :math:`\{ S'(E'), S'(E''), … \}` consisting of the states after each of
:math:`E`'s ``prev_event``\s :math:`\{ E', E'', … \}`.
The *resolution* of a set of states is defined as follows. The resolved state
is built up in a number of passes; here we use :math:`R` to refer to the
results of the resolution so far.
* Start by setting :math:`R` to the union of the states to be resolved,
excluding any *conflicting* events.
* First we resolve conflicts between ``m.room.power_levels`` events. If there
is no conflict, this step is skipped, otherwise:
* Assemble all the ``m.room.power_levels`` events from the states to
be resolved into a list.
* Sort the list by ascending ``depth`` then descending ``sha1(event_id)``.
* Add the first event in the list to :math:`R`.
* For each subsequent event in the list, check that the event would be
allowed by the authorization rules for a room in state :math:`R`. If the
event would be allowed, then update :math:`R` with the event and continue
with the next event in the list. If it would not be allowed, stop and
continue below with ``m.room.join_rules`` events.
* Repeat the above process for conflicts between ``m.room.join_rules`` events.
* Repeat the above process for conflicts between ``m.room.member`` events.
* No other events affect the authorization rules, so for all other conflicts,
just pick the event with the highest depth and lowest ``sha1(event_id)`` that
passes authentication in :math:`R` and add it to :math:`R`.
A *conflict* occurs between states where those states have different
``event_ids`` for the same ``(state_type, state_key)``. The events thus
affected are said to be *conflicting* events.
Authorization rules
~~~~~~~~~~~~~~~~~~~
The types of state events that affect authorization are:
- ``m.room.create``
- ``m.room.member``
- ``m.room.join_rules``
- ``m.room.power_levels``
- ``m.room.third_party_invite``
The rules are as follows:
1. If type is ``m.room.create``:
a. If it has any previous events, reject.
b. If the domain of the ``room_id`` does not match the domain of the
``sender``, reject.
c. If ``content.room_version`` is present and is not a recognised version,
reject.
d. If ``content`` has no ``creator`` field, reject.
e. Otherwise, allow.
#. Reject if event has ``auth_events`` that:
a. have duplicate entries for a given ``type`` and ``state_key`` pair
#. have entries whose ``type`` and ``state_key`` don't match those
specified by the `auth events selection`_ algorithm described in the
server specification.
#. If event does not have a ``m.room.create`` in its ``auth_events``, reject.
#. If type is ``m.room.aliases``:
a. If event has no ``state_key``, reject.
b. If sender's domain doesn't matches ``state_key``, reject.
c. Otherwise, allow.
#. If type is ``m.room.member``:
a. If no ``state_key`` key or ``membership`` key in ``content``, reject.
#. If ``membership`` is ``join``:
i. If the only previous event is an ``m.room.create``
and the ``state_key`` is the creator, allow.
#. If the ``sender`` does not match ``state_key``, reject.
#. If the ``sender`` is banned, reject.
#. If the ``join_rule`` is ``invite`` then allow if membership state
is ``invite`` or ``join``.
#. If the ``join_rule`` is ``public``, allow.
#. Otherwise, reject.
#. If ``membership`` is ``invite``:
i. If ``content`` has ``third_party_invite`` key:
#. If *target user* is banned, reject.
#. If ``content.third_party_invite`` does not have a
``signed`` key, reject.
#. If ``signed`` does not have ``mxid`` and ``token`` keys, reject.
#. If ``mxid`` does not match ``state_key``, reject.
#. If there is no ``m.room.third_party_invite`` event in the
current room state with ``state_key`` matching ``token``, reject.
#. If ``sender`` does not match ``sender`` of the
``m.room.third_party_invite``, reject.
#. If any signature in ``signed`` matches any public key in the
``m.room.third_party_invite`` event, allow. The public keys are
in ``content`` of ``m.room.third_party_invite`` as:
#. A single public key in the ``public_key`` field.
#. A list of public keys in the ``public_keys`` field.
#. Otherwise, reject.
#. If the ``sender``'s current membership state is not ``join``, reject.
#. If *target user*'s current membership state is ``join`` or ``ban``,
reject.
#. If the ``sender``'s power level is greater than or equal to the *invite
level*, allow.
#. Otherwise, reject.
#. If ``membership`` is ``leave``:
i. If the ``sender`` matches ``state_key``, allow if and only if that user's
current membership state is ``invite`` or ``join``.
#. If the ``sender``'s current membership state is not ``join``, reject.
#. If the *target user*'s current membership state is ``ban``, and the
``sender``'s power level is less than the *ban level*, reject.
#. If the ``sender``'s power level is greater than or equal to the *kick
level*, and the *target user*'s power level is less than the
``sender``'s power level, allow.
#. Otherwise, reject.
#. If ``membership`` is ``ban``:
i. If the ``sender``'s current membership state is not ``join``, reject.
#. If the ``sender``'s power level is greater than or equal to the *ban
level*, and the *target user*'s power level is less than the
``sender``'s power level, allow.
#. Otherwise, reject.
#. Otherwise, the membership is unknown. Reject.
#. If the ``sender``'s current membership state is not ``join``, reject.
#. If type is ``m.room.third_party_invite``:
a. Allow if and only if ``sender``'s current power level is greater than
or equal to the *invite level*.
#. If the event type's *required power level* is greater than the ``sender``'s power
level, reject.
#. If the event has a ``state_key`` that starts with an ``@`` and does not match
the ``sender``, reject.
#. If type is ``m.room.power_levels``:
a. If ``users`` key in ``content`` is not a dictionary with keys that are
valid user IDs with values that are integers (or a string that is an
integer), reject.
#. If there is no previous ``m.room.power_levels`` event in the room, allow.
#. For each of the keys ``users_default``, ``events_default``,
``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as
each entry being changed under the ``events`` or ``users`` keys:
i. If the current value is higher than the ``sender``'s current power level,
reject.
#. If the new value is higher than the ``sender``'s current power level,
reject.
#. For each entry being changed under the ``users`` key, other than the
``sender``'s own entry:
i. If the current value is equal to the ``sender``'s current power level,
reject.
#. Otherwise, allow.
#. If type is ``m.room.redaction``:
a. If the ``sender``'s power level is greater than or equal to the *redact
level*, allow.
#. If the domain of the ``event_id`` of the event being redacted is the same
as the domain of the ``event_id`` of the ``m.room.redaction``, allow.
#. Otherwise, reject.
#. Otherwise, allow.
.. NOTE::
Some consequences of these rules:
* Unless you are a member of the room, the only permitted operations (apart
from the initial create/join) are: joining a public room; accepting or
rejecting an invitation to a room.
* To unban somebody, you must have power level greater than or equal to both
the kick *and* ban levels, *and* greater than the target user's power
level.
Event format
~~~~~~~~~~~~
Events in version 1 rooms have the following structure:
{{definition_ss_pdu}}
.. _`auth events selection`: ../../server_server/r0.1.1.html#auth-events-selection
.. _`Signing Events`: ../../server_server/r0.1.1.html#signing-events

@ -0,0 +1,204 @@
.. Copyright 2018-2019 New Vector Ltd
..
.. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License.
.. You may obtain a copy of the License at
..
.. http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing, software
.. distributed under the License is distributed on an "AS IS" BASIS,
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
.. See the License for the specific language governing permissions and
.. limitations under the License.
Room Version 2
==============
This room version builds off of `version 1 <v1.html>`_ with an improved state
resolution algorithm.
.. contents:: Table of Contents
.. sectnum::
Server implementation components
--------------------------------
.. WARNING::
The information contained in this section is strictly for server implementors.
Applications which use the Client-Server API are generally unaffected by the
details contained here, and can safely ignore their presence.
Room version 2 uses the base components of `room version 1 <v1.html>`_, changing
only the state resolution algorithm.
State resolution
~~~~~~~~~~~~~~~~
The room state :math:`S'(E)` after an event :math:`E` is defined in terms of
the room state :math:`S(E)` before :math:`E`, and depends on whether
:math:`E` is a state event or a message event:
* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except
that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key``
is replaced by :math:`E`'s ``event_id``.
The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of
states :math:`\{ S'(E_1), S'(E_2), … \}` consisting of the states after each of
:math:`E`'s ``prev_event``\s :math:`\{ E_1, E_2, … \}`, where the resolution of
a set of states is given in the algorithm below.
Definitions
+++++++++++
The state resolution algorithm for version 2 rooms uses the following
definitions, given the set of room states :math:`\{ S_1, S_2, \ldots \}`:
Power events
A *power event* is a state event with type ``m.room.power_levels`` or
``m.room.join_rules``, or a state event with type ``m.room.member`` where the
``membership`` is ``leave`` or ``ban`` and the ``sender`` does not match the
``state_key``. The idea behind this is that power events are events that might
remove someone's ability to do something in the room.
Unconflicted state map and conflicted state set
The *unconflicted state map* is the state where the value of each key exists
and is the same in each state :math:`S_i`. The *conflicted state set* is the
set of all other state events. Note that the unconflicted state map only has
one event per ``(event_type, state_key)``, whereas the conflicted state set
may have multiple events.
Auth difference
The *auth difference* is calculated by first calculating the full auth chain
for each state :math:`S_i`, that is the union of the auth chains for each
event in :math:`S_i`, and then taking every event that doesn't appear in
every auth chain. If :math:`C_i` is the full auth chain of :math:`S_i`, then
the auth difference is :math:`\cup C_i - \cap C_i`.
Full conflicted set
The *full conflicted set* is the union of the conflicted state set and the
auth difference.
Reverse topological power ordering
The *reverse topological power ordering* of a set of events is the
lexicographically smallest topological ordering based on the DAG formed by
auth events. The reverse topological power ordering is ordered from earliest
event to latest. For comparing two topological orderings to determine which
is the lexicographically smallest, the following comparison relation on
events is used: for events :math:`x` and :math:`y`, :math:`x<y` if
1. :math:`x`'s sender has *greater* power level than :math:`y`'s sender,
when looking at their respective ``auth_event``\s; or
2. the senders have the same power level, but :math:`x`'s
``origin_server_ts`` is *less* than :math:`y`'s ``origin_server_ts``; or
3. the senders have the same power level and the events have the same
``origin_server_ts``, but :math:`x`'s ``event_id`` is *less* than
:math:`y`'s ``event_id``.
The reverse topological power ordering can be found by sorting the events
using Kahn's algorithm for topological sorting, and at each step selecting,
among all the candidate vertices, the smallest vertex using the above
comparison relation.
Mainline ordering
Given an ``m.room.power_levels`` event :math:`P`, the *mainline of* :math:`P`
is the list of events generated by starting with :math:`P` and recursively
taking the ``m.room.power_levels`` events from the ``auth_events``, ordered
such that :math:`P` is last. Given another event :math:`e`, the *closest
mainline event to* :math:`e` is the first event encountered in the mainline
when iteratively descending through the ``m.room.power_levels`` events in the
``auth_events`` starting at :math:`e`. If no mainline event is encountered
when iteratively descending through the ``m.room.power_levels`` events, then
the closest mainline event to :math:`e` can be considered to be a dummy event
that is before any other event in the mainline of :math:`P` for the purposes
of condition 1 below.
The *mainline ordering based on* :math:`P` of a set of events is the
ordering, from smallest to largest, using the following comparison relation
on events: for events :math:`x` and :math:`y`, :math:`x<y` if
1. the closest mainline event to :math:`x` appears *before* the closest
mainline event to :math:`y`; or
2. the closest mainline events are the same, but :math:`x`\'s
``origin_server_ts`` is *less* than :math:`y`\'s ``origin_server_ts``; or
3. the closest mainline events are the same and the events have the same
``origin_server_ts``, but :math:`x`\'s ``event_id`` is *less* than
:math:`y`\'s ``event_id``.
Iterative auth checks
The *iterative auth checks algorithm* takes as input an initial room state
and a sorted list of state events, and constructs a new room state by
iterating through the event list and applying the state event to the room
state if the state event is allowed by the `authorization rules`_. If the
state event is not allowed by the authorization rules, then the event is
ignored. If a ``(event_type, state_key)`` key that is required for checking
the authorization rules is not present in the state, then the appropriate
state event from the event's ``auth_events`` is used if the auth event is
not rejected.
Algorithm
+++++++++
The *resolution* of a set of states is obtained as follows:
1. Take all *power events* and any events in their auth chains, recursively,
that appear in the *full conflicted set* and order them by the *reverse
topological power ordering*.
2. Apply the *iterative auth checks algorithm* on the *unconflicted state map*
and the list of events from the previous step to get a partially resolved
state.
3. Take all remaining events that weren't picked in step 1 and order them by
the mainline ordering based on the power level in the partially resolved
state obtained in step 2.
4. Apply the *iterative auth checks algorithm* on the partial resolved
state and the list of events from the previous step.
5. Update the result by replacing any event with the event with the same key
from the *unconflicted state map*, if such an event exists, to get the final
resolved state.
.. _`authorization rules`: ../server_server/r0.1.1.html#authorization-rules
Rejected events
+++++++++++++++
Events that have been rejected due to failing auth based on the state at the
event (rather than based on their auth chain) are handled as usual by the
algorithm, unless otherwise specified.
Note that no events rejected due to failure to auth against their auth chain
should appear in the process, as they should not appear in state (the algorithm
only uses events that appear in either the state sets or in the auth chain of
the events in the state sets).
.. admonition:: Rationale
This helps ensure that different servers' view of state is more likely to
converge, since rejection state of an event may be different. This can happen if
a third server gives an incorrect version of the state when a server joins a
room via it (either due to being faulty or malicious). Convergence of state is a
desirable property as it ensures that all users in the room have a (mostly)
consistent view of the state of the room. If the view of the state on different
servers diverges it can lead to bifurcation of the room due to e.g. servers
disagreeing on who is in the room.
Intuitively, using rejected events feels dangerous, however:
1. Servers cannot arbitrarily make up state, since they still need to pass the
auth checks based on the event's auth chain (e.g. they can't grant themselves
power levels if they didn't have them before).
2. For a previously rejected event to pass auth there must be a set of state
that allows said event. A malicious server could therefore produce a
fork where it claims the state is that particular set of state, duplicate the
rejected event to point to that fork, and send the event. The
duplicated event would then pass the auth checks. Ignoring rejected events
would therefore not eliminate any potential attack vectors.
Rejected auth events are deliberately excluded from use in the iterative auth
checks, as auth events aren't re-authed (although non-auth events are) during
the iterative auth checks.

@ -0,0 +1,124 @@
.. Copyright 2018-2019 New Vector Ltd
..
.. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License.
.. You may obtain a copy of the License at
..
.. http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing, software
.. distributed under the License is distributed on an "AS IS" BASIS,
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
.. See the License for the specific language governing permissions and
.. limitations under the License.
Room Version 3
==============
This room version builds on `version 2 <v2.html>`_ with an improved event format.
.. note:
All requirements listed in this room version specification are scoped to rooms
which actually use this room version. For example, a requirement of "all APIs must
accept the new event format" does in fact apply to all APIs, but only so much as
where the contextual room of the request is using this room version. Rooms using
other room versions should not be affected by these sweeping requirements.
.. contents:: Table of Contents
.. sectnum::
Client considerations
---------------------
This room version changes the format for event IDs sent to clients. Clients should be
aware that these event IDs may contain slashes and other potentially problematic
characters. Clients should be treating event IDs as opaque identifiers and should not
be attempting to parse them into a usable form, just like with other room versions.
Clients should expect to see event IDs changed from the format of ``$randomstring:example.org``
to something like ``$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk`` (note the lack of
domain and the potentially problematic slash).
Server implementation components
--------------------------------
.. WARNING::
The information contained in this section is strictly for server implementors.
Applications which use the Client-Server API are generally unaffected by the
intricacies contained here. The section above regarding client considerations
is the resource that Client-Server API use cases should reference.
Room version 3 uses the state resolution algorithm defined in `room version 2 <v2.html>`_,
and the event format defined here.
Event IDs
~~~~~~~~~
.. admonition:: Rationale
In other room versions (namely version 1 and 2) the event ID is a distinct field
from the remainder of the event, which must be tracked as such. This leads to
complications where servers receive multiple events with the same ID in either the
same or different rooms where the server cannot easily keep track of which event it
should be using. By removing the use of a dedicated event ID, servers are required
to track the hashes on an event to determine its ID.
The event ID is the `reference hash`_ of the event encoded using `Unpadded Base64`_,
prefixed with ``$``. A resulting event ID using this approach should look similar to
``$CD66HAED5npg6074c6pDtLKalHjVfYb2q4Q3LZgrW6o``.
Event IDs should not be sent over federation to servers when the room uses
this room version. On the receiving end of an event, the server should compute
the relevant event ID for itself.
Additionally, the ``auth_events`` and ``prev_events`` have had a format change
compared to other room versions to make it easier to handle. Instead of a tuple
of values, they are now plain lists of events.
{{definition_ss_pdu_v3}}
Changes to APIs
~~~~~~~~~~~~~~~
Due to the event ID being removed from the event, some APIs need to change. All
APIs which currently accept an event ID must do so with the new format. Servers
must append the calculated event ID to all events sent to clients where an event
ID would normally be expected.
Because the format of events has changed, servers must be aware of the room version
where the event resides so that the server may parse and handle the event. The
federation API has taken this concern into consideration by ensuring that servers
are aware of (or can find) the room version during a request.
Authorization rules for events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The authorization rules for a given event have changed in this room version due
to the change in event format:
* The event no longer needs to be signed by the domain of the event ID (as there
is no domain in the event ID), but still needs to be signed by the sender's
domain.
* In past room versions, redactions were only permitted to enter the DAG if the
sender's domain matched the domain in the event ID being redacted, or the sender
had appropriate permissions per the power levels. Due to servers now not being
able to determine where an event came from during event authorization, redaction
events are always accepted (provided the event is allowed by ``events`` and
``events_default`` in the power levels). However, servers should not apply or send
redactions to clients until both the redaction event and original event have been
seen, and are valid. Servers should only apply redactions to events where the
sender's domains match, or the sender of the redaction has the appropriate
permissions per the power levels.
The remaining rules are the same as `room version 1 <v1.html#authorization-rules>`_.
.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
.. _`Canonical JSON`: ../appendices.html#canonical-json
.. _`Signing Events`: ../server_server/r0.1.1.html#signing-events
.. _`reference hash`: ../server_server/r0.1.1.html#reference-hashes

@ -1,6 +1,5 @@
.. Copyright 2016 OpenMarket Ltd
.. Copyright 2017 New Vector Ltd
.. Copyright 2018 New Vector Ltd
.. Copyright 2017-2019 New Vector Ltd
..
.. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License.
@ -17,9 +16,7 @@
Federation API
==============
.. WARNING::
This API is unstable and will change without warning or discussion while
we work towards a r0 release (scheduled for August 2018).
{{unstable_warning_block_SERVER_RELEASE_LABEL}}
Matrix homeservers use the Federation APIs (also known as server-server APIs)
to communicate with each other. Homeservers use these APIs to push messages to
@ -77,13 +74,14 @@ This version of the specification is generated from
For the full historical changelog, see
https://github.com/matrix-org/matrix-doc/blob/master/changelogs/server_server.rst
Other versions of this specification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following other versions are also available, in reverse chronological order:
- `HEAD <https://matrix.org/docs/spec/server_server/unstable.html>`_: Includes all changes since the latest versioned release.
- `r0.1.1 <https://matrix.org/docs/spec/server_server/r0.1.1.html>`_
- `r0.1.0 <https://matrix.org/docs/spec/server_server/r0.1.0.html>`_
Server discovery
----------------
@ -91,35 +89,107 @@ Server discovery
Resolving server names
~~~~~~~~~~~~~~~~~~~~~~
Each matrix homeserver is identified by a server name consisting of a hostname
Each Matrix homeserver is identified by a server name consisting of a hostname
and an optional port, as described by the `grammar
<../appendices.html#server-name>`_. Server names should be resolved to an IP
address and port using the following process:
* If the hostname is an IP literal, then that IP address should be used,
together with the given port number, or 8448 if no port is given.
* Otherwise, if the port is present, then an IP address is discovered by
looking up an AAAA or A record for the hostname, and the specified port is
used.
* If the hostname is not an IP literal and no port is given, the server is
discovered by first looking up a ``_matrix._tcp`` SRV record for the
hostname, which may give a hostname (to be looked up using AAAA or A queries)
and port. If the SRV record does not exist, then the server is discovered by
looking up an AAAA or A record on the hostname and taking the default
fallback port number of 8448.
Homeservers may use SRV records to load balance requests between multiple TLS
endpoints or to failover to another endpoint if an endpoint fails.
When making requests to servers, use the hostname of the target server in the
``Host`` header, regardless of any hostname given in the SRV record. For
example, if the server name is ``example.org``, and the SRV record resolves to
``matrix.example.org``, the ``Host`` header in the request should be
``example.org``. If an explicit port was given in the server name, it should be
included in the ``Host`` header; otherwise, no port number should be given in
the ``Host`` header.
<../appendices.html#server-name>`_. Where applicable, a delegated server name
uses the same grammar.
Server names are resolved to an IP address and port to connect to, and have
various conditions affecting which certificates and ``Host`` headers to send.
The process overall is as follows:
.. Note from the author: The repetitive "use this Host header and this cert"
comments are intentional. The process is overall quite complicated, and
explaining explicitly what requests look like at each step helps ease the
understanding and ensure everyone is on the same page. Implementations
are of course welcome to realize where the process can be optimized, and
do so - just ensure that the result is the same!
1. If the hostname is an IP literal, then that IP address should be used,
together with the given port number, or 8448 if no port is given. The
target server must present a valid certificate for the IP address.
The ``Host`` header in the request should be set to the server name,
including the port if the server name included one.
2. If the hostname is not an IP literal, and the server name includes an
explicit port, resolve the IP address using AAAA or A records. Requests
are made to the resolved IP address and given port with a ``Host`` header
of the original server name (with port). The target server must present a
valid certificate for the hostname.
3. If the hostname is not an IP literal, a regular HTTPS request is made
to ``https://<hostname>/.well-known/matrix/server``, expecting the
schema defined later in this section. 30x redirects should be followed,
however redirection loops should be avoided. Responses (successful or
otherwise) to the ``/.well-known`` endpoint should be cached by the
requesting server. Servers should respect the cache control headers
present on the response, or use a sensible default when headers are not
present. The recommended sensible default is 24 hours. Servers should
additionally impose a maximum cache time for responses: 48 hours is
recommended. Errors are recommended to be cached for up to an hour,
and servers are encouraged to exponentially back off for repeated
failures. The schema of the ``/.well-known`` request is later in this
section. If the response is invalid (bad JSON, missing properties, non-200
response, etc), skip to step 4. If the response is valid, the ``m.server``
property is parsed as ``<delegated_hostname>[:<delegated_port>]`` and
processed as follows:
* If ``<delegated_hostname>`` is an IP literal, then that IP address
should be used together with the ``<delegated_port>`` or 8448 if no
port is provided. The target server must present a valid TLS certificate
for the IP address. Requests must be made with a ``Host`` header containing
the IP address, including the port if one was provided.
* If ``<delegated_hostname>`` is not an IP literal, and ``<delegated_port>``
is present, an IP address is discovered by looking up an AAAA or A
record for ``<delegated_hostname>``. The resulting IP address is
used, alongside the ``<delegated_port>``. Requests must be made with a
``Host`` header of ``<delegated_hostname>:<delegated_port>``. The
target server must present a valid certificate for ``<delegated_hostname>``.
* If ``<delegated_hostname>`` is not an IP literal and no
``<delegated_port>`` is present, an SRV record is looked up for
``_matrix._tcp.<delegated_hostname>``. This may result in another
hostname (to be resolved using AAAA or A records) and port. Requests
should be made to the resolved IP address and port with a ``Host``
header containing the ``<delegated_hostname>``. The target server
must present a valid certificate for ``<delegated_hostname>``.
* If no SRV record is found, an IP address is resolved using AAAA
or A records. Requests are then made to the resolve IP address
and a port of 8448, using a ``Host`` header of ``<delegated_hostname>``.
The target server must present a valid certificate for ``<delegated_hostname>``.
4. If the ``/.well-known`` request resulted in an error response, a server
is found by resolving an SRV record for ``_matrix._tcp.<hostname>``. This
may result in a hostname (to be resolved using AAAA or A records) and
port. Requests are made to the resolved IP address and port, using 8448
as a default port, with a ``Host`` header of ``<hostname>``. The target
server must present a valid certificate for ``<hostname>``.
5. If the ``/.well-known`` request returned an error response, and the SRV
record was not found, an IP address is resolved using AAAA and A records.
Requests are made to the resolved IP address using port 8448 and a ``Host``
header containing the ``<hostname>``. The target server must present a
valid certificate for ``<hostname>``.
The TLS certificate provided by the target server must be signed by a known
Certificate Authority. Servers are ultimately responsible for determining
the trusted Certificate Authorities, however are strongly encouraged to
rely on the operating system's judgement. Servers can offer administrators
a means to override the trusted authorities list. Servers can additionally
skip the certificate validation for a given whitelist of domains or netmasks
for the purposes of testing or in networks where verification is done
elsewhere, such as with ``.onion`` addresses. Servers should respect SNI
when making requests where possible: a SNI should be sent for the certificate
which is expected, unless that certificate is expected to be an IP address in
which case SNI is not supported and should not be sent.
Servers are encouraged to make use of the
`Certificate Transparency <https://www.certificate-transparency.org/>`_ project.
{{wellknown_ss_http_api}}
Server implementation
~~~~~~~~~~~~~~~~~~~~~~
@ -131,7 +201,7 @@ Retrieving server keys
.. NOTE::
There was once a "version 1" of the key exchange. It has been removed from the
specification due to lack of significance. It may be reviewed `here
specification due to lack of significance. It may be reviewed `from the historical draft
<https://github.com/matrix-org/matrix-doc/blob/51faf8ed2e4a63d4cfd6d23183698ed169956cc0/specification/server_server_api.rst#232version-1>`_.
Each homeserver publishes its public keys under ``/_matrix/key/v2/server/{keyId}``.
@ -153,12 +223,11 @@ server by querying other servers.
Publishing Keys
+++++++++++++++
Homeservers publish the allowed TLS fingerprints and signing keys in a JSON
Homeservers publish their signing keys in a JSON
object at ``/_matrix/key/v2/server/{key_id}``. The response contains a list of
``verify_keys`` that are valid for signing federation requests made by the
homeserver and for signing events. It contains a list of ``old_verify_keys`` which
are only valid for signing events. Finally the response contains a list of TLS
certificate fingerprints to validate any connection made to the homeserver.
are only valid for signing events.
{{keys_server_ss_http_api}}
@ -282,6 +351,8 @@ Transactions are limited in size; they can have at most 50 PDUs and 100 EDUs.
{{transactions_ss_http_api}}
.. _`Persistent Data Unit schema`:
PDUs
----
@ -328,7 +399,7 @@ following subset of the room state:
``m.room.third_party_invite`` event with ``state_key`` matching
``content.third_party_invite.signed.token``, if any.
{{definition_ss_pdu}}
For a full schema of what a PDU looks like, see the `room version specification`_.
Checks performed on receipt of a PDU
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -380,189 +451,9 @@ Authorization rules
The rules governing whether an event is authorized depends on a set of state. A
given event is checked multiple times against different sets of state, as
specified above. The types of state events that affect authorization are:
- ``m.room.create``
- ``m.room.member``
- ``m.room.join_rules``
- ``m.room.power_levels``
- ``m.room.third_party_invite``
The rules are as follows:
1. If type is ``m.room.create``:
a. If it has any previous events, reject.
b. If the domain of the ``room_id`` does not match the domain of the
``sender``, reject.
c. If ``content.room_version`` is present and is not a recognised version,
reject.
d. If ``content`` has no ``creator`` field, reject.
e. Otherwise, allow.
#. Reject if event has ``auth_events`` that:
a. have duplicate entries for a given ``type`` and ``state_key`` pair
#. have entries whose ``type`` and ``state_key`` don't match those
specified by the `auth events selection`_ algorithm described above.
#. If event does not have a ``m.room.create`` in its ``auth_events``, reject.
#. If type is ``m.room.aliases``:
a. If event has no ``state_key``, reject.
b. If sender's domain doesn't matches ``state_key``, reject.
c. Otherwise, allow.
#. If type is ``m.room.member``:
a. If no ``state_key`` key or ``membership`` key in ``content``, reject.
#. If ``membership`` is ``join``:
i. If the only previous event is an ``m.room.create``
and the ``state_key`` is the creator, allow.
#. If the ``sender`` does not match ``state_key``, reject.
#. If the ``sender`` is banned, reject.
#. If the ``join_rule`` is ``invite`` then allow if membership state
is ``invite`` or ``join``.
#. If the ``join_rule`` is ``public``, allow.
#. Otherwise, reject.
#. If ``membership`` is ``invite``:
i. If ``content`` has ``third_party_invite`` key:
#. If *target user* is banned, reject.
#. If ``content.third_party_invite`` does not have a
``signed`` key, reject.
#. If ``signed`` does not have ``mxid`` and ``token`` keys, reject.
#. If ``mxid`` does not match ``state_key``, reject.
#. If there is no ``m.room.third_party_invite`` event in the
current room state with ``state_key`` matching ``token``, reject.
#. If ``sender`` does not match ``sender`` of the
``m.room.third_party_invite``, reject.
#. If any signature in ``signed`` matches any public key in the
``m.room.third_party_invite`` event, allow. The public keys are
in ``content`` of ``m.room.third_party_invite`` as:
#. A single public key in the ``public_key`` field.
#. A list of public keys in the ``public_keys`` field.
#. Otherwise, reject.
#. If the ``sender``'s current membership state is not ``join``, reject.
#. If *target user*'s current membership state is ``join`` or ``ban``,
reject.
#. If the ``sender``'s power level is greater than or equal to the *invite
level*, allow.
#. Otherwise, reject.
#. If ``membership`` is ``leave``:
i. If the ``sender`` matches ``state_key``, allow if and only if that user's
current membership state is ``invite`` or ``join``.
#. If the ``sender``'s current membership state is not ``join``, reject.
#. If the *target user*'s current membership state is ``ban``, and the
``sender``'s power level is less than the *ban level*, reject.
#. If the ``sender``'s power level is greater than or equal to the *kick
level*, and the *target user*'s power level is less than the
``sender``'s power level, allow.
#. Otherwise, reject.
#. If ``membership`` is ``ban``:
i. If the ``sender``'s current membership state is not ``join``, reject.
#. If the ``sender``'s power level is greater than or equal to the *ban
level*, and the *target user*'s power level is less than the
``sender``'s power level, allow.
#. Otherwise, reject.
#. Otherwise, the membership is unknown. Reject.
#. If the ``sender``'s current membership state is not ``join``, reject.
#. If type is ``m.room.third_party_invite``:
a. Allow if and only if ``sender``'s current power level is greater than
or equal to the *invite level*.
#. If the event type's *required power level* is greater than the ``sender``'s power
level, reject.
#. If the event has a ``state_key`` that starts with an ``@`` and does not match
the ``sender``, reject.
#. If type is ``m.room.power_levels``:
a. If ``users`` key in ``content`` is not a dictionary with keys that are
valid user IDs with values that are integers (or a string that is an
integer), reject.
#. If there is no previous ``m.room.power_levels`` event in the room, allow.
#. For each of the keys ``users_default``, ``events_default``,
``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as
each entry being changed under the ``events`` or ``users`` keys:
i. If the current value is higher than the ``sender``'s current power level,
reject.
#. If the new value is higher than the ``sender``'s current power level,
reject.
#. For each entry being changed under the ``users`` key, other than the
``sender``'s own entry:
i. If the current value is equal to the ``sender``'s current power level,
reject.
#. Otherwise, allow.
#. If type is ``m.room.redaction``:
a. If the ``sender``'s power level is greater than or equal to the *redact
level*, allow.
#. If the domain of the ``event_id`` of the event being redacted is the same
as the domain of the ``event_id`` of the ``m.room.redaction``, allow.
#. Otherwise, reject.
#. Otherwise, allow.
.. NOTE::
Some consequences of these rules:
* Unless you are a member of the room, the only permitted operations (apart
from the intial create/join) are: joining a public room; accepting or
rejecting an invitation to a room.
* To unban somebody, you must have power level greater than or equal to both
the kick *and* ban levels, *and* greater than the target user's power
level.
specified above. Each room version can have a different algorithm for how the
rules work, and which rules are applied. For more detailed information, please
see the `room version specification`_.
Rejection
+++++++++
@ -752,191 +643,8 @@ is at the top)::
Suppose E3 and E4 are both ``m.room.name`` events which set the name of the
room. What should the name of the room be at E5?
Servers should follow one of the following recursively-defined algorithms,
depending on the room version, to determine the room state at a given point on
the DAG.
State resolution algorithm for version 2 rooms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The room state :math:`S'(E)` after an event :math:`E` is defined in terms of
the room state :math:`S(E)` before :math:`E`, and depends on whether
:math:`E` is a state event or a message event:
* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except
that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key``
is replaced by :math:`E`'s ``event_id``.
The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of
states :math:`\{ S'(E_1), S'(E_2), … \}` consisting of the states after each of
:math:`E`'s ``prev_event``\s :math:`\{ E_1, E_2, … \}`, where the resolution of
a set of states is given in the algorithm below.
Definitions
+++++++++++
The state resolution algorithm for version 2 rooms uses the following
definitions, given the set of room states :math:`\{ S_1, S_2, \ldots \}`:
Power events
A *power event* is a state event with type ``m.room.power_levels`` or
``m.room.join_rules``, or a state event with type ``m.room.member`` where the
``membership`` is ``leave`` or ``ban`` and the ``sender`` does not match the
``state_key``. The idea behind this is that power events are events that have
may remove someone's ability to do something in the room.
Unconflicted state map and conflicted state set
The *unconflicted state map* is the state where the value of each key exists
and is the same in each state :math:`S_i`. The *conflicted state set* is the
set of all other state events. Note that the unconflicted state map only has
one event per ``(event_type, state_key)``, whereas the conflicted state set
may have multiple events.
Auth difference
The *auth difference* is calculated by first calculating the full auth chain
for each state :math:`S_i`, that is the union of the auth chains for each
event in :math:`S_i`, and then taking every event that doesn't appear in
every auth chain. If :math:`C_i` is the full auth chain of :math:`S_i`, then
the auth difference is :math:`\cup C_i - \cap C_i`.
Full conflicted set
The *full conflicted set* is the union of the conflicted state set and the
auth difference.
Reverse topological power ordering
The *reverse topological power ordering* of a set of events is the
lexicographically smallest topological ordering based on the DAG formed by
auth events. The reverse topological power ordering is ordered from earliest
event to latest. For comparing two topological orderings to determine which
is the lexicographically smallest, the following comparison relation on
events is used: for events :math:`x` and :math:`y`, :math:`x<y` if
1. :math:`x`'s sender has *greater* power level than :math:`y`'s sender,
when looking at their respective ``auth_event``\s; or
2. the senders have the same power level, but :math:`x`'s
``origin_server_ts`` is *less* than :math:`y`'s ``origin_server_ts``; or
3. the senders have the same power level and the events have the same
``origin_server_ts``, but :math:`x`'s ``event_id`` is *less* than
:math:`y`'s ``event_id``.
The reverse topological power ordering can be found by sorting the events
using Kahn's algorithm for topological sorting, and at each step selecting,
among all the candidate vertices, the smallest vertex using the above
comparison relation.
Mainline ordering
Given an ``m.room.power_levels`` event :math:`P`, the *mainline of* :math:`P`
is the list of events generated by starting with :math:`P` and recursively
taking the ``m.room.power_levels`` events from the ``auth_events``, ordered
such that :math:`P` is last. Given another event :math:`e`, the *closest
mainline event to* :math:`e` is the first event encountered in the mainline
when iteratively descending through the ``m.room.power_levels`` events in the
``auth_events`` starting at :math:`e`. If no mainline event is encountered
when iteratively descending through the ``m.room.power_levels`` events, then
the closest mainline event to :math:`e` can be considered to be a dummy event
that is before any other event in the mainline of :math:`P` for the purposes
of condition 1 below.
The *mainline ordering based on* :math:`P` of a set of events is the
ordering, from smallest to largest, using the following comparision relation
on events: for events :math:`x` and :math:`y`, :math:`x<y` if
1. the closest mainline event to :math:`x` appears *before* the closest
mainline event to :math:`y`; or
2. the closest mainline events are the same, but :math:`x`\'s
``origin_server_ts`` is *less* than :math:`y`\'s ``origin_server_ts``; or
3. the closest mainline events are the same and the events have the same
``origin_server_ts``, but :math:`x`\'s ``event_id`` is *less* than
:math:`y`\'s ``event_id``.
Iterative auth checks
The *iterative auth checks algorithm* takes as input an initial room state
and a sorted list of state events, and constructs a new room state by
iterating through the event list and applying the state event to the room
state if the state event is allowed by the `authorization rules`_. If the
state event is not allowed by the authorization rules, then the event is
ignored. If a ``(event_type, state_key)`` key that is required for checking
the authorization rules is not present in the state, then the appropriate
state event from the event's ``auth_events`` is used.
Algorithm
+++++++++
The *resolution* of a set of states is obtained as follows:
1. Take all *power events* and any events in their auth chains, recursively,
that appear in the *full conflicted set* and order them by the *reverse
topological power ordering*.
2. Apply the *iterative auth checks algorithm* on the *unconflicted state map*
and the list of events from the previous step to get a partially resolved
state.
3. Take all remaining events that weren't picked in step 1 and order them by
the mainline ordering based on the power level in the partially resolved
state obtained in step 2.
4. Apply the *iterative auth checks algorithm* on the partial resolved
state and the list of events from the previous step.
5. Update the result by replacing any event with the event with the same key
from the *unconflicted state map*, if such an event exists, to get the final
resolved state.
State resolution algorithm for version 1 rooms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. WARNING::
This section documents the state resolution algorithm as implemented by
Synapse as of December 2017 (and therefore the de-facto Matrix protocol).
However, this algorithm is known to have some problems.
The room state :math:`S'(E)` after an event :math:`E` is defined in terms of
the room state :math:`S(E)` before :math:`E`, and depends on whether
:math:`E` is a state event or a message event:
* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except
that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key``
is replaced by :math:`E`'s ``event_id``.
The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of
states :math:`\{ S'(E'), S'(E''), … \}` consisting of the states after each of
:math:`E`'s ``prev_event``\s :math:`\{ E', E'', … \}`.
The *resolution* of a set of states is defined as follows. The resolved state
is built up in a number of passes; here we use :math:`R` to refer to the
results of the resolution so far.
* Start by setting :math:`R` to the union of the states to be resolved,
excluding any *conflicting* events.
* First we resolve conflicts between ``m.room.power_levels`` events. If there
is no conflict, this step is skipped, otherwise:
* Assemble all the ``m.room.power_levels`` events from the states to
be resolved into a list.
* Sort the list by ascending ``depth`` then descending ``sha1(event_id)``.
* Add the first event in the list to :math:`R`.
* For each subsequent event in the list, check that the event would be
allowed by the `authorization rules`_ for a room in state :math:`R`. If the
event would be allowed, then update :math:`R` with the event and continue
with the next event in the list. If it would not be allowed, stop and
continue below with ``m.room.join_rules`` events.
* Repeat the above process for conflicts between ``m.room.join_rules`` events.
* Repeat the above process for conflicts between ``m.room.member`` events.
* No other events affect the authorization rules, so for all other conflicts,
just pick the event with the highest depth and lowest ``sha1(event_id)`` that
passes authentication in :math:`R` and add it to :math:`R`.
A *conflict* occurs between states where those states have different
``event_ids`` for the same ``(state_type, state_key)``. The events thus
affected are said to be *conflicting* events.
The algorithm to be used for state resolution depends on the room version. For
a description of each room version's algorithm, please see the `room version specification`_.
Backfilling and retrieving missing events
@ -1072,7 +780,9 @@ the homeserver may sign the membership event itself and skip the process defined
here. However, when a user invites another user on a different homeserver, a request
to that homeserver to have the event signed and verified must be made.
{{invites_ss_http_api}}
{{invites_v1_ss_http_api}}
{{invites_v2_ss_http_api}}
Leaving Rooms (Rejecting Invites)
---------------------------------
@ -1195,15 +905,8 @@ The server API for presence is based entirely on exchange of the following
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
sending server's users to their presence list.
Clients may define lists of users that they are interested in via "Presence
Lists" through the `Client-Server API`_. When users are added to a presence
list, a ``m.presence_invite`` EDU is sent to them. The user may then accept
or deny their involvement in the list by sending either an ``m.presence_accept``
or ``m.presence_deny`` EDU back.
would be interested in. Such as the receiving server sharing a room
with a given user.
.. TODO-doc
- Explain the timing-based round-trip reduction mechanism for presence
@ -1213,13 +916,6 @@ or ``m.presence_deny`` EDU back.
{{definition_ss_event_schemas_m_presence}}
{{definition_ss_event_schemas_m_presence_invite}}
{{definition_ss_event_schemas_m_presence_accept}}
{{definition_ss_event_schemas_m_presence_deny}}
Receipts
--------
@ -1297,7 +993,7 @@ which should be used to correlate with subsequent ``m.device_list_update`` EDUs.
.. TODO: this whole thing desperately feels like it should just be state in a room,
rather than inventing a whole different DAG. The same room could be used for
profiles, presence lists, etc.
profiles etc.
{{user_devices_ss_http_api}}
@ -1412,6 +1108,24 @@ been given a redacted version of the event. To enforce this, the receiving
server should use the redacted copy it calculated rather than the full copy it
received.
.. _`reference hashes`:
Calculating the reference hash for an event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The *reference hash* of an event covers the essential fields of an event,
including content hashes. It is calculated as follows.
1. The event is put through the redaction algorithm.
2. The ``signatures``, ``age_ts``, and ``unsigned`` properties are removed
from the event, if present.
3. The event is converted into `Canonical JSON`_.
4. A sha256 hash is calculated on the resulting JSON object.
Calculating the content hash for an event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1454,7 +1168,7 @@ Example code
# Keys under "unsigned" can be modified by other servers.
# They are useful for conveying information like the age of an
# event that will change in transit.
# Since they can be modifed we need to exclude them from the hash.
# Since they can be modified we need to exclude them from the hash.
event_object.pop("unsigned", None)
# Signatures will depend on the current value of the "hashes" key.
@ -1476,7 +1190,7 @@ Example code
[[TODO(markjh): Since the ``hash`` object cannot be redacted a server
shouldn't allow too many hashes to be listed, otherwise a server might embed
illict data within the ``hash`` object.
illicit data within the ``hash`` object.
We might want to specify a maximum number of keys for the
``hash`` and we might want to specify the maximum output size of a hash]]
@ -1485,6 +1199,16 @@ Example code
known hash functions like SHA-256 when none of the keys have been redacted]]
Security considerations
-----------------------
When a domain's ownership changes, the new controller of the domain can masquerade
as the previous owner, receiving messages (similarly to email) and request past
messages from other servers. In the future, proposals like
`MSC1228 <https://github.com/matrix-org/matrix-doc/issues/1228>`_ will address this
issue.
.. |/query/directory| replace:: ``/query/directory``
.. _/query/directory: #get-matrix-federation-v1-query-directory
@ -1500,3 +1224,4 @@ Example code
.. _`Checking for a signature`: ../appendices.html#checking-for-a-signature
.. _`Device Management module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#device-management
.. _`End-to-End Encryption module`: ../client_server/%CLIENT_RELEASE_LABEL%.html#end-to-end-encryption
.. _`room version specification`: ../index.html#room-versions

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save