Merge branch 'master' into babolivier/standardised-federation-response-format

babolivier/standardised-federation-response-format
Brendan Abolivier 5 years ago
commit c1721cb6f7

@ -0,0 +1,11 @@
steps:
- label: ":books: Build spec"
command:
- python3 -m venv env
- env/bin/pip install -r scripts/requirements.txt
- ". env/bin/activate; scripts/generate-matrix-org-assets"
artifact_paths:
- assets.tar.gz
plugins:
- docker#v3.0.1:
image: "python:3.6"

@ -76,11 +76,11 @@ Adding to the changelog
Currently only changes to the client-server API need to end up in a changelog. The
other APIs are not yet stable and therefore do not have a changelog. Adding to the
changelog can only be done after you've opened your pull request, so be sure to do
that first.
that first.
The changelog is managed by Towncrier (https://github.com/hawkowl/towncrier) in the
form of "news fragments". The news fragments for the client-server API are stored
under ``changelogs/client_server/newsfragments``.
under ``changelogs/client_server/newsfragments``.
To create a changelog entry, create a file named in the format ``prNumber.type`` in
the ``newsfragments`` directory. The ``type`` can be one of the following:
@ -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.
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

@ -1,66 +0,0 @@
# Copyright 2016 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Client-Server CAS Login API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
paths:
"/login/cas/ticket":
get:
summary: Receive and validate a CAS login ticket.
description: |-
Once the CAS server has authenticated the user, it will redirect the
browser to this endpoint (assuming |/login/cas/redirect|_ gave it the
correct ``service`` parameter).
The server MUST call ``/proxyValidate`` on the CAS server, to validate
the ticket supplied by the browser.
If validation is successful, the server must generate a Matrix login
token. It must then respond with an HTTP redirect to the URI given in
the ``redirectUrl`` parameter, adding a ``loginToken`` query parameter
giving the generated token.
If validation is unsuccessful, the server should respond with a ``401
Unauthorized`` error, the body of which will be displayed to the user.
operationId: loginByCASTicket
parameters:
- in: query
type: string
name: redirectUrl
description: |-
The ``redirectUrl`` originally provided by the client to
|/login/cas/redirect|_.
required: true
- in: query
type: string
name: ticket
description: |-
CAS authentication ticket.
required: true
responses:
302:
description: A redirect to the Matrix client.
headers:
Location:
type: "string"
x-example: |-
https://client.example.com/?q=p&loginToken=secrettoken
401:
description: The server was unable to validate the CAS ticket.

@ -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

@ -1,4 +1,4 @@
# Copyright 2016 OpenMarket Ltd
# 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.
@ -13,7 +13,7 @@
# limitations under the License.
swagger: '2.0'
info:
title: "Matrix Client-Server CAS Login API"
title: "Matrix Client-Server SSO Login API"
version: "1.0.0"
host: localhost:8008
schemes:
@ -21,34 +21,26 @@ schemes:
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
paths:
"/login/cas/redirect":
"/login/sso/redirect":
get:
summary: Redirect the user's browser to the CAS interface.
summary: Redirect the user's browser to the SSO interface.
description: |-
A web-based Matrix client should instruct the user's browser to
navigate to this endpoint in order to log in via CAS.
navigate to this endpoint in order to log in via SSO.
The server MUST respond with an HTTP redirect to the CAS interface. The
URI MUST include a ``service`` parameter giving the path of the
|/login/cas/ticket|_ endpoint (including the ``redirectUrl`` query
parameter).
For example, if the endpoint is called with
``redirectUrl=https://client.example.com/?q=p``, it might redirect to
``https://cas.example.com/?service=https%3A%2F%2Fserver.example.com%2F_matrix%2Fclient%2F%CLIENT_MAJOR_VERSION%%2Flogin%2Fcas%2Fticket%3FredirectUrl%3Dhttps%253A%252F%252Fclient.example.com%252F%253Fq%253Dp``.
operationId: redirectToCAS
The server MUST respond with an HTTP redirect to the SSO interface.
operationId: redirectToSSO
parameters:
- in: query
type: string
name: redirectUrl
description: |-
URI to which the user will be redirected after the homeserver has
authenticated the user with CAS.
authenticated the user with SSO.
required: true
responses:
302:
description: A redirect to the CAS interface.
description: A redirect to the SSO interface.
headers:
Location:
type: "string"

@ -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:

@ -90,7 +90,7 @@ paths:
}
schema:
$ref: "../client-server/definitions/errors/error.yaml"
"/bind":
"/3pid/bind":
post:
summary: Publish an association between a session and a Matrix user ID.
description: |-

@ -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>`_.

@ -1 +1 @@
Add missing status_msg to m.presence schema
Add missing status_msg to m.presence schema.

@ -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"
}
}

@ -10,7 +10,7 @@
"properties": {
"call_id": {
"type": "string",
"description": "A unique identifer for the call."
"description": "A unique identifier for the call."
},
"offer": {
"type": "object",

@ -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

@ -1,3 +0,0 @@
#!/bin/sh
exec ./scripts/test-and-build.sh

@ -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)

@ -33,4 +33,7 @@ is to pick up to 3 unique servers where the first one is that of the user with t
highest power level in the room, provided that power level is 50 or higher. The other
2 servers should be the most popular servers in the room based on the number of joined
users. This same heuristic should apply to the first server if no user meets the power
level requirements.
level requirements. Servers blocked by server ACLs should not be picked because they
are unlikely to continue being residents of the room. Similarly, IP addresses should
not be picked because they cannot be redirected to another location like domain names
can, making them a higher risk option.

@ -0,0 +1,203 @@
# MSC1708: .well-known support for server name resolution
Currently, mapping from a server name to a hostname for federation is done via
`SRV` records. However,
[MSC1711](https://github.com/matrix-org/matrix-doc/pull/1711) proposes
requiring valid X.509 certificates on the federation endpoint. It will then be
necessary for the homeserver to present a certificate which is valid for the
server name. This presents difficulties for hosted server offerings: BigCorp
may want to delegate responsibility for running its Matrix homeserver to an
outside supplier, but it may be difficult for that supplier to obtain a TLS
certificate for `bigcorp.com` (and BigCorp may be reluctant to let them have
one).
This MSC proposes to solve this problem by augmenting the current `SRV` record
with a `.well-known` lookup.
## Proposal
For reference, the current [specification for resolving server
names](https://matrix.org/docs/spec/server_server/unstable.html#resolving-server-names)
is as follows:
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.
2. 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.
3. 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.
4. Finally, the server is discovered by looking up an AAAA or A record on the
hostname, and taking the default fallback port number of 8448.
We insert the following between Steps 3 and 4.
If the SRV record does not exist, the requesting server should make a `GET`
request to `https://<server_name>/.well-known/matrix/server`, with normal X.509
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 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:
* If `<delegated_server_name>` is an IP literal, then that IP address should be
used, together with `<delegated_port>`, or 8448 if no port is given. The
server should present a valid TLS certificate for `<delegated_server_name>`.
* If `<delegated_server_name>` is not an IP literal, and `<delegated_port>` is
present, then an IP address is discovered by looking up an AAAA or A record
for `<delegated_server_name>`, and the specified port is used. The server
should present a valid TLS certificate for `<delegated_server_name>`.
(In other words, the federation connection is made to
`https://<delegated_server_name>:<delegated_port>`).
* If the hostname is not an IP literal and no port is given, a second SRV
record is looked up; this time for `_matrix._tcp.<delegated_server_name>`,
which may give yet another hostname (to be looked up using A/AAAA queries)
and port. The server must present a TLS cert for the
`<delegated_server_name>` from the .well-known.
* If no SRV record is found, the server is discovered by looking up an AAAA
or A record on `<delegated_server_name>`, and taking the default fallback
port number of 8448.
(In other words, the federation connection is made to
`https://<delegated_server_name>:8448`).
### Structure of the `.well-known` response
The contents of the `.well-known` response should be structured as shown:
```json
{
"m.server": "<server>[:<port>]"
}
```
If the response cannot be parsed as JSON, or lacks a valid `m.server` property,
the request is considered to have failed, and no fallback to port 8448 takes
place.
The formal grammar for the `m.server` property is the same as that of a [server
name](https://matrix.org/docs/spec/appendices.html#server-name): it is a
hostname or IP address, followed by an optional port.
### Caching
Servers should not look up the `.well-known` file for every request, as this
would impose an unacceptable overhead on both sides. Instead, the results of
the `.well-known` request should be cached according to the HTTP response
headers, as per [RFC7234](https://tools.ietf.org/html/rfc7234). If the response
does not include an explicit expiry time, the requesting server should use a
sensible default: 24 hours is suggested.
Because there is no way to request a revalidation, it is also recommended that
requesting servers cap the expiry time. 48 hours is suggested.
A failure to retrieve the `.well-known` file should also be cached, though care
must be taken that a single 500 error or connection failure should not break
federation for an extended period. A short cache time of about an hour might be
appropriate; alternatively, servers might use an exponential backoff.
## Problems
It will take a while for `.well-known` to be supported across the ecosystem;
until it is, it will be difficult to deploy homeservers which rely on it for
their routing: if Alice is using a current homeserver implementation, and Bob
deploys a new implementation which relies on `.well-known` for routing, then
Alice will be unable to send messages to Bob. (This is the same problem we have with
[SNI](https://github.com/matrix-org/synapse/issues/1491#issuecomment-415153428).)
The main defence against this seems to be to release support for `.well-known`
as soon as possible, to maximise uptake in the ecosystem. It is likely that, as
we approach Matrix 1.0, there will be sufficient other new features (such as
new Room versions) that upgrading will be necessary anyway.
## Security considerations
The `.well-known` file potentially broadens the attack surface for an attacker
wishing to intercept federation traffic to a particular server.
## Dismissed alternatives
For future reference, here are the alternative solutions which have been
considered and dismissed.
### Look up the `.well-known` file before the SRV record
We could make the request for `.well-known` before looking up the `SRV`
record. On the one hand this is maybe marginally simpler (and avoids the
overhead of having to make *two* `SRV` lookups in the case that a `.well-known`
is found. It might also open a future path for using `.well-known` for
information other than delegation.
Ultimately we decided to include the initial `SRV` lookup so that deployments
have a mechanism to avoid the `.well-known` overhead in the common case that it
is not required.
### Subdomain hack
As well as accepting TLS certs for `example.com`, we could also accept them for
`delegated--matrix.example.com`. This would allow `example.com` to delegate its
matrix hosting by (a) setting up the SRV record at `_matrix._tcp.example.com`
and (b) setting up a CNAME at `delegated--matrix.example.com`. The latter would
enable the delegatee to obtain an acceptable TLS certificate.
This was certainly an interesting idea, but we dismissed it for the following
reasons:
* There's a security trap for anybody who lets people sign up for subdomains
(which is certainly not an uncommon business model): if you can register for
delegated--matrix.example.com, you get to intercept all the matrix traffic
for example.com.
* Generally it feels quite unintuitive and violates the principle of least
surprise.
* The fact that we can't find any prior art for this sets off alarm bells too.
### Rely on DNS/DNSSEC
If we could trust SRV records, we would be able to accept TLS certs for the
*target* of the SRV record, which avoids this whole problem.
Such trust could come from assuming that plain DNS is "good enough". However,
DNS cache poisoning attacks are a real thing, and the fact that the designers
of TLS chose to implement a server-name check specifically to deal with this
case suggests we would be foolish to make this assumption.
The alternative is to rely on DNSSEC to provide security for SRV records. The
problem here is simply that DNSSEC is not that widely deployed currently. A
number of large organisations are actively avoiding enabling it on their
domains, so requiring DNSSEC would be a direct impediment to the uptake of
Matrix. Furthermore, if we required DNSSEC-authenticated SRV records for
domains doing delegation, we would end up with a significant number of
homeservers unable to talk to such domains, because their local DNS
infrastructure may not implement DNSSEC.
Finally, if we're expecting servers to present the cert for the *target* of the
SRV record, then we'll have to change the Host and SNI fields, and that will
break backwards compat everywhere (and it's hard to see how to mitigate that).
### Stick with perspectives
The final option is to double-down on the Perspectives approach, ie to skip
[MSC1711](https://github.com/matrix-org/matrix-doc/pull/1711). MSC1711
discusses the reasons we do not believe this to be a viable option.
## Conclusion
This proposal adds a new mechanism, alongside the existing `SRV` record lookup
for finding the server responsible for a particular matrix server_name, which
will allow greater flexibility in deploying homeservers.

@ -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,166 @@
# MSC1884: Proposal to replace slashes in event IDs
[MSC1659](https://github.com/matrix-org/matrix-doc/pull/1659) mandated that,
starting in version 3 rooms, event IDs must be calculated as a base64-encoding
of a hash. This implies that event IDs may contain any character in the
standard Base64 alphabet, which notably includes the slash character, `/`.
Event IDs are often embedded in URI paths, and since the slash character is
used as a separator in URI paths, this presents a problem. The immediate
solution is to ensure that event IDs are URL-encoded, so that `/` is instead
represented as `%2F`. However, this is not entirely satisfactory for a number
of reasons:
* The act of escaping and unescaping slash characters when manually calling
the API during devops work becomes an constant and annoying chore which
is entirely avoidable. Whenever using tools like `curl` and `grep` or
manipulating SQL, developers will have to constantly keep in mind whether
they are dealing with escaped or unescaped IDs, and manually convert between
the two as needed. This will only get worse with further keys-as-IDs
landing with MSC1228.
* There exist a number of client (and possibly server) implementations which
do not currently URL-encode such parameters; these are therefore broken by
such event IDs and must be updated. Furthermore, all future client
implementers must remember to do the encoding correctly.
* Even if client implementations do remember to URL-encode their parameters,
they may not do it correctly: many URL-encoding implementations may be
intended to encode parameters in the query-string (which can of course
contain literal slashes) rather than the path component.
* Some proxy software may treat `%2F` specially: for instance, Apache, when
configured as a reverse-proxy, will reject requests for a path containing
`%2F` unless it is also configured with `nocanon`. Again this means that
existing setups will be broken by this change, and it is a trap for new
users of the software.
* Cosmetically, URL-escaping base64 in otherwise-constant-length IDs results
in variable length IDs, making it harder to visually scan lists of IDs and
manipulate them in columnar form when doing devops work.
* Those developing against the CS API might reasonably expect us to use
URL-safe identifiers in URLs where available, rather than deliberately
choosing non-URL-safe IDs, which could be seen as developer-unfriendly.
## Proposal
This MSC proposes that we should introduce a new room version, in which event
IDs are encoded using the [URL-safe Base64
encoding](https://tools.ietf.org/html/rfc4648#section-5) (which uses `-` and
`_` as the 62nd and 63rd characters instead of `+` and `/`).
We will then aim to use URL-safe Base64 encoding across Matrix in future,
such that typical CS API developers should be able to safely assume
that for all common cases (including upcoming MSC1228 identifiers) they should
use URL-safe Base64 when decoding base64 strings.
The exception would be for E2EE data (device keys and signatures etc) which
currently use normal Base64 with no easy mechanism to migrate to a new encoding.
Given E2EE development is rare and requires expert skills, it seems acceptable
to expect E2EE developers to be able to use the right encoding without tripping
up significantly.
Similarly, the S2S API could continue to use standard base64-encoded hashes and
signatures in the places it does today, given they are only exposed to S2S API
developers who are necessarily expert and should be able to correctly pick the
right encoding.
## Counterarguments
1. Inconsistency. Base64 encoding is used heavily elsewhere in the matrix
protocol and in all cases the standard encoding is used (though with some
variation as to the inclusion of padding characters). Further, SHA256 hashes
are used in a number of places and are universally included with standard,
unpadded Base64.
Changing event IDs alone would therefore leave us with a confusing mix of
encodings.
However, the current uses of standard Base64 encodings are not exposed to
common CS API developers, and so whilst this might be slightly confusing
for the minority of expert homeserver developers, the confusion does not
exist today for client developers (except those implementing E2EE).
Therefore it seems safe to standardise on URL-safe Base64 for identifiers
exposed to the client developers, who form by far the majority of the
Matrix ecosystem today, and expect as simple an API as possible.
A potential extension would be to change *all* Base64 encodings to be
URL-safe. This would address the inconsistency. However, it feels like a
large job which would span the entire matrix ecosystem (far larger than
updating clients to URL-encode their URL prarameters), and again the
situation would be confusing while the transition was in progress.
2. Incompleteness. Event IDs are certainly not the only identifier which can
contain slashes - Room aliases, Room IDs, Group IDs, User IDs [1], and state
keys can all contain slashes, as well as a number of identifiers whose
grammars are currently underspecified (eg transaction ids, event types,
device IDs). (Indeed, there was nothing preventing Event IDs from containing
slashes before room v3 - it just happened that Synapse used an algorithm
which didn't generate them).
All of these other identifiers can appear in URLs in either or both the
client-server or server-server APIs, and all have the potential to cause
misbehaviour if software does not correctly URL-encode them.
It can be argued that it is better for software to fail 50% of the time [2]
so that it can be fixed than it is to fail only on edge-cases or, worse,
when deliberately provoked by a malicious or "curious" actor.
Of course, an alternative is to modify the grammars of all of these
identifiers to forbid slashes.
The counter-counterargument to this is that it is of course best practice
for implementations is to URL-escape any IDs used in URLs, and human-selected
IDs such as Room aliases, Group IDs, Matrix user IDs etc apply an adequate
forcing function already to remind developers to do this. However,
it doesn't follow that we should then also deliberately pick URL-unsafe
encodings for machine-selected IDs - the argument that it is better for software
to fail 50% of the time to force a fix is irrelevant when the possibility
exists for the software to fail 0% of the time in the first place by picking
an identifier format which cannot fail.
[1] Discussion remains open as to whether allowing slashes in User IDs was a
good idea.
[2] 48% of random 32-byte sequences will contain a slash when Base64-encoded.
## Alternatives
An alternative would be to modify all REST endpoints to use query or body
parameters instead of path parameters. This would of course be a significant
and incompatible change, but it would also bring the benefit of solving a
common problem where forgetting to use `nocanon` in a reverse-proxy
configuration [breaks
federation](https://github.com/matrix-org/synapse/issues/3294) (though other
solutions to that are also possible).
## Conclusion
There are two main questions here:
1. Whether it's worth forcing CS API developers to juggle escaping of
machine-selected IDs during manual use of the API in order to remind them
to escape all variables in their URIs correctly when writing code.
2. Whether it's a significant problem for E2EE & SS API developers to have to
handle strings which are a mix of standard Base64 and URL-safe Base64
encodings.
Both of these are a subjective judgement call.
Given we wish the CS API particularly to be as easy as possible for manual
use, it feels that we should find another way to encourage developers to
escape variables in their URLs in general - e.g. by recommending that
developers test their clients against a 'torture room' full of exotic IDs and
data, or by improving warnings in the spec... rather than (ab)using
machine-selected IDs as a reminder.
Meanwhile, given we have many more people manually invoking the CS API than
developing on the SS or E2EE APIs, and we wish to make the CS API particularly
easy for developers to manually invoke, it feels we should not prioritise
consistency of encodings for SS/E2EE developers over the usability of the CS
API.
Therefore, on balance, it seems plausible that changing the format of event IDs
does solve sufficient problems to make it desirable.

@ -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/3pid/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 `/3pid/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/3pid/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.

@ -0,0 +1,40 @@
# Proposal to add a default push rule for m.room.tombstone events
Currently users are unaware of when a room becomes upgraded, leaving them potentially in the old room
without knowing until they visit the room again. By having a notification for when the room is upgraded,
users are able to ensure they are able to stay relevant in rooms by joining the upgraded room.
## Proposal
A new default override rule is to be added which is similar to `@room` notifications:
```json
{
"rule_id": ".m.rule.tombstone",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.room.tombstone"
}
],
"actions": [
"notify",
{
"set_tweak": "highlight",
"value": true
}
]
}
```
## Tradeoffs
Clients could calculate this on their own and show some sort of "room upgraded" notification instead,
however by doing it this way it means that all clients would need to be aware of room upgrades. Having
a default push rule means that clients get this notification for free. Clients which want a more diverse
UX can still do so by ignoring this push rule locally.

@ -0,0 +1,77 @@
# Remove prev_content from the essential keys list
Matrix supports the concept of event redaction. The ability to redact rather
than delete is necessary because some events e.g. membership events are
essential to the protocol and _cannot_ be deleted. Therefore we do not delete
events outright and instead redact them. This involves removing all keys from
an event that are not required by the protocol. The stripped down event is
thereafter returned anytime a client or remote server requests it.
## Proposal
[The redaction algorithm](https://matrix.org/docs/spec/client_server/r0.4.0.html#redactions)
defines which keys must be retained through a redaction. Currently it lists
```prev_content``` as a key to retain, though in practice there is no need to
do so at the protocol level.
The proposal is simply to remove ```prev_content``` from the essential keys
list.
Note: the inclusion of ```prev_content``` in the essential keys list was
unintentional and should be considered a spec bug. Synapse (and other server
implementations) have not implemented the bug and already omit
```prev_content``` from redacted events.
## Tradeoffs
When sending events over federation the events are [hashed and
signed](https://matrix.org/docs/spec/server_server/unstable.html#adding-hashes-and-signatures-to-outgoing-events),
this involves operating not only on the original event but also the redacted
form of the event. The redacted hash and redacted signed event are necessary if
the event is ever redacted in future. As a result, any change of the essential
keys list must be managed carefully. If disparate servers implement different
versions of the redaction algorithm (for a given event) attempts to send the
event over federation will fail.
We _could_ manage this change via room versioning and create a new room
version that implements this MSC. However, because the federation already
omits the ```prev_content``` key by convention, implementing this MSC only in
the new room version would mean that the entire existing federation would not
be spec compliant.
As a result it seems pragmatic to have the spec reflect reality, acknowledge
that the spec and federation have deviated and instead update the spec
retrospectively to describe the de-facto redaction algorithm.
## Potential issues
It is theoretically possible that a closed federation could exist whose servers
do follow the spec as is. This MSC would render those servers non-compliant with
the spec. On balance this seems unlikely and in the worst case those
implementors could add the change to a subsequent room version, eventually
reaching spec consistency as older room versions are deprecated.
Another scenario is that a client may redact events according to the spec as is
and persist prev_content through the redaction, thereby diverting from that on
the server(s). Client authors will have to update their code to drop
```prev_content``` - however, given that prev_content should not be used in
important calculations and/or visualisations, this ought to be a relatively
non-invasive change.
## Security considerations
A further reason to support removal of ```prev_content``` is the case where a
malicious user adds illegal or abusive content into a state event and then
overwrites that state event. The content would then be preserved through the
redaction.
Additionally, there are plenty of reasons to have security concerns over a
precedent that the federation can deviate from the spec.
## Conclusions
Removing ```prev_content``` is pragmatic response to the current situation. It
alligns the federation and the spec, and does so in a way that removes
unecessary overhead.

@ -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)

@ -4,8 +4,12 @@
docutils >= 0.14
pygments >= 2.2.0
Jinja2 >= 2.9.6
jsonschema >= 2.6.0
# jsonschema 3.0.0 objects to the $refs in our schema file. TODO: figure out
# why.
jsonschema >= 2.6.0, < 3.0.0
PyYAML >= 3.12
requests >= 2.18.4
towncrier == 18.6.0
six >= 1.11.0
six >= 1.11.0

@ -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::
$opaque_id:domain
An event has exactly one event ID. The format of an event ID depends upon the
`room version specification <index.html#room-versions>`_.
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.
@ -338,28 +311,90 @@ in the room's history (a permalink).
A matrix.to URI has the following format, based upon the specification defined
in RFC 3986:
https://matrix.to/#/<identifier>/<extra parameter>
https://matrix.to/#/<identifier>/<extra parameter>?<additional arguments>
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.
The ``<additional arguments>`` and the preceeding question mark are optional and
only apply in certain circumstances, documented below.
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/#/%23somewhere: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::
Room ID permalinks are unroutable as there is no reliable domain to send requests
to upon receipt of the permalink. Clients should do their best route Room IDs to
where they need to go, however they should also be aware of `issue #1579 <https://github.com/matrix-org/matrix-doc/issues/1579>`_.
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.
Routing
<<<<<<<
Room IDs are not routable on their own as there is no reliable domain to send requests
to. This is partially mitigated with the addition of a ``via`` argument on a matrix.to
URI, however the problem of routability is still present. Clients should do their best
to route Room IDs to where they need to go, however they should also be aware of
`issue #1579 <https://github.com/matrix-org/matrix-doc/issues/1579>`_.
A room (or room permalink) which isn't using a room alias should supply at least one
server using ``via`` in the ``<additional arguments>``, like so:
``https://matrix.to/!somewhere%3Aexample.org?via=example.org&via=alt.example.org``. The
parameter can be supplied multiple times to specify multiple servers to try.
The values of ``via`` are intended to be passed along as the ``server_name`` parameters
on the Client Server ``/join`` API.
When generating room links and permalinks, the application should pick servers which
have a high probability of being in the room in the distant future. How these servers
are picked is left as an implementation detail, however the current recommendation is
to pick 3 unique servers based on the following criteria:
* The first server should be the server of the highest power level user in the room,
provided they are at least power level 50. If no user meets this criteria, pick the
most popular server in the room (most joined users). The rationale for not picking
users with power levels under 50 is that they are unlikely to be around into the
distant future while higher ranking users (and therefore servers) are less likely
to give up their power and move somewhere else. Most rooms in the public federation
have a power level 100 user and have not deviated from the default structure where
power level 50 users have moderator-style privileges.
* The second server should be the next highest server by population, or the first
highest by population if the first server was based on a user's power level. The
rationale for picking popular servers is that the server is unlikely to be removed
as the room naturally grows in membership due to that server joining users. The
server could be refused participation in the future due to server ACLs or similar,
however the chance of that happening to a server which is organically joining the
room is unlikely.
* The third server should be the next highest server by population.
* Servers which are blocked due to server ACLs should never be chosen.
* Servers which are IP addresses should never be chosen. Servers which use a domain
name are less likely to be unroutable in the future whereas IP addresses cannot be
pointed to a different location and therefore higher risk options.
* All 3 servers should be unique from each other. If the room does not have enough users
to supply 3 servers, the application should only specify the servers it can. For example,
a room with only 2 users in it would result in maximum 2 ``via`` parameters.

@ -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
@ -1016,9 +1024,14 @@ follows:
}
As with `token-based`_ interactive login, the ``token`` must encode the
user id. In the case that the token is not valid, the homeserver must respond
user ID. In the case that the token is not valid, the homeserver must respond
with ``403 Forbidden`` and an error code of ``M_FORBIDDEN``.
If the homeserver advertises ``m.login.sso`` as a viable flow, and the client
supports it, the client should redirect the user to the ``/redirect`` endpoint
for `Single Sign-On <#sso-client-login>`_. After authentication is complete, the
client will need to submit a ``/login`` request matching ``m.login.token``.
{{login_cs_http_api}}
{{logout_cs_http_api}}
@ -1065,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
----------
@ -1161,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

@ -1,119 +0,0 @@
.. Copyright 2016 OpenMarket Ltd
..
.. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License.
.. You may obtain a copy of the License at
..
.. http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing, software
.. distributed under the License is distributed on an "AS IS" BASIS,
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
.. See the License for the specific language governing permissions and
.. limitations under the License.
CAS-based client login
======================
.. _module:cas_login:
`Central Authentication Service
<https://github.com/apereo/cas/blob/master/docs/cas-server-documentation/protocol/CAS-Protocol-Specification.md>`_
(CAS) is a web-based single sign-on protocol.
An overview of the process, as used in Matrix, is as follows:
1. The Matrix client instructs the user's browser to navigate to the
|/login/cas/redirect|_ endpoint on the user's homeserver.
2. The homeserver responds with an HTTP redirect to the CAS user interface,
which the browser follows.
3. The CAS system authenticates the user.
4. The CAS server responds to the user's browser with a redirect back to the
|/login/cas/ticket|_ endpoint on the homeserver, which the browser
follows. A 'ticket' identifier is passed as a query parameter in the
redirect.
5. The homeserver receives the ticket ID from the user's browser, and makes a
request to the CAS server to validate the ticket.
6. Having validated the ticket, the homeserver responds to the browser with a
third HTTP redirect, back to the Matrix client application. A login token
is passed as a query parameter in the redirect.
7. The Matrix client receives the login token and passes it to the |/login|_
API.
Client behaviour
----------------
The client starts the process by instructing the browser to navigate to
|/login/cas/redirect|_ with an appropriate ``redirectUrl``. Once authentication
is successful, the browser will be redirected to that ``redirectUrl``.
.. TODO-spec
Should we recommend some sort of CSRF protection here (specifically, we
should guard against people accidentally logging in by sending them a link
to ``/login/cas/redirect``.
Maybe we should recommend that the ``redirectUrl`` should contain a CSRF
token which the client should then check before sending the login token to
``/login``?
{{cas_login_redirect_cs_http_api}}
{{cas_login_ticket_cs_http_api}}
Server behaviour
----------------
The URI for the CAS system to be used should be configured on the server by the
server administrator.
Handling the redirect endpoint
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When responding to the ``/login/cas/redirect`` endpoint, the server must
generate a URI for the CAS login page. The server should take the base CAS URI
described above, and add a ``service`` query parameter. This parameter MUST be
the URI of the ``/login/cas/ticket`` endpoint, including the ``redirectUrl``
query parameter. Because the homeserver may not know its base URI, this may
also require manual configuration.
.. TODO-spec:
It might be nice if the server did some validation of the ``redirectUrl``
parameter, so that we could check that aren't going to redirect to a non-TLS
endpoint, and to give more meaningful errors in the case of
faulty/poorly-configured clients.
Handling the authentication endpoint
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When responding to the ``/login/cas/ticket`` endpoint, the server MUST make a
request to the CAS server to validate the provided ticket. The server MAY also
check for certain user attributes in the response. Any required attributes
should be configured by the server administrator.
Once the ticket has been validated, the server MUST map the CAS ``user_id``
to a valid `Matrix user identifier <../index.html#user-identifiers>`_. The
guidance in `Mapping from other character sets
<../index.html#mapping-from-other-character-sets>`_ may be useful.
If the generated user identifier represents a new user, it should be registered
as a new user.
Finally, the server should generate a short-term login token. The generated
token should be a macaroon, suitable for use with the ``m.login.token`` type of
the |/login|_ API, and `token-based interactive login <#token-based>`_. The
lifetime of this token SHOULD be limited to around five seconds.
.. |/login| replace:: ``/login``
.. _/login: #post-matrix-client-%CLIENT_MAJOR_VERSION%-login
.. |/login/cas/redirect| replace:: ``/login/cas/redirect``
.. _/login/cas/redirect: #get-matrix-client-%CLIENT_MAJOR_VERSION%-login-cas-redirect
.. |/login/cas/ticket| replace:: ``/login/cas/ticket``
.. _/login/cas/ticket: #get-matrix-client-%CLIENT_MAJOR_VERSION%-login-cas-ticket

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

Loading…
Cancel
Save