Merge branch 'old_master' into travis/msc/voice-messages

travis/msc/voice-messages
Travis Ralston 1 year ago
commit 41bb8e5b92

3
.gitignore vendored

@ -1,5 +1,4 @@
/api/node_modules
/assets
/assets.tar.gz
/data/msc
/env*
@ -18,4 +17,4 @@
_rendered.rst
/.vscode/
/.idea/
/spec/
/spec/

@ -5,44 +5,44 @@ This repository contains the Matrix Specification, rendered at [spec.matrix.org]
Developers looking to use Matrix should join [#matrix-dev:matrix.org](https://matrix.to/#/#matrix-dev:matrix.org)
on Matrix for help.
Spec authors and proposal writers are welcome to join [#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org).
Spec authors and proposal writers are welcome to join [#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org).
We welcome contributions! See [CONTRIBUTING.rst](./CONTRIBUTING.rst) for details.
## Structure
The Matrix spec is compiled with [Hugo](https://gohugo.io/) (a static site generator) with the following structure:
* `/assets`: assets that need postprocessing using [Hugo Pipes](https://gohugo.io/hugo-pipes/introduction/).
* `/assets`: assets that need postprocessing using [Hugo Pipes](https://gohugo.io/hugo-pipes/introduction/).
For example, Sass files would go here.
* `/content`: files that will become pages in the site go here. Typically these are Markdown files with some YAML front
matter indicating, [among other things](https://gohugo.io/content-management/front-matter/), what layout should be
applied to this page. The organization of files under `/content` determines the organization of pages in the built
* `/content`: files that will become pages in the site go here. Typically these are Markdown files with some YAML front
matter indicating, [among other things](https://gohugo.io/content-management/front-matter/), what layout should be
applied to this page. The organization of files under `/content` determines the organization of pages in the built
site.
* `/data`: this can contain TOML, YAML, or JSON files. Files kept here are directly available to template code as
[data objects](https://gohugo.io/templates/data-templates/), so templates don't need to load them from a file and
* `/data`: this can contain TOML, YAML, or JSON files. Files kept here are directly available to template code as
[data objects](https://gohugo.io/templates/data-templates/), so templates don't need to load them from a file and
parse them. This is also where our Swagger/OpenAPI definitions and schemas are.
* `/layouts`: this contains [Hugo templates](https://gohugo.io/templates/). Some templates define the overall layout of
* `/layouts`: this contains [Hugo templates](https://gohugo.io/templates/). Some templates define the overall layout of
a page: for example, whether it has header, footer, sidebar, and so on.
* `/layouts/partials`: these templates can be called from other templates, so they can be used to factor out
template code that's used in more than one template. An obvious example here is something like a sidebar, where
several different page layouts might all include the sidebar. But also, partial templates can return values: this
* `/layouts/partials`: these templates can be called from other templates, so they can be used to factor out
template code that's used in more than one template. An obvious example here is something like a sidebar, where
several different page layouts might all include the sidebar. But also, partial templates can return values: this
means they can be used like functions, that can be called by multiple templates to do some common processing.
* `/layouts/shortcodes`: these templates can be called directly from files in `/content`.
* `/static`: static files which don't need preprocessing. JS or CSS files could live here.
* `/themes`: you can use just Hugo or use it with a theme. Themes primarily provide additional templates, which are
supplied in a `/themes/$theme_name/layouts` directory. You can use a theme but customise it by providing your own
versions of any of the theme layouts in the base `/layouts` directory. That is, if a theme provides
`/themes/$theme_name/layouts/sidebar.html` and you provide `/layouts/sidebar.html`, then your version of the
* `/themes`: you can use just Hugo or use it with a theme. Themes primarily provide additional templates, which are
supplied in a `/themes/$theme_name/layouts` directory. You can use a theme but customise it by providing your own
versions of any of the theme layouts in the base `/layouts` directory. That is, if a theme provides
`/themes/$theme_name/layouts/sidebar.html` and you provide `/layouts/sidebar.html`, then your version of the
template will be used.
It also has the following top-level file:
* `config.toml`: site-wide configuration settings. Some of these are built-in and you can add your own. Config settings
* `config.toml`: site-wide configuration settings. Some of these are built-in and you can add your own. Config settings
defined here are available in templates. All these directories above are configurable via `config.toml` settings.
Additionally, the following directories may be of interest:
@ -59,34 +59,40 @@ Additionally, the following directories may be of interest:
Please read [CONTRIBUTING.rst](./CONTRIBUTING.rst) before authoring a change to the spec. Note that spec authoring takes
place after an MSC has been accepted, not as part of a proposal itself.
1. Install the extended version (often the OS default) of Hugo: <https://gohugo.io/getting-started/installing>
1. Install the extended version (often the OS default) of Hugo:
<https://gohugo.io/getting-started/installing>. Note that at least Hugo
v0.74 is required.
Alternatively, use the Docker image at https://hub.docker.com/r/klakegg/hugo/.
2. Run `git submodule update --init --recursive` for good measure.
3. Run `npm i` to install the dependencies. Note that this will require NodeJS to be installed.
4. Run `npm run get-proposals` to seed proposal data. This is merely for populating the content of the "Spec Change Proposals"
page and is not required.
5. Run `hugo serve` to run a local webserver which builds whenever a file change is detected. If watching doesn't appear
to be working for you, try `hugo serve --disableFastRender` instead.
5. Run `hugo serve` (or `docker run --rm -it -v $(pwd):/src -p 1313:1313
klakegg/hugo serve`) to run a local webserver which builds whenever a file
change is detected. If watching doesn't appear to be working for you, try
adding `--disableFastRender` to the commandline.
6. Edit the specification 🙂
We use a highly customized [Docsy](https://www.docsy.dev/) theme for our generated site, which uses Bootstrap and Font
Awesome. If you're looking at making design-related changes to the spec site, please coordinate with us in
We use a highly customized [Docsy](https://www.docsy.dev/) theme for our generated site, which uses Bootstrap and Font
Awesome. If you're looking at making design-related changes to the spec site, please coordinate with us in
[#matrix-docs:matrix.org](https://matrix.to/#/#matrix-docs:matrix.org) before opening a PR.
## Building the specification
If for some reason you're not a CI/CD system and want to render a static version of the spec for yourself, follow the above
steps for authoring changes to the specification and instead of `hugo serve` run `hugo -d "spec"` - this will generate the
spec to `/spec`. If you'd like to serve the spec off a path instead of a domain root (eg: `/unstable`), add `--baseURL "/unstable"`
If for some reason you're not a CI/CD system and want to render a static version of the spec for yourself, follow the above
steps for authoring changes to the specification and instead of `hugo serve` run `hugo -d "spec"` - this will generate the
spec to `/spec`. If you'd like to serve the spec off a path instead of a domain root (eg: `/unstable`), add `--baseURL "/unstable"`
to the `hugo -d "spec"` command.
For building the swagger definitions, create a python3 virtualenv and activate it. Then run `pip install -r ./scripts/requirements.txt`
and finally `python ./scripts/dump-swagger.py` to generate it to `./scripts/swagger/api-docs.json`. To make use of the generated file,
For building the swagger definitions, create a python3 virtualenv and activate it. Then run `pip install -r ./scripts/requirements.txt`
and finally `python ./scripts/dump-swagger.py` to generate it to `./scripts/swagger/api-docs.json`. To make use of the generated file,
there are a number of options:
* It can be uploaded from your filesystem to an online editor/viewer such as [on the swagger website](http://editor.swagger.io/).
* You can run a local HTTP server by running `./scripts/swagger-http-server.py`, and then view the documentation via an
online viewer; for example, at <http://petstore.swagger.io/?url=http://localhost:8000/api-docs.json>.
* You can host the swagger UI yourself. See <https://github.com/swagger-api/swagger-ui#how-to-run> for advice on how to
* You can host the swagger UI yourself. See <https://github.com/swagger-api/swagger-ui#how-to-run> for advice on how to
do so.
## Issue tracking

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

@ -45,9 +45,6 @@ Custom SCSS for the Matrix spec
scroll-behavior: smooth;
overscroll-behavior: contain;
/* This overrides calc(100vh - 10rem);, which gives us a blank space at the bottom of the sidebar */
max-height: calc(100vh - 6rem);
&>.td-sidebar-nav__section {
margin-top: 1rem;
}
@ -92,6 +89,15 @@ Custom SCSS for the Matrix spec
}
}
@media (min-width: 768px) {
@supports (position: sticky) {
.td-sidebar-nav {
/* This overrides calc(100vh - 10rem);, which gives us a blank space at the bottom of the sidebar */
max-height: calc(100vh - 6rem);
}
}
}
/* Customise footer */
footer {
box-shadow: 0px 0px 8px rgba(179, 179, 179, 0.25);

@ -0,0 +1 @@
Update `Access-Control-Allow-Headers` recommendation to fit CORS specification.

@ -0,0 +1 @@
Explicitly state that `replacment_room` is a room ID in `m.room.tombstone` events.

@ -0,0 +1 @@
Clarify that all request bodies are required.

@ -0,0 +1 @@
Add support for knocking, as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).

@ -0,0 +1 @@
Extend `/_matrix/client/r0/login` to accept a `m.login.appservice`, as per [MSC2778](https://github.com/matrix-org/matrix-doc/pull/2778).

@ -0,0 +1 @@
Fix key_backup operation IDs in OpenAPI spec.

@ -0,0 +1 @@
Add auth property to UIA endpoint uploadCrossSigningKeys.

@ -0,0 +1 @@
Clarify that all request bodies are required.

@ -0,0 +1 @@
Disambiguate getEvents and peekEvents, and include both in swagger.

@ -0,0 +1 @@
Mention that a canonical alias event should be added when a room is created with an alias.

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

@ -0,0 +1 @@
Add an 'API conventions' section to the Appendices.

@ -0,0 +1 @@
Clarify the documentation around the pagination tokens used by `/sync`, `/rooms/{room_id}/messages`, `/initialSync`, `/rooms/{room_id}/initialSync`, and `/notifications`.

@ -0,0 +1 @@
Remove the inaccurate 'Pagination' section.

@ -0,0 +1 @@
Correct the `/_matrix/federation/v1/user/devices/{userId}` response which actually returns `"self_signing_key"` instead of `"self_signing_keys"`.

@ -0,0 +1 @@
Explain the reasons why `<hostname>` TLS certificate is needed rather than `<delegated_hostname>` for SRV delegation.

@ -0,0 +1 @@
Tweak the example PDU diagram to better demonstrate situations with multiple `prev_events`.

@ -12,10 +12,6 @@ theme = ["docsy"]
disableKinds = ["taxonomy", "taxonomyTerm"]
# Change the default for assets, because the old Python toolchain uses "assets" for build output.
# When the old toolchain is retired we can switch back to the default here.
assetDir = "assets-hugo"
[languages]
[languages.en]
title = "Matrix Specification"
@ -43,7 +39,13 @@ privacy_policy = "https://matrix.org/legal/privacy-notice"
# must be one of "unstable", "current", "historical"
# this is used to decide whether to show a banner pointing to the current release
status = "unstable"
# A URL pointing to the latest, stable release of the spec. To be shown in the unstable version warning banner.
current_version_url = "https://matrix.org/docs/spec/"
# The following is used when status = "stable", and is displayed in various UI elements on a released version
# of the spec. CI will set these values here automatically when a release git tag (i.e `v1.5`) is created.
#major = "1"
#minor = "0"
#release_date = "April 01, 2021"
# User interface configuration
[params.ui]

@ -41,7 +41,7 @@ browsing the Client-Server API.
{{% boxes/note %}}
As of June 10th 2019, the Matrix specification is considered out of beta
-indicating that all currently released APIs are considered stable and
- indicating that all currently released APIs are considered stable and
secure to the best of our knowledge, and the spec should contain the
complete information necessary to develop production-grade
implementations of Matrix without the need for external reference.
@ -328,7 +328,7 @@ 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
These describe transient 'one-off' activity in a room such as an
instant messages, VoIP call setups, file transfers, etc. They generally
describe communication activity.

@ -47,6 +47,27 @@ When decoding Base64, implementations SHOULD accept input with or
without padding characters wherever possible, to ensure maximum
interoperability.
## Binary data
In some cases it is necessary to encapsulate binary data, for example,
public keys or signatures. Given that JSON cannot safely represent raw
binary data, all binary values should be encoded and represented in
JSON as unpadded Base64 strings as described above.
In cases where the Matrix specification refers to either opaque byte
or opaque Base64 values, the value is considered to be opaque AFTER
Base64 decoding, rather than the encoded representation itself.
It is safe for a client or homeserver implementation to check for
correctness of a Base64-encoded value at any point, and to altogether
reject a value which is not encoded properly. However, this is optional
and is considered to be an implementation detail.
Special consideration is given for future protocol transformations,
such as those which do not use JSON, where Base64 encoding may not be
necessary in order to represent a binary value safely. In these cases,
Base64 encoding of binary values may be skipped altogether.
## Signing JSON
Various points in the Matrix specification require JSON objects to be
@ -1039,3 +1060,48 @@ The event signing algorithm should emit the following signed event:
}
}
```
## Conventions for Matrix APIs
This section is intended primarily to guide API designers when adding to Matrix,
setting guidelines to follow for how those APIs should work. This is important to
maintain consistency with the Matrix protocol, and thus improve developer
experience.
### HTTP endpoint and JSON property naming
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.
{{% boxes/note %}}
There are a few historical exceptions to this rule, such as `/createRoom`.
These inconsistencies may be addressed in future versions of this specification.
{{% /boxes/note %}}
### Pagination
REST API endpoints which can return multiple "pages" of results should adopt the
following conventions.
* If more results are available, the endpoint should return a property named
`next_batch`. The value should be a string token which can be passed into
a subsequent call to the endpoint to retrieve the next page of results.
If no more results are available, this is indicated by *omitting* the
`next_batch` property from the results.
* The endpoint should accept a query-parameter named `from` which the client
is expected to set to the value of a previous `next_batch`.
* Some endpoints might support pagination in two directions (example:
`/messages`, which can be used to move forward or backwards in the timeline
from a known point). In this case, the endpoint should return a `prev_batch`
property which can be passed into `from` to receive the previous page of
results.
Avoid having a separate "direction" parameter, which is generally redundant:
the tokens returned by `next_batch` and `prev_batch` should contain enough
information for subsequent calls to the API to know which page of results
they should return.

@ -312,10 +312,11 @@ information.
The homeserver needs to give the application service *full control* over
its namespace, both for users and for room aliases. This means that the
AS should be able to create/edit/delete any room alias in its namespace,
as well as create/delete any user in its namespace. No additional API
AS should be able to manage any users and room alias in its namespace. No additional API
changes need to be made in order for control of room aliases to be
granted to the AS. Creation of users needs API changes in order to:
granted to the AS.
Creation of users needs API changes in order to:
- Work around captchas.
- Have a 'passwordless' user.
@ -334,8 +335,26 @@ user ID without a password.
username: "_irc_example"
}
Similarly, logging in as users needs API changes in order to allow the AS to
log in without needing the user's password. This is achieved by including the
`as_token` on a `/login` request, along with a login type of
`m.login.application_service`.
POST /_matrix/client/%CLIENT_MAJOR_VERSION%/login
Authorization: Bearer YourApplicationServiceTokenHere
Content:
{
type: "m.login.application_service",
"identifier": {
"type": "m.id.user",
"user": "_irc_example"
}
}
Application services which attempt to create users or aliases *outside*
of their defined namespaces will receive an error code `M_EXCLUSIVE`.
of their defined namespaces, or log in as users outside of their defined
namespaces will receive an error code `M_EXCLUSIVE`.
Similarly, normal users who attempt to create users or aliases *inside*
an application service-defined namespace will receive the same
`M_EXCLUSIVE` error code, but only if the application service has

@ -4,7 +4,7 @@ weight: 10
type: docs
---
The client-server API provides a simple lightweight API to let clients
The client-server API allows clients to
send messages, control rooms and synchronise conversation history. It is
designed to support both lightweight clients which store no state and
lazy-load data from the server as required - as well as heavyweight
@ -20,20 +20,15 @@ supported as optional extensions - e.g. a packed binary encoding over
stream-cipher encrypted TCP socket for low-bandwidth/low-roundtrip
mobile usage. For the default HTTP transport, all API calls use a
Content-Type of `application/json`. In addition, all strings MUST be
encoded as UTF-8. Clients are authenticated using opaque `access_token`
strings (see [Client Authentication](#client-authentication) for
details), passed as a query string parameter on all requests.
encoded as UTF-8.
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.
Clients are authenticated using opaque `access_token` strings (see [Client
Authentication](#client-authentication) for details).
{{% boxes/note %}}
There are a few historical exceptions to this rule, such as
`/createRoom`. A future version of this specification will address the
inconsistency.
{{% /boxes/note %}}
See also [Conventions for Matrix APIs](/appendices#conventions-for-matrix-apis)
in the Appendices for conventions which all Matrix APIs are expected to follow.
### Standard error response
Any errors which occur at the Matrix API level MUST return a "standard
error response". This is a JSON object which looks like:
@ -46,15 +41,17 @@ error response". This is a JSON object which looks like:
```
The `error` string will be a human-readable error message, usually a
sentence explaining what went wrong. The `errcode` string will be a
unique string which can be used to handle an error message e.g.
`M_FORBIDDEN`. These error codes should have their namespace first in
ALL CAPS, followed by a single \_ to ease separating the namespace from
the error code. For example, if there was a custom namespace
`com.mydomain.here`, and a `FORBIDDEN` code, the error code should look
like `COM.MYDOMAIN.HERE_FORBIDDEN`. There may be additional keys
depending on the error, but the keys `error` and `errcode` MUST always
be present.
sentence explaining what went wrong.
The `errcode` string will be a unique string which can be used to handle an
error message e.g. `M_FORBIDDEN`. Error codes should have their namespace
first in ALL CAPS, followed by a single `_`. For example, if there was a custom
namespace `com.mydomain.here`, and a `FORBIDDEN` code, the error code should
look like `COM.MYDOMAIN.HERE_FORBIDDEN`. Error codes defined by this
specification should start `M_`.
Some `errcode`s define additional keys which should be present in the error
response object, but the keys `error` and `errcode` MUST always be present.
Errors are generally best expressed by their error code rather than the
HTTP status code returned. When encountering the error code `M_UNKNOWN`,
@ -66,7 +63,9 @@ found. However, if the client were to receive an error code of
`M_UNKNOWN` with a 400 Bad Request, the client should assume that the
request being made was invalid.
The common error codes are:
#### Common error codes
These error codes can be returned by any API endpoint:
`M_FORBIDDEN`
Forbidden access, e.g. joining a room without permission, failed login.
@ -98,7 +97,11 @@ then try again.
`M_UNKNOWN`
An unknown error has occurred.
Other error codes the client might encounter are:
#### Other error codes
The following error codes are specific to certain endpoints.
<!-- TODO: move them to the endpoints that return them -->.
`M_UNRECOGNIZED`
The server did not understand the request.
@ -226,7 +229,7 @@ headers to be returned by servers on all requests are:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
Access-Control-Allow-Headers: X-Requested-With, Content-Type, Authorization
## Server Discovery
@ -1021,6 +1024,41 @@ client supports it, the client should redirect the user to the
is complete, the client will need to submit a `/login` request matching
`m.login.token`.
#### Appservice Login
An appservice can log in by providing a valid appservice token and a user within the appservice's
namespace.
{{% boxes/note %}}
Appservices do not need to log in as individual users in all cases, as they
can perform [Identity Assertion](/application-service-api#identity-assertion)
using the appservice token. However, if the appservice needs a scoped token
for a single user then they can use this API instead.
{{% /boxes/note %}}
This request must be authenticated by the [appservice `as_token`](/application-service-api#registration)
(see [Client Authentication](#client-authentication) on how to provide the token).
To use this login type, clients should submit a `/login` request as follows:
```json
{
"type": "m.login.appservice",
"identifier": {
"type": "m.id.user",
"user": "<user_id or user localpart>"
}
}
```
If the access token is not valid, does not correspond to an appservice
or the user has not previously been registered then the homeserver will
respond with an errcode of `M_FORBIDDEN`.
If the access token does correspond to an appservice, but the user id does
not lie within its namespace then the homeserver will respond with an
errcode of `M_EXCLUSIVE`.
{{% http-api spec="client-server" api="login" %}}
{{% http-api spec="client-server" api="logout" %}}
@ -1211,88 +1249,6 @@ using an `unstable` version.
When this capability is not listed, clients should use `"1"` as the
default and only stable `available` room version.
## Pagination
{{% boxes/note %}}
The paths referred to in this section are not actual endpoints. They
only serve as examples to explain how pagination functions.
{{% /boxes/note %}}
Pagination is the process of dividing a dataset into multiple discrete
pages. Matrix makes use of pagination to allow clients to view extremely
large datasets. These datasets are not limited to events in a room (for
example clients may want to paginate a list of rooms in addition to
events within those rooms). Regardless of what is being paginated, there
is a common approach which is used to give clients an easy way of
selecting subsets of a potentially changing dataset. Each endpoint that
uses pagination may use different parameters. However the theme among
them is that they take a `from` and `to` token, and occasionally a
`limit` and `dir`. Together, these parameters describe the position in a
data set, where `from` and `to` are known as "stream tokens" matching
the regular expression `[a-zA-Z0-9.=_-]+`. If supported, the `dir`
defines the direction of events to return: either forwards (`f`) or
backwards (`b`). The response may contain tokens that can be used for
retrieving results before or after the returned set. These tokens may be
called <span class="title-ref">start</span> or <span
class="title-ref">prev\_batch</span> for retrieving the previous result
set, or <span class="title-ref">end</span>, <span
class="title-ref">next\_batch</span> or <span
class="title-ref">next\_token</span> for retrieving the next result set.
In the following examples, 'START' and 'END' are placeholders to signify
the start and end of the data sets respectively.
For example, if an endpoint had events E1 -&gt; E15. The client wants
the last 5 events and doesn't know any previous events:
```
S E
|-E1-E2-E3-E4-E5-E6-E7-E8-E9-E10-E11-E12-E13-E14-E15-|
| | |
| _____| <--backwards-- |
|__________________ | | ________|
| | | |
GET /somepath?to=START&limit=5&dir=b&from=END
Returns:
E15,E14,E13,E12,E11
```
Another example: a public room list has rooms R1 -&gt; R17. The client
is showing 5 rooms at a time on screen, and is on page 2. They want to
now show page 3 (rooms R11 -&gt; 15):
```
S E
| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | stream token
|-R1-R2-R3-R4-R5-R6-R7-R8-R9-R10-R11-R12-R13-R14-R15-R16-R17| room
|____________| |________________|
| |
Currently |
viewing |
|
GET /roomslist?from=9&to=END&limit=5
Returns: R11,R12,R13,R14,R15
```
Note that tokens are treated in an *exclusive*, not inclusive, manner.
The end token from the initial request was '9' which corresponded to
R10. When the 2nd request was made, R10 did not appear again, even
though from=9 was specified. If you know the token, you already have the
data.
Responses for pagination-capable endpoints SHOULD have a `chunk` array
alongside the applicable stream tokens to represent the result set.
In general, when the end of a result set is reached the applicable
stream token will be excluded from the response. For example, if a user
was backwards-paginating events in a room they'd eventually reach the
first event in the room. In this scenario, the `prev_batch` token would
be excluded from the response. Some paginated endpoints are open-ended
in one direction, such as endpoints which expose an event stream for an
active room. In this case, it is not possible for the client to reach
the true "end" of the data set and therefore should always be presented
with a token to keep moving forwards.
## Filtering
Filters can be created on the server and can be passed as a parameter to

@ -134,6 +134,15 @@ to send. The process overall is as follows:
8448 and a `Host` header containing the `<hostname>`. The target
server must present a valid certificate for `<hostname>`.
{{% boxes/note %}}
The reasons we require `<hostname>` rather than `<delegated_hostname>` for SRV
delegation are:
1. DNS is insecure (not all domains have DNSSEC), so the target of the delegation
must prove that it is a valid delegate for `<hostname>` via TLS.
2. Consistency with the recommendations in [RFC6125](https://datatracker.ietf.org/doc/html/rfc6125#section-6.2.1)
and other applications using SRV records such [XMPP](https://datatracker.ietf.org/doc/html/rfc6120#section-13.7.2.1).
{{% /boxes/note %}}
The TLS certificate provided by the target server must be signed by a
known Certificate Authority. Servers are ultimately responsible for
determining the trusted Certificate Authorities, however are strongly
@ -324,15 +333,16 @@ after all other known events.
For example, consider a room whose events form the DAG shown below. A
server creating a new event in this room should populate the new event's
`prev_events` field with `E4` and `E5`, since neither event yet has a
child:
`prev_events` field with both `E4` and `E6`, since neither event yet has
a child:
E1
^
|
+-> E2 <-+
| |
E3 E5
E2 <--- E5
^ ^
| |
E3 E6
^
|
E4

@ -87,7 +87,7 @@ paths:
associated the third party identifier with the user.
required: ['medium', 'address', 'validated_at', 'added_at']
tags:
- User data
- Account management
post:
summary: Adds contact information to the user's account.
description: |-
@ -154,6 +154,7 @@ paths:
properties:
submit_url:
type: string
format: uri
description: |-
An optional field containing a URL where the client must
submit the validation token to, with identical parameters
@ -178,7 +179,7 @@ paths:
schema:
"$ref": "definitions/errors/error.yaml"
tags:
- User data
- Account management
"/account/3pid/add":
post:
summary: Adds contact information to the user's account.
@ -234,7 +235,7 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- Account management
"/account/3pid/bind":
post:
summary: Binds a 3PID to the user's account through an Identity Service.
@ -288,7 +289,7 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- Account management
"/account/3pid/delete":
post:
summary: Deletes a third party identifier from the user's account
@ -345,7 +346,7 @@ paths:
description: |-
An indicator as to whether or not the homeserver was able to unbind
the 3PID from the identity server. `success` indicates that the
indentity server has unbound the identifier whereas `no-support`
identity server has unbound the identifier whereas `no-support`
indicates that the identity server refuses to support the request
or the homeserver was not able to determine an identity server to
unbind from.
@ -353,7 +354,7 @@ paths:
required:
- id_server_unbind_result
tags:
- User data
- Account management
"/account/3pid/unbind":
post:
summary: Removes a user's third party identifier from an identity server.
@ -417,7 +418,7 @@ paths:
required:
- id_server_unbind_result
tags:
- User data
- Account management
"/account/3pid/email/requestToken":
post:
summary: Begins the validation process for an email address for association with the user's account.
@ -469,6 +470,8 @@ paths:
"errcode": "M_THREEPID_IN_USE",
"error": "Third party identifier already in use"
}
tags:
- Account management
"/account/3pid/msisdn/requestToken":
post:
summary: Begins the validation process for a phone number for association with the user's account.
@ -517,3 +520,5 @@ paths:
"errcode": "M_THREEPID_IN_USE",
"error": "Third party identifier already in use"
}
tags:
- Account management

@ -66,6 +66,7 @@ paths:
properties:
content_uri:
type: string
format: uri
description: "The [MXC URI](/client-server-api/#matrix-content-mxc-uris) to the uploaded content."
examples:
application/json: {
@ -360,6 +361,7 @@ paths:
parameters:
- in: query
type: string
format: uri
x-example: "https://matrix.org"
name: url
description: "The URL to get a preview of."
@ -389,6 +391,7 @@ paths:
The byte-size of the image. Omitted if there is no image attached.
"og:image":
type: string
format: uri
description: |-
An [MXC URI](/client-server-api/#matrix-content-mxc-uris) to the image. Omitted if there is no image.
examples:

@ -48,16 +48,18 @@ paths:
(and not other members) permission to send state events. Overridden
by the `power_level_content_override` parameter.
4. Events set by the `preset`. Currently these are the `m.room.join_rules`,
4. An `m.room.canonical_alias` event if `room_alias_name` is given.
5. Events set by the `preset`. Currently these are the `m.room.join_rules`,
`m.room.history_visibility`, and `m.room.guest_access` state events.
5. Events listed in `initial_state`, in the order that they are
6. Events listed in `initial_state`, in the order that they are
listed.
6. Events implied by `name` and `topic` (`m.room.name` and `m.room.topic`
7. Events implied by `name` and `topic` (`m.room.name` and `m.room.topic`
state events).
7. Invite events implied by `invite` and `invite_3pid` (`m.room.member` with
8. Invite events implied by `invite` and `invite_3pid` (`m.room.member` with
`membership: invite` and `m.room.third_party_invite`).
The available presets do the following with respect to room state:
@ -112,7 +114,8 @@ paths:
would be `#foo:example.com`.
The complete room alias will become the canonical alias for
the room.
the room and an `m.room.canonical_alias` event will be sent
into the room.
name:
type: string
description: |-
@ -171,7 +174,7 @@ paths:
type: object
description: |-
Extra keys, such as `m.federate`, to be added to the content
of the [`m.room.create`](client-server-api/#mroomcreate) event. The server will clobber the following
of the [`m.room.create`](/client-server-api/#mroomcreate) event. The server will clobber the following
keys: `creator`, `room_version`. Future versions of the specification
may allow the server to clobber other keys.
initial_state:
@ -221,7 +224,7 @@ paths:
description: |-
The power level content to override in the default power level
event. This object is applied on top of the generated
[`m.room.power_levels`](client-server-api/#mroompower_levels)
[`m.room.power_levels`](/client-server-api/#mroompower_levels)
event content prior to it being sent to the room. Defaults to
overriding nothing.
responses:

@ -42,6 +42,7 @@ paths:
name: keys
description: |-
The keys to be published.
required: true
schema:
type: object
properties:
@ -66,6 +67,12 @@ paths:
request.
allOf:
- $ref: definitions/cross_signing_key.yaml
auth:
description: |-
Additional authentication information for the
user-interactive authentication API.
allOf:
- $ref: "definitions/auth_data.yaml"
example: {
"master_key": {
"user_id": "@alice:example.com",
@ -129,6 +136,8 @@ paths:
"errcode": "M_FORBIDDEN",
"error": "Key ID in use"
}
tags:
- End-to-end encryption
"/keys/signatures/upload":
post:
summary: Upload cross-signing signatures.
@ -143,6 +152,7 @@ paths:
name: signatures
description: |-
The signatures to be published.
required: true
schema:
type: object
title: Signatures
@ -222,3 +232,5 @@ paths:
}
}
}
tags:
- End-to-end encryption

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: Error
description: A Matrix-level Error
properties:
errcode:
@ -22,4 +23,4 @@ properties:
type: string
description: A human-readable error message.
example: An unknown error occurred
required: ["errcode"]
required: ["errcode"]

@ -13,6 +13,7 @@
# limitations under the License.
$ref: error.yaml
type: object
title: RateLimitError
description: The rate limit was reached for this request
properties:
errcode:
@ -29,4 +30,4 @@ properties:
The amount of time in milliseconds the client should wait
before trying the request again.
example: 2000
required: ["errcode"]
required: ["errcode"]

@ -21,3 +21,4 @@ properties:
type: object
type: array
type: object
title: EventBatch

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: OpenIdCredentials
properties:
access_token:
type: string

@ -68,6 +68,7 @@ properties:
rules like any other user.
avatar_url:
type: string
format: uri
description: The URL for the room's avatar, if one is set.
join_rule:
type: string
@ -110,4 +111,4 @@ example: {
"next_batch": "p190q",
"prev_batch": "p1902",
"total_room_count_estimate": 115
}
}

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: RequestTokenResponse
properties:
sid:
type: string
@ -22,6 +23,7 @@ properties:
example: "123abc"
submit_url:
type: string
format: uri
description: |-
An optional field containing a URL where the client must submit the
validation token to, with identical parameters to the Identity Service

@ -25,3 +25,4 @@ properties:
- origin_server_ts
type: array
type: object
title: RoomEventBatch

@ -26,3 +26,4 @@ properties:
- state_key
type: array
type: object
title: StateEventBatch

@ -21,6 +21,11 @@ properties:
type: boolean
prev_batch:
description: A token that can be supplied to the `from` parameter of the
rooms/{roomId}/messages endpoint.
[`/rooms/<room_id>/messages`](#get_matrixclientr0roomsroomidmessages)
endpoint in order to retrieve earlier events.
If no earlier events are available, this property may be omitted from
the response.
type: string
type: object
title: TimelineBatch

@ -18,6 +18,7 @@ type: object
properties:
base_url:
type: string
format: uri
description: The base URL for the homeserver for client-server connections.
example: https://matrix.example.com
required:

@ -18,6 +18,7 @@ type: object
properties:
base_url:
type: string
format: uri
description: The base URL for the identity server for client-server connections.
example: https://identity.example.com
required:

@ -375,7 +375,7 @@ paths:
summary: Store a key in the backup.
description: |-
Store a key in the backup.
operationId: postRoomKeysKeyRoomIdSessionId
operationId: putRoomKeysBySessionId
security:
- accessToken: []
parameters:
@ -409,6 +409,7 @@ paths:
description: The update succeeded.
schema:
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
@ -443,10 +444,10 @@ paths:
tags:
- End-to-end encryption
get:
summary: Retrieve a key from the backup
summary: Retrieve a key from the backup.
description: |-
Retrieve a key from the backup.
operationId: getRoomKeysKeyRoomIdSessionId
operationId: getRoomKeysBySessionId
security:
- accessToken: []
parameters:
@ -487,11 +488,13 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
delete:
summary: Delete a key from the backup
summary: Delete a key from the backup.
description: |-
Delete a key from the backup.
operationId: deleteRoomKeysKeyRoomIdSessionId
operationId: deleteRoomKeysBySessionId
security:
- accessToken: []
parameters:
@ -519,6 +522,7 @@ paths:
description: The update succeeded
schema:
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
@ -547,12 +551,14 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
"/room_keys/keys/{roomId}":
put:
summary: Store several keys in the backup for a given room.
description: |-
Store a key in the backup.
operationId: postRoomKeysKeyRoomId
Store several keys in the backup for a given room.
operationId: putRoomKeysByRoomId
security:
- accessToken: []
parameters:
@ -580,6 +586,7 @@ paths:
description: The update succeeded
schema:
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
@ -624,10 +631,10 @@ paths:
tags:
- End-to-end encryption
get:
summary: Retrieve the keys from the backup for a given room
summary: Retrieve the keys from the backup for a given room.
description: |-
Retrieve the keys from the backup for a given room
operationId: getRoomKeysKeyRoomId
Retrieve the keys from the backup for a given room.
operationId: getRoomKeysByRoomId
security:
- accessToken: []
parameters:
@ -666,11 +673,13 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
delete:
summary: Delete a key from the backup
summary: Delete the keys from the backup for a given room.
description: |-
Delete a key from the backup.
operationId: deleteRoomKeysKeyRoomId
Delete the keys from the backup for a given room.
operationId: deleteRoomKeysByRoomId
security:
- accessToken: []
parameters:
@ -692,6 +701,7 @@ paths:
description: The update succeeded
schema:
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
@ -720,12 +730,14 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
"/room_keys/keys":
put:
summary: Store several keys in the backup.
description: |-
Store several keys in the backup.
operationId: postRoomKeysKey
operationId: putRoomKeys
security:
- accessToken: []
parameters:
@ -773,6 +785,7 @@ paths:
description: The update succeeded
schema:
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
@ -817,10 +830,10 @@ paths:
tags:
- End-to-end encryption
get:
summary: Retrieve the keys from the backup for a given room
summary: Retrieve the keys from the backup.
description: |-
Retrieve the keys from the backup for a given room
operationId: getRoomKeysKeyRoomId
Retrieve the keys from the backup.
operationId: getRoomKeys
security:
- accessToken: []
parameters:
@ -875,11 +888,13 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption
delete:
summary: Delete a key from the backup
summary: Delete the keys from the backup.
description: |-
Delete a key from the backup.
operationId: deleteRoomKeysKeyRoomId
Delete the keys from the backup.
operationId: deleteRoomKeys
security:
- accessToken: []
parameters:
@ -895,6 +910,7 @@ paths:
description: The update succeeded
schema:
type: object
title: RoomKeysUpdateResponse
properties:
etag:
description: |-
@ -923,3 +939,5 @@ paths:
description: This request was rate-limited.
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- End-to-end encryption

@ -61,6 +61,8 @@ paths:
}
schema:
"$ref": "definitions/errors/error.yaml"
tags:
- Room discovery
put:
summary: Sets the visibility of a room in the room directory
description: |-
@ -111,6 +113,8 @@ paths:
}
schema:
"$ref": "definitions/errors/error.yaml"
tags:
- Room discovery
"/publicRooms":
get:
summary: Lists the public rooms on the server.

@ -51,9 +51,12 @@ paths:
name: from
description: |-
The token to start returning events from. This token can be obtained
from a `prev_batch` token returned for each room by the sync API,
or from a `start` or `end` token returned by a previous request
to this endpoint.
from a `prev_batch` or `next_batch` token returned by the `/sync` endpoint,
or from an `end` token returned by a previous request to this endpoint.
This endpoint can also accept a value returned as a `start` token
by a previous request to this endpoint, though servers are not
required to support this. Clients should not rely on the behaviour.
required: true
x-example: "s345_678_333"
- in: query
@ -61,16 +64,18 @@ paths:
name: to
description: |-
The token to stop returning events at. This token can be obtained from
a `prev_batch` token returned for each room by the sync endpoint,
or from a `start` or `end` token returned by a previous request to
this endpoint.
a `prev_batch` or `next_batch` token returned by the `/sync` endpoint,
or from an `end` token returned by a previous request to this endpoint.
required: false
- in: query
type: string
enum: ["b", "f"]
name: dir
description: |-
The direction to return events from.
The direction to return events from. If this is set to `f`, events
will be returned in chronological order starting at `from`. If it
is set to `b`, events will be returned in *reverse* chronolgical
order, again starting at `from`.
required: true
x-example: "b"
- in: query
@ -96,20 +101,29 @@ paths:
start:
type: string
description: |-
The token the pagination starts from. If `dir=b` this will be
the token supplied in `from`.
A token corresponding to the start of `chunk`. This will be the same as
the value given in `from`.
end:
type: string
description: |-
The token the pagination ends at. If `dir=b` this token should
be used again to request even earlier events.
A token corresponding to the end of `chunk`. This token can be passed
back to this endpoint to request further events.
If no further events are available (either because we have
reached the start of the timeline, or because the user does
not have permission to see any more events), this property
is omitted from the response.
chunk:
type: array
description: |-
A list of room events. The order depends on the `dir` parameter.
For `dir=b` events will be in reverse-chronological order,
for `dir=f` in chronological order, so that events start
at the `from` point.
for `dir=f` in chronological order. (The exact definition of `chronological`
is dependent on the server implementation.)
Note that an empty `chunk` does not *necessarily* imply that no more events
are available. Clients should continue to paginate until no `end` property
is returned.
items:
"$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
state:
@ -125,6 +139,7 @@ paths:
the membership of those members has not changed.
items:
$ref: "../../event-schemas/schema/core-event-schema/state_event.yaml"
required: [start, chunk]
examples:
application/json: {
"start": "t47429-4392820_219380_26003_2265",

@ -41,7 +41,9 @@ paths:
- in: query
type: string
name: from
description: Pagination token given to retrieve the next set of events.
description: |-
Pagination token to continue from. This should be the `next_token`
returned from an earlier call to this endpoint.
required: false
x-example: "xxxxx"
- in: query

@ -36,7 +36,7 @@ paths:
This endpoint was deprecated in r0 of this specification. Clients
should instead call the [`/sync`](/client-server-api/#get_matrixclientr0sync)
API with a `since` parameter. See
endpoint with a `since` parameter. See
the [migration guide](https://matrix.org/docs/guides/migrating-from-client-server-api-v-1#deprecated-endpoints).
operationId: getEvents
security:
@ -73,12 +73,12 @@ paths:
start:
type: string
description: |-
A token which correlates to the first value in `chunk`. This
A token which correlates to the start of `chunk`. This
is usually the same token supplied to `from=`.
end:
type: string
description: |-
A token which correlates to the last value in `chunk`. This
A token which correlates to the end of `chunk`. This
token should be used in the next request to `/events`.
chunk:
type: array
@ -102,7 +102,7 @@ paths:
This endpoint was deprecated in r0 of this specification. Clients
should instead call the [`/sync`](/client-server-api/#get_matrixclientr0sync)
API with no `since` parameter. See
endpoint with no `since` parameter. See
the [migration guide](https://matrix.org/docs/guides/migrating-from-client-server-api-v-1#deprecated-endpoints).
operationId: initialSync
security:
@ -199,8 +199,8 @@ paths:
end:
type: string
description: |-
A token which correlates to the last value in `chunk`. This
token should be used with the `/events` API to listen for new
A token which correlates to the end of the timelines returned. This
token should be used with the `/events` endpoint to listen for new
events.
presence:
type: array
@ -237,13 +237,20 @@ paths:
start:
type: string
description: |-
A token which correlates to the first value in `chunk`.
Used for pagination.
A token which correlates to the start of `chunk`.
Can be passed to
[`/rooms/<room_id>/messages`](#get_matrixclientr0roomsroomidmessages)
to retrieve earlier events.
If no earlier events are available, this property may be omitted from
the response.
end:
type: string
description: |-
A token which correlates to the last value in `chunk`.
Used for pagination.
A token which correlates to the end of `chunk`.
Can be passed to
[`/rooms/<room_id>/messages`](#get_matrixclientr0roomsroomidmessages)
to retrieve later events.
chunk:
type: array
description: |-
@ -257,7 +264,7 @@ paths:
title: RoomEvent
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
required: ["start", "end", "chunk"]
required: ["end", "chunk"]
state:
type: array
description: |-

@ -27,9 +27,11 @@ produces:
securityDefinitions:
$ref: definitions/security.yaml
paths:
"/events":
# With an extra " " to disambiguate from the getEvents endpoint
# The extra space makes it sort first for what I'm sure is a good reason.
"/events ":
get:
summary: Listen on the event stream.
summary: Listen on the event stream of a particular room.
description: |-
This will listen for new events related to a particular room and return
them to the caller. This will block until an event is received, or until
@ -103,4 +105,5 @@ paths:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
400:
description: "Bad pagination `from` parameter."
# No tags to exclude this from the swagger UI - use the normal version instead.
tags:
- Room participation

@ -129,6 +129,7 @@ paths:
properties:
avatar_url:
type: string
format: uri
description: The new avatar URL for this user.
responses:
200:
@ -170,6 +171,7 @@ paths:
properties:
avatar_url:
type: string
format: uri
description: The user's avatar URL if they have set one, otherwise not present.
404:
description: There is no avatar URL for this user or this user does not exist.
@ -204,6 +206,7 @@ paths:
properties:
avatar_url:
type: string
format: uri
description: The user's avatar URL if they have set one, otherwise not present.
displayname:
type: string

@ -113,6 +113,7 @@ paths:
properties:
url:
type: string
format: uri
description: |-
Required if `kind` is `http`. The URL to use to send
notifications to.
@ -220,6 +221,7 @@ paths:
properties:
url:
type: string
format: uri
description: |-
Required if `kind` is `http`. The URL to use to send
notifications to. MUST be an HTTPS URL with a path of

@ -61,6 +61,7 @@ paths:
description: |-
Extra receipt information to attach to `content` if any. The
server will automatically set the `ts` field.
required: true
schema:
type: object
example: {

@ -214,7 +214,7 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- Account management
"/register/email/requestToken":
post:
summary: Begins the validation process for an email to be used during registration.
@ -265,6 +265,8 @@ paths:
}
schema:
"$ref": "definitions/errors/error.yaml"
tags:
- Account management
"/register/msisdn/requestToken":
post:
summary: Requests a validation token be sent to the given phone number for the purpose of registering an account
@ -315,6 +317,8 @@ paths:
}
schema:
"$ref": "definitions/errors/error.yaml"
tags:
- Account management
"/account/password":
post:
summary: "Changes a user's password."
@ -378,7 +382,7 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- Account management
"/account/password/email/requestToken":
post:
summary: Requests a validation token be sent to the given email address for the purpose of resetting a user's password
@ -434,6 +438,8 @@ paths:
"errcode": "M_THREEPID_NOT_FOUND",
"error": "Email not found"
}
tags:
- Account management
"/account/password/msisdn/requestToken":
post:
summary: Requests a validation token be sent to the given phone number for the purpose of resetting a user's password.
@ -489,6 +495,8 @@ paths:
"errcode": "M_THREEPID_NOT_FOUND",
"error": "Phone number not found"
}
tags:
- Account management
"/account/deactivate":
post:
summary: "Deactivate a user's account."
@ -565,7 +573,7 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- Account management
"/register/available":
get:
summary: Checks to see if a username is available on the server.
@ -627,4 +635,4 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- Account management

@ -49,6 +49,7 @@ paths:
x-example: "$something:example.org"
- in: body
name: body
required: true
schema:
type: object
example: {

@ -98,13 +98,18 @@ paths:
start:
type: string
description: |-
A token which correlates to the first value in `chunk`.
Used for pagination.
A token which correlates to the start of `chunk`. Can be passed to
[`/rooms/<room_id>/messages`](#get_matrixclientr0roomsroomidmessages)
to retrieve earlier events.
If no earlier events are available, this property may be omitted from
the response.
end:
type: string
description: |-
A token which correlates to the last value in `chunk`.
Used for pagination.
A token which correlates to the end of `chunk`. Can be passed to
[`/rooms/<room_id>/messages`](#get_matrixclientr0roomsroomidmessages)
to retrieve later events.
chunk:
type: array
description: |-
@ -118,7 +123,7 @@ paths:
title: RoomEvent
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
required: ["start", "end", "chunk"]
required: ["end", "chunk"]
state:
type: array
description: |-

@ -65,6 +65,7 @@ paths:
x-example: "35"
- in: body
name: body
required: true
schema:
type: object
example: {

@ -74,6 +74,7 @@ paths:
x-example: "@alice:example.com"
- in: body
name: body
required: true
schema:
type: object
example: {

@ -206,6 +206,7 @@ paths:
enum:
- join
- invite
- knock
- leave
- ban
description: |-
@ -220,6 +221,7 @@ paths:
enum:
- join
- invite
- knock
- leave
- ban
description: |-
@ -301,6 +303,7 @@ paths:
description: The display name of the user this object is representing.
avatar_url:
type: string
format: uri
description: The mxc avatar url of the user this object is representing.
description: A map from user ID to a RoomMember object.
type: object

@ -238,6 +238,7 @@ paths:
title: Display name
avatar_url:
type: string
format: uri
title: Avatar Url
events_before:
type: array

@ -46,6 +46,8 @@ paths:
headers:
Location:
type: "string"
tags:
- Session management
"/login/sso/redirect/{idpId}":
get:
summary: Redirect the user's browser to the SSO interface for an IdP.
@ -56,7 +58,7 @@ paths:
The server MUST respond with an HTTP redirect to the SSO interface
for that IdP.
operationId: redirectToSSO
operationId: redirectToIdP
parameters:
- in: path
type: string
@ -83,3 +85,5 @@ paths:
The IdP ID was not recognized by the server. The server is encouraged
to provide a user-friendly page explaining the error given the user
will be navigated to it.
tags:
- Session management

@ -73,7 +73,8 @@ paths:
name: since
type: string
description: |-
A point in time to continue a sync from.
A point in time to continue a sync from. This should be the
`next_batch` token returned by an earlier call to this endpoint.
x-example: "s72594_4483_1934"
- in: query
name: full_state

@ -42,6 +42,8 @@ paths:
description: The protocols supported by the homeserver.
schema:
$ref: ../application-service/definitions/protocol_metadata.yaml
tags:
- Third Party Lookup
"/thirdparty/protocol/{protocol}":
get:
summary: Retrieve metadata about a specific protocol that the homeserver supports.
@ -71,6 +73,8 @@ paths:
}
schema:
$ref: definitions/errors/error.yaml
tags:
- Third Party Lookup
"/thirdparty/location/{protocol}":
get:
summary: Retrieve Matrix-side portals rooms leading to a third party location.
@ -112,6 +116,8 @@ paths:
}
schema:
$ref: definitions/errors/error.yaml
tags:
- Third Party Lookup
"/thirdparty/user/{protocol}":
get:
summary: Retrieve the Matrix User ID of a corresponding third party user.
@ -147,6 +153,8 @@ paths:
}
schema:
$ref: definitions/errors/error.yaml
tags:
- Third Party Lookup
"/thirdparty/location":
get:
summary: Reverse-lookup third party locations given a Matrix room alias.
@ -177,6 +185,8 @@ paths:
}
schema:
$ref: definitions/errors/error.yaml
tags:
- Third Party Lookup
"/thirdparty/user":
get:
summary: Reverse-lookup third party users given a Matrix User ID.
@ -206,3 +216,5 @@ paths:
}
schema:
$ref: definitions/errors/error.yaml
tags:
- Third Party Lookup

@ -47,6 +47,7 @@ paths:
parameters:
- in: body
name: body
required: true
schema:
type: object
properties:
@ -95,6 +96,7 @@ paths:
description: The display name of the user, if one exists.
avatar_url:
type: string
format: uri
example: "mxc://bar.com/foo"
description: The avatar url, as an MXC, if one exists.
limited:
@ -105,4 +107,4 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- User directory

@ -89,4 +89,4 @@ paths:
schema:
"$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
- Session management

@ -96,7 +96,7 @@ paths:
"ed25519:base64+master+public+key": "base64+master+public+key",
}
}
self_signing_keys:
self_signing_key:
type: object
description: |-
The user\'s self-signing key.

@ -45,6 +45,7 @@ properties:
avatar_url:
description: 'The avatar URL for this user, if any.'
type: string
format: uri
displayname:
description: 'The display name for this user, if any.'
type:

@ -10,7 +10,7 @@ properties:
description: A server-defined message.
replacement_room:
type: string
description: The new room the client should be visiting.
description: The room ID of the new room the client should be visiting.
required:
- replacement_room
- body

@ -45,8 +45,10 @@
{{ $status := .Site.Params.version.status }}
{{ if ne $status "unstable"}}
{{ $ret = .Site.Params.version.number }}
{{ $ret = delimit (slice "version" $ret) " " }}
{{ $path := path.Join "changelogs" }}
{{/* produces a string similar to "version v1.5" */}}
{{ $ret = delimit (slice "version v" .Site.Params.version.major "." .Site.Params.version.minor) "" }}
{{ end }}
{{ return $ret }}

@ -7,22 +7,26 @@
it expects to find newsfragments describing changes to that API.
If the `version.status` setting in config.toml is anything other than
"unstable", then it also expects to find a "release.yaml" file in /changelogs,
which contains:
- `tag`: Git tag for this release
- `date`: date of this release
It then renders this info a table, before the list of changes.
"unstable", then it also expects to find additional settings under
`version` in config.toml:
- `major`: the major version number of the release
- `minor`: the minor version number of the release
- `release_date`: the date of the release
The release tag is calculated as `v<major>.<minor>`; for example `v1.5`.
It then renders this into a table displayed before the list of changes.
*/}}
{{ $path := path.Join "changelogs" }}
{{ $status := .Site.Params.version.status }}
{{ $release_tag := delimit (slice "v" .Site.Params.version.major "." .Site.Params.version.minor) "" }}
{{ if ne $status "unstable" }}
{{ $release_info := readFile (path.Join $path "release.yaml") | transform.Unmarshal }}
<table class="release-info">
<tr><th>Git commit</th><td><a href="https://github.com/matrix-org/matrix-doc/tree/{{ $release_info.tag }}">https://github.com/matrix-org/matrix-doc/tree/{{ $release_info.tag }}</a></td>
<tr><th>Release date</th><td>{{ $release_info.date }}</td>
<tr><th>Git commit</th><td><a href="https://github.com/matrix-org/matrix-doc/tree/{{ $release_tag }}">https://github.com/matrix-org/matrix-doc/tree/{{ $release_tag }}</a></td>
<tr><th>Release date</th><td>{{ .Site.Params.version.release_date }}</td>
</table>
{{ end }}

@ -8,8 +8,12 @@
{{ $status := .Site.Params.version.status }}
{{ if eq $status "unstable"}}
<p>This is the <strong>unstable</strong> version of the Matrix specification.</p>
<p>This changelog lists changes made since the last release of the specification.</p>
{{ else }}
<p>This is version <strong>{{ .Site.Params.version.number }}</strong> of the Matrix specification.</p>
<p>This is version <strong>v{{ .Site.Params.version.major }}.{{ .Site.Params.version.minor }}</strong> of the Matrix specification.</p>
{{ end }}

@ -8,7 +8,7 @@ in.
Format
------
Documentation is written in github-flavored markdown.
Documentation is written in Commonmark markdown.
Sections
--------
@ -66,6 +66,12 @@ Lists should:
* Be used where they provide clarity.
* Contain entries which start with a capital and end with a full stop.
When talking about properties in JSON objects, prefer the word "property" to "field",
"member", or various other alternatives. For example: "this property will be set to
X if ...". Also avoid the term "key" unless you are specifically talking about the
*name* of a property - and be mindful of the scope for confusion with cryptographic
keys.
OpenAPI
~~~~~~~

27
package-lock.json generated

@ -97,22 +97,10 @@
"postcss-value-parser": "^4.1.0"
},
"dependencies": {
"postcss": {
"version": "7.0.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
"source-map": "^0.6.1",
"supports-color": "^6.1.0"
}
},
"supports-color": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
"integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
@ -408,9 +396,9 @@
"dev": true
},
"glob-parent": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"requires": {
"is-glob": "^4.0.1"
@ -439,8 +427,7 @@
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"ignore": {
"version": "5.1.8",
@ -673,9 +660,9 @@
"dev": true
},
"postcss": {
"version": "7.0.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
"version": "7.0.36",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
"integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
"dev": true,
"requires": {
"chalk": "^2.4.2",

@ -0,0 +1,410 @@
# MSC1767: Extensible events in Matrix
While events are currently JSON blobs which accept additional metadata appended to them,
there is no formal structure for how to represent this information or interpret it on the
client side, particularly in the case of unknown event types.
When specifying new events, the proposals often reinvent the same wheel instead of reusing
existing blocks or types, such as in cases where captions, thumbnails, etc need to be
considered for an event. This has further issues of clients not knowing how to render these
newly-specified events, leading to mixed compatibility within the ecosystem.
The above seriously hinders the uptake of new event types (and therefore features) within
the Matrix ecosystem. In the current system, a new event type would be introduced and
all implementations slowly gain support for it - if we instead had reusable types then
clients could automatically support a "good enough" version of that new event type while
"proper" support is written in over time. Such an example could be polls: not every
client will want polls right away, but it would be quite limiting as a user experience
if some users can't even see the question being posed.
This proposal introduces a structure for how extensible events are represented, using
the existing extensible nature of events today, laying the groundwork for more reusable
blocks of content in future events.
With text being the simplest form of representation for events today, this MSC also
specifies a relatively basic text schema for room messages that can be reused in other
events. Other building block types are specified by other MSCs:
* [MSC3954 - Emotes](https://github.com/matrix-org/matrix-doc/pull/3954)
* [MSC3955 - Notices / automated events](https://github.com/matrix-org/matrix-doc/pull/3955)
* [MSC3956 - Encryption](https://github.com/matrix-org/matrix-doc/pull/3956)
* [MSC3927 - Audio](https://github.com/matrix-org/matrix-doc/pull/3927)
* [MSC3551 - Files](https://github.com/matrix-org/matrix-doc/pull/3551)
* [MSC3552 - Images and Stickers](https://github.com/matrix-org/matrix-doc/pull/3552)
* [MSC3553 - Videos](https://github.com/matrix-org/matrix-doc/pull/3553)
* [MSC3554 - Translatable text](https://github.com/matrix-org/matrix-doc/pull/3554)
Some examples of new features/events using extensible events are:
* [MSC3488 - Location data](https://github.com/matrix-org/matrix-doc/pull/3488)
* [MSC3381 - Polls](https://github.com/matrix-org/matrix-doc/pull/3381)
* [MSC3245 - Voice messages](https://github.com/matrix-org/matrix-doc/pull/3245)
* [MSC2192 - Inline widgets](https://github.com/matrix-org/matrix-doc/pull/2192)
* [MSC3765 - Rich text topics](https://github.com/matrix-org/matrix-doc/pull/3765)
**Note**: Readers might find [Andy's blog](https://www.artificialworlds.net/blog/2022/03/08/comparison-of-matrix-events-before-and-after-extensible-events/)
useful for understanding the problem space. Unfortunately, for those who need to
understand the changes to the protocol/specification, the best option is to read
this proposal.
## Proposal
In a new room version (why is described later in this proposal), events are declared
to be represented by their extensible form, as described by this MSC. `m.room.message`
is formally deprecated by this MSC, with removal from the specification happening as
part of a room version adopting the feature. Clients are expected to use extensible
events only in rooms versions which explicitly declare such support (in both unstable
and stable settings), except where noted later in this proposal.
An extensible event is made up of two critical parts: an event type and zero or more
content blocks. The event type defines which content blocks a receiver can expect,
and the content blocks carry the information needed to render the event (whether the
client understands the event type or not).
Content blocks are simply any top-level key in `content` on the event. They can have
any value type (that is also legal in an event generally: string, integer, etc), and
are namespaced using the
[Matrix conventions for namespacing](https://spec.matrix.org/v1.4/appendices/#common-namespaced-identifier-grammar).
Content blocks can be invented independent of event types and *should* be reusable
in nature. For example, this proposal introduces an `m.text` content block which
can be reused by other event types to represent textual fallback.
When a client encounters an extensible event (any event sent in a supported room
version) that it does *not* understand, the client begins searching for a best match
based on event type schemas it *does* know. This may mean combining multiple different
content blocks to match a suitable schema, such as in the case of
[MSC3553](https://github.com/matrix-org/matrix-doc/pull/3553) video events.
Which schemas to try, and in what order, is left as a deliberate implementation detail.
A client might decide to try parsing the event as a video, then image, then file, then
text message, for example.
It is generally not expected that a single content block will describe an entire event,
except in the exceedingly trivial cases (like text messages in this proposal). Multiple
content blocks will usually fully describe the information in the event, and mixins
(described later) can further change how an event is represented or processed.
Note that a "client" in an extensible events sense will typically mean an application
using the Client-Server API, however in reality a client will be anything which needs
to parse and understand event contents (servers for some functions like push rules,
application services, etc).
Per the introduction, text is the baseline format that most/all Matrix clients support
today, often through use of HTML and `m.room.message`. Instead of using `m.room.message`
to represent this content, clients would instead use an `m.message` event with, at
a minimum, a `m.text` content block:
```json5
{
// irrelevant fields not shown
"type": "m.message",
"content": {
"m.text": [
{ "body": "<i>Hello world</i>", "mimetype": "text/html" },
{ "body": "Hello world" }
]
}
}
```
`m.text` has the following definitions associated with it:
* An ordered array of mimetypes and applicable string content to represent a single
marked-up blob of text. Each element is known as a representation.
* `body` in a representation is required, and must be a string.
* `mimetype` is optional in a representation, and defaults to `text/plain`.
* Zero representations are permitted, however senders should aim to always specify
at least one.
* Invalid representations are skipped by clients (missing `body`, not an object, etc).
* The first representation a renderer understands should be used.
* Senders are strongly encouraged to always include a plaintext representation.
* The `mimetype` of a representation determines its `body` - no effort is made to
limit what is allowed in the `body`, however clients are still strongly encouraged
to validate/sanitize the content further, like in the
[existing spec](https://spec.matrix.org/v1.4/client-server-api/#mroommessage-msgtypes)
for HTML.
* Custom text formats in a representation are specified by a suitably custom `mimetype`.
For example, a representation might use a text format extending HTML or XML, or an
all-new markup. This can be used to create bridge-compatible clients where the
destination network's markup is first in the array, followed by more common HTML
and text formats.
Like with the event described above, all event types now describe which content blocks
they expect to see on their events. These content blocks could be required, as is the
case of `m.text` in `m.message`, or they could be optional depending on the situation.
Of course, senders are welcome to send even more blocks which aren't specified in the
schema for an event type, however clients which understand that event type might not
consider them at all.
In `m.message`'s case, `m.text` is the only required content block. The `m.text`
block can be reused by other events to include a text-like format for the event, such
as a text fallback for clients which do not understand how to render a custom event
type.
To reiterate, when a client encounters an unknown event type it first tries to see
if there's a set of content blocks present that it can associate with a known event
type. If it finds suitable content blocks, it parses the event as though the event
were of the known type. If it doesn't find anything useful, the event is left as
unrenderable, just as it likely would today.
To avoid a situation where events end up being unrenderable, it is strongly
recommended that all event types support at least an `m.text` content block in
their schema, thus allowing all events to theoretically be rendered as message
events (in a worst case scenario).
For clarity, events are not able to specify *how* they are handled when the receiver
doesn't know how to render the event type: the sender simply includes all possible or
feasible representations for the data, hoping the receiver will pick the richest form
for the user. As an example, a special medical imaging event type might also be
represented as a video, static image, or text (URL to some healthcare platform): the
sender includes all 3 fallbacks by specifying the needed content blocks, and the
receiver may pick the video, image, or text depending on its own rules.
Events must still only represent a single logical piece of information, thus encouraging
sensible fallback options in the form of content blocks. The information being represented
is described by the event type, as it always has been before this MSC. It is explicitly
not permitted to represent two or more pieces of information in a single event, such
as a livestream reference and poll: senders should look into
[relationships](https://spec.matrix.org/v1.5/client-server-api/#forming-relationships-between-events)
instead.
### Worked example: Custom temperature event
In a hypothetical scenario, a temperature event might look as such:
```json5
{
// irrelevant fields not shown
"type": "org.example.temperature",
"content": {
"m.text": [{"body": "It is 22 degrees at Home"}],
"org.example.probe_value": {
"label": "Home",
"units": "org.example.celsius",
"value": 22
}
}
}
```
In this scenario, clients which understand how to render an `org.example.temperature`
event might use the information in `org.example.probe_value` exclusively, leaving the
`m.text` block for clients which *don't* understand the temperature event type.
Another event type might find inspiration and use the probe value block for their
event as well. Such an example might be in a more industrial control application:
```json5
{
// irrelevant fields not shown
"type": "org.example.tank.level",
"content": {
"m.text": [{"body": "[Danger] The water tank is 90% full."}],
"org.example.probe_value": {
"label": "Tank 3",
"units": "org.example.litres",
"value": 9037
},
"org.example.danger_level": "alert"
}
}
```
This event also demonstrates a `org.example.danger_level` block, which uses a string
value type instead of the previously demonstrated objects and values - this is a legal
content block, as blocks can be of any type.
Clients should be cautious and avoid reusing too many unspecified types as it can create
opportunities for confusion and inconsistency. There should always be an effort to get
useful event types into the Matrix spec for others to benefit from.
### Room version
This MSC requires a room version to make the transition process clear and coordinated.
Normally for a feature such as this, an effort would be made to attempt to support
backwards compatibility for a duration of time, however for a feature that requires
significant overhaul of clients, servers, and Matrix as a whole it feels more important
to bias towards a clear switch between legacy and modern (extensible) events.
**Note**: A previous draft of this proposal (codenamed "v1 extensible events") did attempt
to describe a timeline-based approach, allowing for event types to mix concepts of content
blocks and legacy fields, however that approach did not give sufficient reason for clients
to fully adopt the extensible events changes.
In room versions supporting extensible events, clients MUST only send extensible events.
Deprecated event types (to be enumerated at the time of making the room version) MUST NOT
be sent into extensible event-supporting room versions, and clients MUST treat deprecated
event types as unrenderable by force. For example, if a client sees an `m.room.message` in
an extensible event-supporting room version, it must not render it, even if it knows how
to render that type.
While full enforcement of this restriction is not feasible, servers are encouraged to block
Client-Server API requests for sending known-banned event types into applicable rooms. This
obviously does not help when the room is encrypted, or the client is sending custom events
in a non-extensible form, hence the requirement that clients treat the events as invalid too.
Using the usual MSC process, the Spec Core Team (SCT) will be responsible for determining
the minimum scope of extensible events in a published (stable) room version.
Meanwhile, clients are welcome to use the unstable implementations of extensible event-supporting
features, provided they are in an appropriate room version. Some event type MSCs declare
explicit support for what would normally be an unsupported room version - client authors
should check the applicable MSC or specification for the feature to determine if they are
allowed to do this. Such examples include MSC3381 Polls and MSC3245 Voice Messages.
### State events
Unknown state event types generally should not be parsed by clients. This is to prevent situations
where the sender masks a state change as some other, non-state, event. For example, even
if a state event has an `m.text` content block, it should not be treated as a room message.
Note that state events MUST still make use of content blocks in applicable room versions, and that
any top-level key in `content` is defined as a content block under this proposal. As such, this
MSC implicitly promotes all existing content fields of `m.*` state events to independent content
blocks as needed. Other MSCs may override this decision on a per-event type basis (ie: redeclaring
how room topics work to support content blocks, deprecating the existing `m.room.topic` event in
the process, like in [MSC3765](https://github.com/matrix-org/matrix-spec-proposals/pull/3765)).
Unlike most content blocks, these promoted-to-content-blocks are not realistically meant to be
reused: it is simply a formality given this MSC's scope.
### Notifications
Currently [push notifications](https://spec.matrix.org/v1.5/client-server-api/#push-notifications)
describe how an event can cause a notification to the user, though it makes the assumption
that there are `m.room.message` events flying around to denote "messages" which can trigger
keyword/mention-style alerts. With extensible events, the same might not be possible as it
relies on understanding how/when the client will render the event to cause notifications.
For simplicity, when `content.body` is used in an `event_match` condition, it now looks for
an `m.text` block's `text/plain` representation (implied or explicit) in room versions
supporting extensible events. This is not an easy rule to represent in the existing push
rules schema, and this MSC has no interest in designing a better schema. Note that other
conditions applied to push notifications, such as an event type check, are not affected by
this: clients/servers will have to alter applicable push rules to handle the new event types
(see also: [MSC3933](https://github.com/matrix-org/matrix-spec-proposals/pull/3933) and friends).
### Power levels
This MSC proposes no changes to how power levels interact with events: they are still
capable of restricting which users can send an event type. Though events might be rendered
as a different logical type (ie: unknown event being rendered as a message), this does not
materially impact the room's ability to function. Thus, considerations for how to handle
power levels more intelligently are details left for a future MSC.
As of writing, most rooms fit into two categories: any event type is possible to send, or
specific cherry-picked event types are allowed (announcement rooms: reactions & redactions).
Extensible events don't materially change the situation implied by this power levels structure.
### Mixins specifically allowed
A **mixin** is a specific type of content block which can be added to any type of event to
change how that event is processed. Content blocks which are
mixins will be called out as such in the spec. Mixins are meant to be purely additive,
thus all event types MUST support being rendered/processed *without* the use of mixins.
See also the [Wikipedia entry on mixins](https://en.wikipedia.org/wiki/Mixin).
Note that mixins differ from optional content blocks in an event type's schema: a mixin
is able to be applied to *any* event type sensibly while optional content blocks are
generally only valuable to the applicable event types.
Though this MSC does not describe any such mixins itself,
[MSC3955](https://github.com/matrix-org/matrix-spec-proposals/pull/3955) does by allowing any
event to be flagged as "automated" - a strictly additive annotation on events.
Another possible mixin would be `m.relates_to` (not described by this MSC). Currently,
some features like the [key verification framework](https://spec.matrix.org/v1.5/client-server-api/#key-verification-framework)
rely on relationships as part of making the feature work. The expectation is that
these features would be adapted to meet the "purely additive" condition (assuming
`m.relates_to` does actually end up being a mixin).
### Uses of HTML & text throughout the spec
For an abundance of clarity, all functionality not explicitly called out in this MSC which
relies on the `formatted_body` of an `m.room.message` is expected to transition to using
an appropriate `m.text` representation instead. For example, the HTML representation of
a [mention](https://spec.matrix.org/v1.5/client-server-api/#user-and-room-mentions) will
now appear under `m.text`'s `text/html` representation (adding one if required).
A similar condition is applied to `body` in `m.room.message`: all existing functionality
will instead use the `text/plain` representation within `m.text`, if not explicitly
called out by this MSC.
## Potential issues
It's a bit ugly to not know whether a given key in `content` will take a string, object,
boolean, integer, or array.
It's a bit ugly to not know at a glance if a content block is a mixin or not.
It's a bit ugly that you have to look over the keys of contents to see what blocks
are present, but better than duplicating this into an explicit `blocks` list within the
event content (on balance).
We're skipping over defining rules for which fallback combinations to display
(i.e. "display hints") for now; these can be added in a future MSC if needed.
[MSC1225](https://github.com/matrix-org/matrix-doc/issues/1225) contains a proposal for this.
Placing content blocks at the top level of `content` is a bit unfortunate, though mixes
nicely thanks to namespacing. Potentially conflicting cases in the wild would be
namespaced fields, which would get translated as unrenderable events if the value type
doesn't meet the client's known schema.
This MSC does not rewrite or redefine all possible events in the specification: this is
deliberately left as an exercise for several future MSCs.
## Security considerations
Like today, it's possible to have the different representations of an event not match,
thus introducing a potential for malicious payloads (text-only clients seeing something
different to HTML-friendly ones). Clients could try to do similarity comparisons, though
this is complicated with features like HTML and arbitrary custom markup (markdown, etc)
showing up in the plaintext or in tertiary formats on the events. Historically, room
moderators have been pretty good about removing these malicious senders from their rooms
when other users point out (quite quickly) that the event is appearing funky to them.
## Note about spec process
Extensible events as a spec feature requires dozens of different MSCs, with this MSC being
the structure definition and text baseline. It is *not* expected that this MSC will be
written into spec once it has passed FCP. Instead, it is expected that all of the "core"
extensible events MSCs will pass FCP and extensible events be assigned a stable room version
before any spec authoring begins. Thus, this particular MSC should be anticipated to sit
in accepted-but-not-merged (stable, not formal spec yet) for a while, and that's okay.
The Spec Core Team (SCT) has decision making power over what is considered core for extensible
events, though the recommendation is to ensure replacements for all non-state `m.room.*` types
have accepted (successful FCP) MSCs to replace them.
## Unstable prefix
While this MSC is not considered stable by the specification, implementations *must* use
`org.matrix.msc1767` as a prefix to denote the unstable functionality. For example, sending
an `m.message` event would mean sending an `org.matrix.msc1767.message` event instead.
For purposes of testing, implementations can use a dynamically-assigned unstable room version
`org.matrix.msc1767.<version>` to use extensible events within. For example, `org.matrix.msc1767.10`
for room version 10 or `org.matrix.msc1767.org.example.cool_ver` for a hypothetical
`org.example.cool_ver` room version. Any events sent in these room versions *can* use stable
identifiers given the entire room version itself is unstable, however senders *must* take care
to ensure stable identifiers do not leak out to other room versions - it may be simpler to not
send stable identifiers at all.
## Changes from MSC1225
* converted from googledoc to MD, and to be a single PR rather than split PR/Issue.
* simplifies it by removing displayhints (for now - deferred to a future MSC).
* replaces the clunky m.text.1 idea with lists for types which support fallbacks.
* removes the concept of optional compact form for m.text by instead having m.text always in expanded form.
* tries to accomodate most of the feedback on GH and Google Docs from MSC1225.
## Historical changes
* Anything that wasn't simple text rendering was broken out to dedicated MSCs in an effort to get the
structure approved and adopted while the more complex types get implemented independently.
* Renamed subtypes/reusable types to just "content blocks".
* Allow content blocks to be nested.
* Fix push rules in the most basic sense, deferring to a future MSC on better support.
* Explicitly make no changes to power levels, deferring to a future MSC on better support.
* Drop timeline for transition in favour of an explicit room version.
* Move most push rule changes and such into their own/future MSCs.
* Move emotes, notices, and encryption out to their own dedicated MSCs.

@ -0,0 +1,151 @@
# MSC2285: Private read receipts
Currently users must send read receipts in order to affect their notification
counts, which alerts other people that the user has read their message. For
primarily privacy reasons, it may be desirable to users to not advertise to
others that they've read a message.
## Proposal
This MSC proposes adding a new `receiptType` (see [the receipts
spec](https://spec.matrix.org/v1.3/client-server-api/#receipts)) of
`m.read.private`. This `receiptType` is used when the user wants to affect their
notification count but doesn't want other users to see their read receipt.
To move the user's private read receipt to `$123` the client can make a POST
request to the [`/receipt`
endpoint](https://spec.matrix.org/v1.3/client-server-api/#post_matrixclientv3roomsroomidreceiptreceipttypeeventid).
For example:
```HTTP
POST /_matrix/client/v3/rooms/!a:example.org/receipt/m.read.private/$123
{}
```
The MSC also proposes adding `m.fully_read` and `m.read.private` as a possible
`receiptType` for `/receipt` to make this endpoint consistent with
`/read_markers`. (we have two endpoints that do essentially the same thing, so
it would make sense for them to be consistent)
Alternatively, the client can move the user's `m.fully_read` marker and/or
`m.read` receipt at the same time as `m.read.private` by making a POST request
to the [`/read_markers`
endpoint](https://spec.matrix.org/v1.3/client-server-api/#post_matrixclientv3roomsroomidread_markers).
For example:
```HTTP
POST /_matrix/client/r0/rooms/!a:example.org/read_markers
{
"m.fully_read": "$123",
"m.read": "$123",
"m.read.private": "$123"
}
```
Both `m.read` and `m.read.private` clear notifications in the same way. If the
user sent two receipts into a room, the later one should be the one that decides
the notification count.
The receipt that is more "ahead" of the other takes precedence when considering
notifications and a client's rendering of read receipts. This means that given
an ordered set of events A, B, C, and D the public read receipt could be at
point C, private at point A. If the user moves the private receipt from A to B
then the user's notification count is still considered from point C as the public
receipt is further ahead, still. Other users would also see the user's public read
receipt as not having moved. The user can then move the private read receipt
to point D, hopping over the public receipt, to change their notification count.
For clarity, if the public receipt is "fast forwarded" to be at the same position
as the private receipt then the public receipt is broadcast to other users, even
if previously considered private.
Note that like regular read receipts today, neither receipt can cause a backwards
movement: both receipts can only move forwards, but do not have to be ahead of
each other. It's valid to, for example, update a public read receipt which lags
20 messages behind the private one.
The `m.fully_read` property is now optional for the [`/read_markers`
endpoint](https://spec.matrix.org/v1.3/client-server-api/#post_matrixclientv3roomsroomidread_markers)
as sometimes we only want to send `m.read.private`.
The MSC proposes that from now on, not all things sent over `/receipt` are
federated. Servers MUST NOT send receipts of `receiptType` `m.read.private` to
any other user than the sender. Servers also MUST NOT send receipts of
`receiptType` `m.read.private` to any server over federation.
## Security considerations
Servers could act as if `m.read.private` is the same as `m.read` so the user
must already trust the homeserver to a degree however, and the methods of
notifying the user to the problem are difficult to implement. Users can always
run their own homeservers to ensure it behaves correctly.
## Potential issues
Clients which support read receipts would end up rendering the user's receipt as
jumping down when they send a message. This is no different from how IRC and
similarly bridged users are perceived today.
## Alternatives
It has been suggested to use account data to store the setting that controls
whether read receipts should be private on a per-account/per-room basis. While
this might have some benefits, it is much less flexible.
Previous iterations of this MSC additionally suggested that having an `m.hidden`
flag on existing read receipts could work, however this feels like assigning too
much responsibility to an existing structure.
## Unstable prefix
While this MSC is not considered stable, implementations should use
`org.matrix.msc2285` as a namespace.
|Stable (post-FCP)|Unstable |
|-----------------|---------------------------------|
|`m.read.private` |`org.matrix.msc2285.read.private`|
Clients should check for server support before sending private read receipts:
if the server does not support them, then a private read receipt will not clear
any notifications for the user.
The presence of `org.matrix.msc2285` or `org.matrix.msc2285.stable` in
`unstable_features` is a reliable indication that a server supports private read
receipts; however the converse is not true: their absence does not necessarily
mean that the server does *not* support private read receipts. In particular,
the server may have been updated to a future spec version which includes
private read receipts, and hence removed the `unstable_features` entry.
Therefore, if a client has this feature enabled, but the server does not advertise
support for this MSC in `unstable_features`, the client should either keep sending
private read receipts with the risk that notifications will not be clearing, or it
should warn the user and start sending public read receipts instead.
To mitigate this problem, once this MSC gets merged and once it becomes a part of a
spec version, clients should update their implementations as fast as possible to
accommodate the fact that the way of detecting server support will change: clients
will now be looking for that spec version in `/versions`.
### While the MSC is unstable
During this period, to detect server support clients should check for the
presence of the `org.matrix.msc2285` flag in `unstable_features` on `/versions`.
Clients are also required to use the unstable prefixes (see [unstable
prefix](#unstable-prefix)) during this time.
### Once the MSC is merged but not in a spec version
Once this MSC is merged, but is not yet part of the spec, clients should rely on
the presence of the `org.matrix.msc2285.stable` flag in `unstable_features` to
determine server support. If the flag is present, clients are required to use
stable prefixes (see [unstable prefix](#unstable-prefix)).
### Once the MSC is in a spec version
Once this MSC becomes a part of a spec version, clients should rely on the
presence of the spec version, that supports the MSC, in `versions` on
`/versions`, to determine support. Servers are encouraged to keep the
`org.matrix.msc2285.stable` flag around for a reasonable amount of time
to help smooth over the transition for clients. "Reasonable" is intentionally
left as an implementation detail, however the MSC process currently recommends
*at most* 2 months from the date of spec release.

@ -0,0 +1,250 @@
# MSC2674: Event relationships
It's common to want to send events in Matrix which relate to existing events -
for instance, reactions, edits and even replies/threads.
This proposal is one in a series of proposals that defines a mechanism for
events to relate to each other. Together, these proposals replace
[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849).
* This proposal defines a standard shape for indicating events which relate to
other events.
* [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) defines APIs to
let the server calculate the aggregations on behalf of the client, and so
bundle the related events with the original event where appropriate.
* [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676) defines how
users can edit messages using this mechanism.
* [MSC2677](https://github.com/matrix-org/matrix-doc/pull/2677) defines how
users can annotate events, such as reacting to events with emoji, using this
mechanism.
* [MSC3267](https://github.com/matrix-org/matrix-doc/pull/3267) defines how events
can make a reference to other events.
* [MSC3389](https://github.com/matrix-org/matrix-doc/pull/3389) defines changes to
the redaction algorithm, to preserve the type and target id of a relation.
## Proposal
This proposal introduces the concept of relations, which can be used to
associate new information with an existing event.
A relationship is an object with a field `rel_type`, which is a string describing the type of relation,
and a field `event_id`, which is a string that represents the event_id of the target event of this relation.
The target event must exist in the same room as the relating event is sent.
Both of those fields are required. An event is said to contain a relationship if its `content` contains
a relationship with all the required fields under the `m.relates_to` key. If any of these conditions is not met,
clients and servers should treat the event as if it does not contain a relationship.
Servers should reject events not meeting these conditions with an HTTP 400 error when
they are received via the client-server API.
Here's a (partial) example of an event relating to another event:
```json
{
"content": {
"m.relates_to": {
"rel_type": "m.replace",
"event_id": "$abc:server.tld"
}
}
}
```
All the information about the relationship lives under the `m.relates_to` key.
If it helps, you can think of relations as a "subject verb object" triple,
where the subject is the relation event itself; the verb is the `rel_type`
field of the `m.relates_to` and the object is the `event_id` field.
We consciously do not support multiple different relations within a single event,
in order to keep the API simple. This means that if event A relates to event B
in two different ways you would send two events to describe the two relations,
rather than bundling them into a single event. Another MSC,
like [MSC 3051](https://github.com/matrix-org/matrix-doc/pull/3051),
can propose a change to add support for multiple relations if it turns out that
this would facilitate certain use cases.
Relations do not yet replace the
[reply mechanism currently defined in the spec](https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies).
### Relation types
Any values for `rel_type` should abide the
[general guidelines for identifiers](https://github.com/matrix-org/matrix-doc/pull/3171).
The `rel_type` property determines how an event relates to another and can be used
by clients to determine how and in what context a relation should be displayed.
[MSC 2675](https://github.com/matrix-org/matrix-doc/pull/2675) proposes to also interpret the `rel_type` server-side.
It is left up to the discretion of other MSCs building on this one whether they introduce
`rel_type`s that are specific to their use case or that can serve a broad range
of use cases. MSCs may define additional properties on the relation object for a given `rel_type`.
Currently, a few `rel_type`s are already proposed. Here's a non-exhaustive list:
- `m.replace` in [MSC 2676](https://github.com/matrix-org/matrix-doc/pull/2676).
- `m.annotation` in [MSC 2677](https://github.com/matrix-org/matrix-doc/pull/2677).
- `m.reference` in [MSC 3267](https://github.com/matrix-org/matrix-doc/pull/3267).
- `m.thread` in [MSC 3440](https://github.com/matrix-org/matrix-doc/pull/3440).
### Sending relations
Related events are normal Matrix events, and can be sent by the normal `/send`
API.
The server should postprocess relations if needed before sending them into a
room, as defined by the relationship type. For example, a relationship type
might only allow a user to send one related message to a given event.
### Receiving relations
Relations are received like other non-state events, with `/sync`,
`/messages` and `/context`, as normal discrete Matrix events. As explained
in the limitations, clients may be unaware of some relations using just these endpoints.
[MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) defines ways in
which the server may aid clients in processing relations by aggregating the
events.
### Redactions
Events with a relation may be redacted like any other event.
[MSC3389](https://github.com/matrix-org/matrix-doc/pull/3389) proposes that
the redaction algorithm should preserve the type and target id of a relation.
However, event relationships can still be used in existing room versions, but
the user experience may be worse if redactions are performed.
## Potential issues
### Federation considerations
We have a problem with resynchronising relations after a gap in federation:
We have no way of knowing that an edit happened in the gap to one of the events
we already have. So, we'll show inconsistent data until we backfill the gap.
* We could write this off as a limitation.
* Or we could make *ALL* relations a DAG, so we can spot holes at the next
relation, and go walk the DAG to pull in the missing relations? Then, the
next relation for an event could pull in any of the missing relations.
Socially this probably doesn't work as reactions will likely drop off over
time, so by the time your server comes back there won't be any more reactions
pulling the missing ones in.
* Could we also ask the server, after a gap, to provide all the relations which
happened during the gap to events whose root preceeded the gap.
* "Give me all relations which happened between this set of
forward-extremities when I lost sync, and the point i've rejoined the DAG,
for events which preceeded the gap"?
* Would be hard to auth all the relations which this api coughed up.
* We could auth them based only the auth events of the relation, except we
lose the context of the nearby DAG which we'd have if it was a normal
backfilled event.
* As a result it would be easier for a server to retrospectively lie about
events on behalf of its users.
* This probably isn't the end of the world, plus it's more likely to be
consistent than if we leave a gap.
* i.e. it's better to consistent with a small chance of being maliciously
wrong, than inconsistent with a guaranteed chance of being innocently
wrong.
* We'd need to worry about pagination.
* This is probably the best solution, but can also be added as a v2.
* In practice this seems to not be an issue, which is worth complicating
the s-s API over. Clients very rarely jump over the federation gap to an edit.
In most cases they scroll up, which backfills the server and we have all the
edits, when we reach the event before the gap.
## Limitations
Based solely on this MSC, relations are only received as discrete events in
the timeline, so clients may only have an incomplete image of all the relations
with an event if they do not fill gaps (syncs with a since token that have
`limited: true` set in the sync response for a room) in the timeline.
In practice, this has proven not to be too big of a problem, as reactions
(as proposed in [MSC 2677](https://github.com/matrix-org/matrix-doc/pull/2677))
tend to be posted close after the target event in the timeline.
A more complete solution to this has been deferred to
[MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675).
## Tradeoffs
### Event shape
Shape of
```json
"content": {
"m.relates_to": {
"m.reference": {
"event_id": "$another:event.com"
}
}
}
```
versus
```json
"content": {
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$another:event.com"
}
}
```
The reasons to go with `rel_type` is:
* This format is now in use in the wider matrix ecosystem without a prefix,
in spite of the original MSC 1849 not being merged. This situation is not ideal
but we still don't want to break compatibility with several clients.
* We don't need the extra indirection to let multiple relations apply to a given pair of
events, as that should be expressed as separate relation events.
* If we want 'adverbs' to apply to 'verbs' in the subject-verb-object triples which
relations form, then we apply it as mixins to the relation data itself rather than trying
to construct subject-verb-verb-object sentences.
* We decided to not adopt the format used by `m.in_reply_to` as it allows for multiple relations
and is hence overly flexible. Also, the relation type of `m.in_reply_to` is also overly specific
judged by the guidelines for `rel_type`s laid out in this MSC. Having replies use the same
format as relations is postponed to a later MSC, but it would likely involve replies
adopting the relation format with a more broadly useful `rel_type` (possibly the `m.reference`
type proposed in [MSC3267](https://github.com/matrix-org/matrix-doc/pull/3267)),
rather than relations adopting the replies format.
## Historical context
pik's MSC441 has:
Define the JSON schema for the aggregation event, so the server can work out
which fields should be aggregated.
```json
"type": "m.room._aggregation.emoticon",
"content": {
"emoticon": "::smile::",
"msgtype": "?",
"target_id": "$another:event.com"
}
```
These would then be aggregated, based on target_id, and returned as annotations on
the source event in an `aggregation_data` field:
```json
"content": {
...
"aggregation_data": {
"m.room._aggregation.emoticon": {
"aggregation_data": [
{
"emoticon": "::smile::",
"event_id": "$14796538949JTYis:pik-test",
"sender": "@pik:pik-test"
}
],
"latest_event_id": "$14796538949JTYis:pik-test"
}
}
}
```

@ -0,0 +1,320 @@
# MSC2675: Serverside aggregations of message relationships
It's common to want to send events in Matrix which relate to existing events -
for instance, reactions, edits and even replies/threads.
Clients typically need to track the related events alongside the original
event they relate to, in order to correctly display them. For instance,
reaction events need to be aggregated together by summing and be shown next to
the event they react to; edits need to be aggregated together by replacing the
original event and subsequent edits, etc.
It is possible to treat relations as normal events and aggregate them
clientside, but to do so comprehensively could be very resource intensive, as
the client would need to spider all possible events in a room to find
relationships and maintain a correct view.
Instead, this proposal seeks to solve this problem by defining APIs to let the
server calculate the aggregations on behalf of the client, and so bundle the
aggregated data with the original event where appropriate. It also proposes an
API to let clients paginate through all relations of an event.
This proposal is one in a series of proposals that defines a mechanism for
events to relate to each other. Together, these proposals replace
[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849).
* [MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674) defines a
standard shape for indicating events which relate to other events.
* This proposal defines APIs to let the server calculate the aggregations on
behalf of the client, and so bundle the aggregated data with the original
event where appropriate.
* [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676) defines how
users can edit messages using this mechanism.
* [MSC2677](https://github.com/matrix-org/matrix-doc/pull/2677) defines how
users can annotate events, such as reacting to events with emoji, using this
mechanism.
## Proposal
### Aggregations
Relation events can be aggregated per relation type by the server.
The format of the aggregated value (hereafter called "aggregation")
depends on the relation type.
Some relation types might group the aggregations by the `key` property
in the relation and aggregate to an array, while
others might aggregate to a single object or any other value really.
Here are some non-normative examples of what aggregations can look like:
Example aggregation for [`m.thread`](https://github.com/matrix-org/matrix-doc/pull/3440) (which
aggregates all relations into a single object):
```
{
"latest_event": {
"content": { ... },
...
},
"count": 7,
"current_user_participated": true
}
```
Example aggregation for [`m.annotation`](https://github.com/matrix-org/matrix-doc/pull/2677) (which
aggregates relations into a list of objects, grouped by `key`).
```
[
{
"key": "👍",
"origin_server_ts": 1562763768320,
"count": 3
},
{
"key": "👎",
"origin_server_ts": 1562763768320,
"count": 2
}
]
```
#### Bundling
Other than during non-gappy incremental syncs, timeline events that have other
events relate to them should include the aggregation of those related events
in the `m.relations` property of their unsigned data. This process is
referred to as "bundling", and the aggregated relations included via
this mechanism are called "bundled aggregations".
By sending a summary of the relations, bundling
avoids us having to always send lots of individual relation events
to the client.
Aggregations are never bundled into state events. This is a current
implementation detail that could be revisited later,
rather than a specific design decision.
The following client-server APIs should bundle aggregations
with events they return:
- `GET /rooms/{roomId}/messages`
- `GET /rooms/{roomId}/context/{eventId}`
- `GET /rooms/{roomId}/event/{eventId}`
- `GET /sync`, only for room sections in the response where `limited` field
is `true`; this amounts to all rooms in the response if
the `since` request parameter was not passed, also known as an initial sync.
- `GET /relations`, as proposed in this MSC.
Deprecated APIs like `/initialSync` and `/events/{eventId}` are *not* required
to bundle aggregations.
The bundled aggregations are grouped according to their relation type.
The format of `m.relations` (here with *non-normative* examples of
the [`m.replace`](https://github.com/matrix-org/matrix-doc/pull/2676) and
[`m.annotation`](https://github.com/matrix-org/matrix-doc/pull/2677) relation
types) is as follows:
```json
{
"event_id": "abc",
"unsigned": {
"m.relations": {
"m.annotation": {
"key": "👍",
"origin_server_ts": 1562763768320,
"count": 3
},
"m.replace": {
"event_id": "$edit_event_id",
"origin_server_ts": 1562763768320,
"sender": "@alice:localhost"
},
}
}
}
```
#### Client-side aggregation
Bundled aggregations on an event give a snapshot of what relations were known
at the time the event was received. When relations are received through `/sync`,
clients should locally aggregate (as they might have done already before
supporting this MSC) the relation on top of any bundled aggregation the server
might have sent along previously with the target event, to get an up to date
view of the aggregations for the target event. The aggregation algorithm is the
same as the one described here for the server.
### Querying relations
A single event can have lots of associated relations, and we do not want to
overload the client by, for example, including them all bundled with the
related-to event. Instead, we also provide a new `/relations` API in
order to paginate over the relations, which behaves in a similar way to
`/messages`, except using `next_batch` and `prev_batch` names
(in line with `/sync` API). Tokens from `/sync` or `/messages` can be
passed to `/relations` to only get relating events from a section of
the timeline.
The `/relations` API returns the discrete relation events
associated with an event that the server is aware of
in standard topological order. Note that events may be missing,
see [limitations](#servers-might-not-be-aware-of-all-relations-of-an-event).
You can optionally filter by a given relation type and the event type of the
relating event:
```
GET /_matrix/client/v1/rooms/{roomID}/relations/{event_id}[/{rel_type}[/{event_type}]][?from=token][&to=token][&limit=amount]
```
```
{
"chunk": [
{
"type": "m.reaction",
"sender": "...",
"content": {
"m.relates_to": {
"rel_type": "m.annotation",
...
}
}
}
],
"prev_batch": "some_token",
"next_batch": "some_token",
}
```
The endpoint does not have any trailing slashes. It requires authentication
and is not rate-limited.
The `from` and `limit` query parameters are used for pagination, and work
just like described for the `/messages` endpoint.
Note that [MSC2676](https://github.com/matrix-org/matrix-doc/pull/2676)
adds the related-to event in `original_event` property of the response.
This way the full history (e.g. also the first, original event) of the event
is obtained without further requests. See that MSC for further details.
### End to end encryption
Since the server has to be able to aggregate relation events, structural
information about relations must be visible to the server, and so the
`m.relates_to` field must be included in the plaintext.
A future MSC may define a method for encrypting certain parts of the
`m.relates_to` field that may contain sensitive information.
### Redactions
Redacted relations should not be taken into consideration in
bundled aggregations, nor should they be returned from `/relations`.
Requesting `/relations` on a redacted event should
still return any existing relation events.
This is in line with other APIs like `/context` and `/messages`.
### Local echo
For the best possible user experience, clients should also include unsent
relations into the client-side aggregation. When adding a relation to the send
queue, clients should locally aggregate it into the relations of the target
event, ideally regardless of the target event having received an `event_id`
already or still being pending. If the client gives up on sending the relation
for some reason, the relation should be de-aggregated from the relations of
the target event. If the client offers the user a possibility of manually
retrying to send the relation, it should be re-aggregated when the user does so.
De-aggregating a relation refers to rerunning the aggregation for a given
target event while not considering the de-aggregated event any more.
Upon receiving the remote echo for any relations, a client is likely to remove
the pending event from the send queue. Here, it should also de-aggregate the
pending event from the target event's relations, and re-aggregate the received
remote event from `/sync` to make sure the client-side aggregation happens with
the same event data as on the server.
When adding a redaction for a relation to the send queue, the relation
referred to should be de-aggregated from the relations of the target of the
relation. Similar to a relation, when the sending of the redaction fails or
is cancelled, the relation should be aggregated again.
If the target event is still pending and hasn't received its `event_id` yet,
clients can locally relate relation events to their target by using
`transaction_id` like they already do for detecting remote echos when sending
events.
## Edge cases
### How do you handle ignored users?
* Information about relations sent from ignored users must never be sent to
the client, either in aggregations or discrete relation events.
This is to let you block someone from harassing you with emoji reactions
(or using edits as a side-channel to harass you). Therefore, it is possible
that different users will see different aggregations (a different last edit,
or a different reaction count) on an event.
## Limitations
### Relations can be missed while not being in the room
Relation events behave no different from other events in terms of room history visibility,
which means that some relations might not be visible to a user while they are not invited
or have not joined the room. This can cause a user to see an incomplete edit history or reaction count
based on discrete relation events upon (re)joining a room.
Ideally the server would not include these events in aggregations, as it would mean breaking
the room history visibility rules, but this MSC defers addressing this limitation and
specifying the exact server behaviour to [MSC3570](https://github.com/matrix-org/matrix-doc/pull/3570).
### Servers might not be aware of all relations of an event
The response of `/relations` might be incomplete because the homeserver
potentially doesn't have the full DAG of the room. The federation API doens't
have an equivalent of the `/relations` API, so has no way but to fetch the
full DAG over federation to assure itself that it is aware of all relations.
[MSC2836](https://github.com/matrix-org/matrix-doc/pull/2836) provided a proposal for following relationships over federation in the "Querying relationships over federation" section via a `/_matrix/federation/v1/event_relationships` API
### Event type based aggregation and filtering won't work well in encrypted rooms
The `/relations` endpoint allows filtering by event type,
which for encrypted rooms will be `m.room.encrypted`, rendering this filtering
less useful for encrypted rooms. Aggregation algorithms that take the type of
the relating events they aggregate into account will suffer from the same
limitation.
## Future extensions
### Handling limited (gappy) syncs
For the special case of a gappy incremental sync, many relations (particularly
reactions) may have occurred during the gap. It would be inefficient to send
each one individually to the client, but it would also be inefficient to send
all possible bundled aggregations to the client.
The server could tell the client the event IDs of events which
predate the gap which received relations during the gap. This means that the
client could invalidate its copy of those events (if any) and then requery them
(including their bundled relations) from the server if/when needed,
for example using an extension of the `/event` API for batch requests.
The server could do this with a new `stale_events` field of each room object
in the sync response. The `stale_events` field would list all the event IDs
prior to the gap which had updated relations during the gap. The event IDs
would be grouped by relation type,
and paginated as per the normal Matrix pagination model.
This was originally part of this MSC but left out to limit the scope
to what is implemented at the time of writing.
## Prefix
While this MSC is not considered stable, the endpoints become:
- `GET /_matrix/client/unstable/rooms/{roomID}/relations/{eventID}[/{relationType}[/{eventType}]]`
None of the newly introduced identifiers should use a prefix though, as this MSC
tries to document relation support already being used in
the wider matrix ecosystem.

@ -0,0 +1,407 @@
# MSC2676: Message editing
Users may wish to edit previously sent messages, for example to correct typos.
This can be done by sending a new message with an indication that it replaces
the previously sent message.
This proposal is one in a series of proposals that defines a mechanism for
events to relate to each other. Together, these proposals replace
[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849).
* [MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674) defines a
standard shape for indicating events which relate to other events.
* [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) defines APIs to
let the server calculate the aggregations on behalf of the client, and so
bundle the related events with the original event where appropriate.
* This proposal defines how users can edit messages using this mechanism.
* [MSC2677](https://github.com/matrix-org/matrix-doc/pull/2677) defines how
users can annotate events, such as reacting to events with emoji, using this
mechanism.
## Background
Element-Web (then Riot-Web) and Synapse both implemented initial support for
message editing, following the proposals of MSC1849, in May 2019
([matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk/pull/2952),
[synapse](https://github.com/matrix-org/synapse/pull/5209)). Element-Android
and Element-iOS also added implementations around that time. Unfortunately,
those implementations presented the feature as "production-ready", despite it
not yet having been adopted into the Matrix specification.
The current situation is therefore that client or server implementations hoping
to interact with Element users must simply follow the examples of that
implementation. In other words, message edits form part of the *de-facto* spec
despite not being formalised in the written spec. This is clearly a regrettable
situation. Hopefully, processes have improved over the last three years so that
this situation will not arise again. Nevertheless there is little we can do
now other than formalise the status quo.
This MSC, along with the others mentioned above, therefore seeks primarily to
do that. Although there is plenty of scope for improvement, we consider that
better done in *future* MSCs, based on a shared understanding of the *current*
implementation.
In short, this MSC prefers fidelity to the current implementations over
elegance of design.
## Proposal
### `m.replace` event relationship type
A new `rel_type` of `m.replace` is defined for use with the `m.relates_to`
field as defined in
[MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674). This is
intended primarily for handling edits, and lets you define an event which
replaces an existing event.
Such an event, with `rel_type: m.replace`, is referred to as a "message edit event".
### `m.new_content` property
The `content` of a message edit event must contain a `m.new_content` property
which defines the replacement content. (This allows the normal `body` fields to
be used for a fallback for clients who do not understand replacement events.)
For instance, an `m.room.message` which replaces an existing event might look like:
```json
{
"type": "m.room.message",
"content": {
"body": "* Hello! My name is bar",
"msgtype": "m.text",
"m.new_content": {
"body": "Hello! My name is bar",
"msgtype": "m.text"
},
"m.relates_to": {
"rel_type": "m.replace",
"event_id": "$some_event_id"
}
}
}
```
The `m.new_content` can include any properties that would normally be found in
an event's `content` property, such as `formatted_body`.
#### Encrypted events
If the original event was encrypted, the replacement should be too. In that
case, `m.new_content` is placed in the `content` of the encrypted payload. The
`m.relates_to` property remains unencrypted, as required by the
[relationships](https://spec.matrix.org/v1.3/client-server-api/#forming-relationships-between-events)
section of the Client-Server API specification.
For example, an encrypted replacement event might look like this:
```json
{
"type": "m.room.encrypted",
"content": {
"m.relates_to": {
"rel_type": "m.replace",
"event_id": "$some_event_id"
},
"algorithm": "m.megolm.v1.aes-sha2",
"sender_key": "<sender_curve25519_key>",
"device_id": "<sender_device_id>",
"session_id": "<outbound_group_session_id>",
"ciphertext": "<encrypted_payload_base_64>"
}
}
```
... and, once decrypted, the payload might look like this:
```json
{
"type": "m.room.<event_type>",
"room_id": "!some_room_id",
"content": {
"body": "* Hello! My name is bar",
"msgtype": "m.text",
"m.new_content": {
"body": "Hello! My name is bar",
"msgtype": "m.text"
}
}
}
```
Note that:
* There is no `m.relates_to` property in the encrypted payload. (Any such
property would be ignored.)
* There is no `m.new_content` property in the cleartext `content` of the
`m.room.encrypted` event. (Again, any such property would be ignored.)
For clarity: the payload must be encrypted as normal, ratcheting the Megolm session
as normal. The original Megolm ratchet entry should **not** be re-used.
#### Applying `m.new_content`
When applying a replacement, the `content` property of the original event is
replaced entirely by the `m.new_content`, with the exception of `m.relates_to`,
which is left *unchanged*. Any `m.relates_to` property within `m.new_content`
is ignored.
For example, given a pair of events:
```json
{
"event_id": "$original_event",
"type": "m.room.message",
"content": {
"body": "I *really* like cake",
"msgtype": "m.text",
"formatted_body": "I <em>really</em> like cake",
}
}
```
```json
{
"event_id": "$edit_event",
"type": "m.room.message",
"content": {
"body": "* I *really* like *chocolate* cake",
"msgtype": "m.text",
"m.new_content": {
"body": "I *really* like *chocolate* cake",
"msgtype": "m.text",
"com.example.extension_property": "chocolate"
},
"m.relates_to": {
"rel_type": "m.replace",
"event_id": "$original_event_id"
}
}
}
```
... then the end result is an event as shown below. Note that `formatted_body`
is now absent, because it was absent in the replacement event, but
`m.relates_to` remains unchanged (ie, absent).
```json
{
"event_id": "$original_event",
"type": "m.room.message",
"content": {
"body": "I *really* like *chocolate* cake",
"msgtype": "m.text",
"com.example.extension_property": "chocolate"
}
}
```
Note that the `msgtype` property of `m.room.message` events need not be the
same as in the original event. For example, if a user intended to send a
message beginning with "/me", but their client sends an `m.emote` event
instead, they could edit the message to send be an `m.text` event as they had
originally intended.
### Validity of message edit events
Some message edit events are defined to be invalid. To be considered valid, all
of the following criteria must be satisfied:
* The replacement and original events must have the same `type`.
* Neither the replacement nor original events can be state events (ie, neither
may have a `state_key`).
* The original event must not, itself, have a `rel_type` of `m.replace`.
* The original event and replacement event must have the same `sender`.
* The replacement event (once decrypted, if appropriate) must have an
`m.new_content` property.
The original event and replacement event must also have the same `room_id`, as
required by the
[relationships](https://spec.matrix.org/v1.3/client-server-api/#forming-relationships-between-events)
section of the Client-Server API specification.
If any of these criteria are not satisfied, implementations should ignore the
replacement event (the content of the original should not be replaced, and the
edit should not be included in the server-side aggregation).
### Server behaviour
#### Server-side aggregation of `m.replace` relationships
Note that there can be multiple events with an `m.replace` relationship to a
given event (for example, if an event is edited multiple times). These should
be [aggregated](https://spec.matrix.org/v1.3/client-server-api/#aggregations)
by the homeserver.
The format of the aggregation for `m.replace` simply gives gives the
`event_id`, `origin_server_ts`, and `sender` of the most recent replacement
event (as determined by `origin_server_ts`, falling back to a lexicographic
ordering of `event_id`).
This aggregation is bundled into the `unsigned/m.relations` property of any
event that is the target of an `m.replace` relationship. For example:
```json5
{
"event_id": "$original_event_id",
// ...
"unsigned": {
"m.relations": {
"m.replace": {
"event_id": "$latest_edit_event_id",
"origin_server_ts": 1649772304313,
"sender": "@editing_user:localhost"
}
}
}
}
```
If the original event is redacted, any `m.replace` relationship should **not**
be bundled with it (whether or not any subsequent edits are themselves
redacted). Note that this behaviour is specific to the `m.replace`
relationship.
#### Server-side replacement of content
Whenever an `m.replace` is to be bundled with an event as above, the server should
also modify the `content` of the original event according
to the `m.new_content` of the most recent edit (determined as above).
An exception applies to [`GET
/_matrix/client/v3/rooms/{roomId}/event/{eventId}`](https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomideventeventid),
which should return the *unmodified* event (though the relationship should
still be bundled, as described above).
The endpoints where this behaviour takes place is the same as those where
aggregations are bundled, with the exception of
`/room/{roomId}/event/{eventId}`. This includes:
* `GET /rooms/{roomId}/messages`
* `GET /rooms/{roomId}/context/{eventId}`
* `GET /rooms/{roomId}/relations/{eventId}`
* `GET /rooms/{roomId}/relations/{eventId}/{relType}`
* `GET /rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
* `GET /sync` when the relevant section has a `limited` value of `true`
* `POST /search` for any matching events under `room_events`.
### Client behaviour
Clients can often ignore message edit events, since any events the server
returns via the C-S API will be updated by the server to account for subsequent
edits.
However, clients should apply the replacement themselves when the server is
unable to do so. This happens in the following situations:
1. The client has already received and stored the original event before the message
edit event arrives.
2. The original event (and hence its replacement) are encrypted.
Client authors are reminded to take note of the requirements for [Validity of
message edit events](#validity-of-message-edit-events), and to ignore any
invalid edit events that may be received.
### Permalinks
Permalinks to edited events should capture the event ID that the creator of the
permalink is viewing at that point (which might be a message edit event).
The client viewing the permalink should resolve this ID to the original event
ID, and then display the most recent version of that event.
### Redactions
When a message using a `rel_type` of `m.replace` is redacted, it removes that
edit revision. This has little effect if there were subsequent edits, however
if it was the most recent edit, the event is in effect reverted to its content
before the redacted edit.
Redacting the original message in effect removes the message, including all
subsequent edits, from the visible timeline. In this situation, homeservers
will return an empty `content` for the original event as with any other
redacted event. It must be noted that, although they are not immediately
visible in Element, subsequent edits remain unredacted and can be seen via API
calls. See [Future considerations](#future-considerations).
### Edits of replies
Some particular constraints apply to events which replace a
[reply](https://spec.matrix.org/v1.3/client-server-api/#rich-replies). In
particular:
* There should be no `m.in_reply_to` property in the the `m.relates_to`
object, since it would be redundant (see [Applying
`m.new_content`](#applying-mnew_content) above, which notes that the original
event's `m.relates_to` is preserved), as well as being contrary to the
spirit of
[MSC2674](https://github.com/matrix-org/matrix-spec-proposals/pull/2674)
which expects only one relationship per event.
* `m.new_content` should **not** contain any ["reply
fallback"](https://spec.matrix.org/v1.3/client-server-api/#fallbacks-for-rich-replies),
since it is assumed that any client which can handle edits can also
display replies natively.
An example of an edit to a reply is as follows:
```json
{
"type": "m.room.message",
"content": {
"body": "> <@richvdh:sw1v.org> ab\n\n * ef",
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"formatted_body": "<mx-reply><blockquote><a href=\"https://matrix.to/#/!qOZKfwKPirAoSosXrf:matrix.org/$1652807718765kOVDf:sw1v.org?via=matrix.org&amp;via=sw1v.org\">In reply to</a> <a href=\"https://matrix.to/#/@richvdh:sw1v.org\">@richvdh:sw1v.org</a><br>ab</blockquote></mx-reply> * ef",
"m.new_content": {
"body": "ef",
"msgtype": "m.text",
"format": "org.matrix.custom.html",
"formatted_body": "ef"
},
"m.relates_to": {
"rel_type": "m.replace",
"event_id": "$original_reply_event"
}
}
}
```
## Future considerations
### Ordering of edits
In future we may wish to consider ordering replacements (or relations in
general) via a DAG rather than using `origin_server_ts` to determine ordering -
particularly to mitigate potential abuse of edits applied by moderators.
Whatever, care must be taken by the server to ensure that if there are multiple
replacement events, the server must consistently choose the same one as all
other servers.
### Redaction of edits
It is highly unintuitive that redacting the original event leaves subsequent
edits visible to curious eyes even though they are hidden from the
timeline. This is considered a bug which this MSC makes no attempt to
resolve. See also
[element-web#11978](https://github.com/vector-im/element-web/issues/11978) and
[synapse#5594](https://github.com/matrix-org/synapse/issues/5594).
### Edits to state events
There are various issues which would need to be resolved before edits to state
events could be supported. In particular, we would need to consider how the
semantically-meaningful fields of the content of a state event relate to
`m.new_content`. Variation between implementations could easily lead to
security problems (See
[element-web#21851](https://github.com/vector-im/element-web/issues/21851) for
example.)
### Editing other users' events
There is a usecase for users with sufficient power-level to edit other peoples'
events. For now, no attempt is made to support this. If it is supported in the
future, we would need to find a way to make it clear in the timeline.

@ -0,0 +1,294 @@
# MSC2677: Annotations and Reactions
Users sometimes wish to respond to a message using emojis. When such responses
are grouped visually below the message being reacted to, this provides a
(visually) light-weight way for users to react to messages.
This proposal was originally part of [MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849).
## Background
As with [message
edits](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/2676-message-editing.md#background),
support for reactions were landed in the Element clients and Synapse in May
2019, following the proposals of
[MSC1849](https://github.com/matrix-org/matrix-doc/pull/1849) and then
presented as being "production-ready", despite them not yet having been adopted
into the Matrix specification.
Again as with edits, the current situation is therefore that client or server
implementations hoping to interact with Element users must simply follow the
examples of that implementation.
To rectify the situation, this MSC therefore seeks primarily to formalise the
status quo. Although there is plenty of scope for improvement, we consider
that better done in *future* MSCs, based on a shared understanding of the
*current* implementation.
In short, this MSC prefers fidelity to the current implementations over
elegance of design.
On the positive side: this MSC is the last part of the former MSC1849 to be
formalised, and is by far the most significant feature implemented by the
Element clients which has yet to be specified.
## Proposal
### `m.annotation` event relationship type
A new [event relationship type](https://spec.matrix.org/v1.6/client-server-api/#relationship-types)
with a `rel_type` of `m.annotation`.
This relationship type is intended primarily for handling emoji reactions, allowing clients to
send an event which annotates an existing event.
Another potential usage of annotations is for bots, which could use them to
report the success/failure or progress of a command.
Along with the normal properties `event_id` and `rel_type`, the
[`m.relates_to`](https://spec.matrix.org/v1.6/client-server-api/#definition-mrelates_to)
property should contains a `key` that indicates the annotation being
applied. For example, when reacting with emojis, the `key` contains the emoji
being used.
An event annotating another with the thumbs-up emoji would therefore have the following `m.relates_to` propperty:
```json
"m.relates_to": {
"rel_type": "m.annotation",
"event_id": "$some_event_id",
"key": "👍"
}
```
When sending emoji reactions, the `key` property should include the unicode
[emoji presentation
selector](https://www.unicode.org/reports/tr51/#def_emoji_presentation_selector)
(`\uFE0F`) for codepoints which allow it (see the [emoji variation sequences
list](https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-variation-sequences.txt)).
Any `type` of event is eligible for an annotation, including state events.
### `m.reaction` event type
A new message type `m.reaction` is proposed to indicate that a user is reacting
to a message. No `content` properties are defined for this event type: it serves
only to hold a relationship to another event.
For example, an `m.reaction` event which annotates an existing event with a 👍
looks like:
```json
{
"type": "m.reaction",
"content": {
"m.relates_to": {
"rel_type": "m.annotation",
"event_id": "$some_event_id",
"key": "👍"
}
}
}
```
Since they contain no `content` other than `m.relates_to`, `m.reaction` events
are normally not encrypted, as there would be no benefit in doing so. (However,
see [Encrypted reactions](#encrypted-reactions) below.)
### Interation with edited events
It is not considered valid to send an annotation for a [replacement
event](https://spec.matrix.org/v1.6/client-server-api/#event-replacements)
(i.e., a message edit event): any reactions should refer to the original
event. Annotations of replacement events will be ignored according to the rules
for [counting annotations](#counting-annotations).
As an aside, note that it is not possible to edit a reaction, since replacement
events do not change `m.relates_to` (see [Applying
`m.new_content`](https://spec.matrix.org/v1.6/client-server-api/#applying-mnew_content)),
and there is no other meaningful content within `m.reaction`. If a user wishes
to change their reaction, the original reaction should be redacted and a new
one sent in its place.
### Counting annotations
The intention of annotations is that they are counted up, rather than being displayed individually.
Clients must keep count of the number of annotations with a given event `type`
and annotation `key` they observe for each event; these counts are typically
presented alongside the event in the timeline.
When performing this count:
* Each event `type` and annotation `key` should normally be counted separately,
though whether to actually do so is an implementation decision.
* Annotation events sent by [ignored users](https://spec.matrix.org/v1.6/client-server-api/#ignoring-users)
should be excluded from the count.
* Multiple identical annotations (i.e., with the same event `type` and
annotation `key`) from the same user (i.e., events with the same `sender`) should
be treated as a single annotation.
* It is not considered valid to annotate an event which itself has an
`m.relates_to` with `rel_type: m.annotation` or `rel_type:
m.replace`. Implementations should ignore any such annotation events.
* When an annotation is redacted, it is removed from the count.
### Push rules
Since reactions are considered "metadata" that annotate an existing event, they
should not by default trigger notifications. Thus a new [default override
rule](https://spec.matrix.org/v1.6/client-server-api/#default-override-rules)
is to be added that ignores reaction events:
```json
{
"rule_id": ".m.rule.reaction",
"default": true,
"enabled": true,
"conditions": [
{
"kind": "event_match",
"key": "type",
"pattern": "m.reaction"
}
],
"actions": []
}
```
The rule is added between `.m.rule.tombstone` and `.m.rule.room.server_acl`.
(Synapse implementation: [base_rules.rs](https://github.com/matrix-org/synapse/blob/157c571f3e9d3d09cd763405b6a9eb967f2807e7/rust/src/push/base_rules.rs#L216-L229))
### Server support
#### Avoiding duplicate annotations
Homeservers should prevent users from sending a second annotation for a given
event with identical event `type` and annotation `key` (unless the first event
has been redacted).
Attempts to send such an annotation should be rejected with a 400 error and an
error code of `M_DUPLICATE_ANNOTATION`.
Note that this does not guarantee that duplicate annotations will not arrive
over federation. Clients and servers are responsible for deduplicating received
annotations when [counting annotations](#counting-annotations).
#### Server-side aggregation of `m.annotation` relationships
`m.annotation` relationships are *not* [aggregated](https://spec.matrix.org/v1.6/client-server-api/#aggregations)
by the server. In other words, `m.annotation` is not included in the `m.relations` property.
## Alternatives
### Encrypted reactions
[matrix-spec#660](https://github.com/matrix-org/matrix-spec/issues/660)
discusses the possibility of encrypting message relationships in general.
Given that reactions do not rely on server-side aggregation support, an easier
solution to encrypting reactions might be not to use the relationships
framework at all and instead just use a keys within `m.reaction` events, which
could then be encrypted. For example, a reaction could instead be formatted as:
```json5
{
"type": "m.reaction",
"content": {
"event_id": "$some_event_id",
"key": "👍"
}
}
```
### Extended annotation use case
In future it might be useful to be able to annotate events with more
information, some examples include:
* Annotate commit/PR notification messages with their associated CI state, e.g.
pending/passed/failed.
* If a user issues a command to a bot, e.g. `!deploy-site` the bot could
annotate that event with current state, like "acknowledged",
"redeploying...", "success", "failed", etc.
* Other use cases...?
However, this doesn't really work with the proposed grouping, as the aggregation
key wouldn't contain the right information needed to display it (unlike for
reactions).
One way to potentially support this is to include the events (or a subset of the
event) when grouping, so that clients have enough information to render them.
However this dramatically inceases the size of the parent event if we bundle the
full events inside, even if limit the number we bundle in. To reduce the
overhead the annotation event could include a `m.result` field which gets
included.
This would look something like the following, where the annotation is:
```json
{
"type": "m.bot_command_response",
"content": {
"m.result": {
"state": "success",
},
"m.relates_to": {
"type": "m.annotation",
"key": ""
}
}
}
```
and gets bundled into an event like:
```json
{
"unsigned": {
"m.relations": {
"m.annotation": [
{
"type": "m.bot_command_response",
"key": "",
"count": 1,
"chunk": [
{
"m.result": {
"state": "success",
},
}
],
"limited": false,
}
]
}
}
}
```
This is something that could be added later on. A few issues with this are:
* How does this work with E2EE? How do we encrypt the `m.result`?
* We would end up including old annotations that had been superceded, should
these be done via edits instead?
## Security considerations
Clients should render reactions that have a long `key` field in a sensible
manner. For example, clients can elide overly-long reactions.
If using reactions for upvoting/downvoting purposes we would almost certainly want to anonymise the
reactor, at least from other users if not server admins, to avoid retribution problems.
This gives an unfair advantage to people who run their own servers however and
can cheat and deanonymise (and publish) reactor details. In practice, reactions may
not be best used for upvote/downvote as at the unbundled level they are intrinsically
private data.
Or in a MSC1228 world... we could let users join the room under an anonymous
persona from a big public server in order to vote? However, such anonymous personae
would lack any reputation data.

@ -0,0 +1,97 @@
# MSC2732: Olm fallback keys
Olm uses a set of one-time keys when initializing a session between two
devices: Alice uploads one-time keys to her homeserver, and Bob claims one of
them to perform a Diffie-Hellman to generate a shared key. As implied by the
name, a one-time key is only to be used once. However, if all of Alice's
one-time keys are claimed, Bob will not be able to create a session with Alice.
This can be addressed by Alice uploading a fallback key that is used in place
of a one-time key when no one-time keys are available.
## Proposal
A new request parameter, `fallback_keys`, is added to the body of the
[`/keys/upload` client-server API](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-upload), which is in the same format as the
`one_time_keys` parameter with the exception that there must be at most one key
per key algorithm. If the user had previously uploaded a fallback key for a
given algorithm, it is replaced -- the server will only keep one fallback key
per algorithm for each user.
When uploading fallback keys for algorithms whose key format is a signed JSON
object, client should include a property named `fallback` with a value of
`true`.
Example:
`POST /keys/upload`
```json
{
"fallback_keys": {
"signed_curve25519:AAAAAA": {
"key": "base64+public+key",
"fallback": true,
"signatures": {
"@alice:example.org": {
"ed25519:DEVICEID": "base64+signature"
}
}
}
}
}
```
When Bob calls `/keys/claim` to claim one of Alice's one-time keys, but Alice
has no one-time keys left, the homeserver will return the fallback key instead,
if Alice had previously uploaded one. Unlike with one-time keys, fallback keys
are not deleted when they are returned by `/keys/claim`. However, the server
marks that they have been used.
A new response parameter, `device_unused_fallback_key_types`, is added to
`/sync`. This is an array listing the key algorithms for which the server has
an unused fallback key for the device. If the client wants the server to have a
fallback key for a given key algorithm, but that algorithm is not listed in
`device_unused_fallback_key_types`, the client will upload a new key as above.
The `device_unused_fallback_key_types` parameter must be present if the server
supports fallback keys. Clients can thus treat this field as an indication
that the server supports fallback keys, and so only upload fallback keys to
servers that support them.
Example:
`GET /sync`
Response:
```jsonc
{
// other fields...
"device_unused_fallback_key_types": ["signed_curve25519"]
}
```
## Security considerations
Using a fallback key rather than a one-time key has security implications. An
attacker can replay a message that was originally sent with a fallback key, and
the receiving client will accept it as a new message if the fallback key is
still active. Also, an attacker that compromises a client may be able to
retrieve the private part of the fallback key to decrypt past messages if the
client has still retained the private part of the fallback key.
For this reason, clients should not store the private part of the fallback key
indefinitely. For example, client should only store at most two fallback keys:
the current fallback key (that it has not yet received any messages for) and
the previous fallback key, and should remove the previous fallback key once it
is reasonably certain that it has received all the messages that use it (for
example, one hour after receiving the first message that used it).
For addressing replay attacks, clients can also keep track of inbound sessions
to detect replays.
## Unstable prefix
The `fallback_keys` request parameter and the `device_unused_fallback_key_types`
response parameter will be prefixed by `org.matrix.msc2732.`.

@ -0,0 +1,135 @@
# MSC2778: Providing authentication method for appservice users
Appservices within Matrix are increasingly attempting to support End-to-End Encryption. As such, they
need a way to generate devices for their users so that they can participate in E2E rooms. In order to
do so, this proposal suggests implementing an appservice extension to the
[`POST /login` endpoint](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login).
Appservice users do not usually need to log in as they do not need their own access token, and do not
traditionally need a "device". However, E2E encryption demands that at least one user in a room has a
Matrix device which means bridge users need to be able to generate a device on demand. In the past,
bridge developers have used the bridge bot's device for all bridge users in the room, but this causes
problems should the bridge wish to only join ghosts to a room (e.g. for DMs).
Another advantage this provides is that an appservice can now be used to generate access tokens for
any user in its namespace without having to set a password for that user, which may be useful where
maintaining password(s) in the configuration is undesirable.
## Proposal
A new `type` is to be added to `POST /login`: `m.login.application_service`
The `/login` endpoint may now take an `access_token` in the same way that other
authenticated endpoints do. No additional parameters should be specified in the request body.
Example request
```json
{
"type": "m.login.application_service",
"identifier": {
"type": "m.id.user",
"user": "_bridge_alice"
}
}
```
Note: Implementations MUST use the `identifier.type`=`m.id.user` method of specifying the
localpart. The deprecated top-level `user` field **cannot** use this login flow type. This
is deliberate so as to coax developers into using the new identifier format when implementing
new flows.
The response body should be unchanged from the existing `/login` specification.
If one of the following conditions are true:
- The access token is not provided
- The access token does not correspond to an appservice
- Or the user has not previously been registered
Then the servers MUST reject with HTTP 403, with an `errcode` of `"M_FORBIDDEN"`.
If the access token DOES correspond to an appservice but the user is not inside its namespace,
then the `errcode` must be `"M_EXCLUSIVE"`.
Homeservers should ignore the `access_token` parameter if a type other than
`m.login.application_service` has been provided.
Appservices creating **new** users can still use the `/register` endpoint to generate an `access_token` / `device_id`
but for existing users, the `/login` endpoint can be used instead.
## Potential issues
This proposal means that there will be more calls to make when setting up a appservice user, when
using encryption. While this could be done during the registration step, this would prohibit creating
new devices should the appservice intentionally or inadvertently have lost the client-side device data.
## Alternatives
### 1. Include the token in the `/login` request body
One minor tweak to the current proposal could be to include the token as part of the auth data, rather than
being part of the header/params to the request. An argument could be made for either, but since the specification
expects the appservice to pass the token this way in all requests, including `/register`, it seems wise to keep
it that way.
### 2. Use implementation specific "shared secret" authentication
Some community members have used homeserver implementation details such as a "shared secret" authentication method to
log into the accounts without having to use the /login process at all. Synapse provides such a function,
but also means the appservice can now authenticate as any user on the homeserver. This is undesirable from a
security standpoint.
### 3. Keep using `/register` solely
A third option could be to create a new endpoint that simply creates a new device for an appservice user on demand.
Given the rest of the matrix eco-system does this with /login, and /login is already extensible with `type`, it would
create more work for all parties involved for little benefit.
Finally, `POST /register` does already return a `device_id` and `access_token` so appservices
could store this information rather than calling `POST /login` at all. This does however present a few problems:
- Quite a few appservices which only support unencrypted messaging do not use/store the `device_id`/`access_token` from a register call.
In the event that an appservice eventually gains the ability to support encryption, they would be unable to fetch a new `device_id`/
`access_token` for any existing users (as `/register` would fail for an existing user).
- If user tokens were lost or exposed, there is no way to programattically create new access tokens for these users.
- Finally, if a user was registered externally and the appservice would like to masquerade as it, it would be unable to fetch
an access token for that user.
While `POST /register` does work, it is impactical as the sole method of fetching an access token.
## Security considerations
Appservices could use this new functionality to generate devices for any userId that are within its namespace e.g. setting the
user namespace regex to `@.*:example.com` would allow appservice to control anyone on the homeserver. While this sounds scary, in practice
this is not a problem because:
- Appservice namespaces are maintained by the homeserver admin. If the namespace were to change, then it's reasonable
to assume that the server admin is aware. There is no defense mechanism to stop a malicious server admin from creating new
devices for a given user's account as they could also do so by simply modifying the database.
- While an appservice *could* try to masquerade as a user maliciously without the server admin expecting it, it would still
be bound by the restrictions of the namespace. Server admins are expected to be aware of the implications of adding new
appservices to their server so the burden of responsibility lies with the server admin.
- Appservices already can /sync as any user using the `as_token` and send any messages as any user in the namespace, the only
difference is that without a dedicated access token they are unable to receive device messages. While in theory this
does make them unable to see encrypted messages, this is not designed to be a security mechanism.
In conclusion this MSC only automates the creation of new devices for users inside an AS namespace, which is something
a server admin could already do. Appservices should always be treated with care and so with these facts in mind the MSC should
be considered secure.
## Unstable prefix
Implementations should use `uk.half-shot.msc2778.login.application_service` for `type` given in the
`POST /login` until this lands in a released version of the specification.
## Implementations
The proposal has been implemented by a homeserver, a bridge SDK and two bridges:
- [synapse](https://github.com/matrix-org/synapse/pull/8320)
- [mautrix-python](https://github.com/tulir/mautrix-python/commit/12d7c48ca7c15fd3ff61608369af1cf69e289aeb)
- [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp/commit/ead8a869c84d07fadc7cfcf3d522452c99faaa36)
- [matrix-appservice-bridge](https://github.com/matrix-org/matrix-appservice-bridge/pull/231/files#diff-5e93f1b51d50a44fcf0ca46ea1793c1cR851-R864)

@ -0,0 +1,28 @@
# MSC2832: Homeserver -> Application Service authorization header
Most of the auth tokens in the spec are passed in the `Authorization` header,
with the `access_token` query parameter supported for backwards-compatibility.
For some reason, the application service spec was not updated in the same way
and it still requires using the archaic query parameter when the homeserver
pushes transactions to the appservice.
## Proposal
The `access_token` query parameter is removed from all requests made by the
homeserver to appservice and is replaced with the `Authorization` header with
`Bearer <token>` as the value.
### Backwards-compatibility
Homeservers which want to support old spec versions in the appservice API may
send both the query parameter and header. Similarly, appservices may accept the
token from either source.
## Security considerations
Not fixing this causes access tokens to be logged in many bridges.
## Alternatives
We could add a way for appservices to explicitly specify which spec version
they want in order to implement backwards-compatibility without sending both
tokens.
## Unstable prefix
The authorization header is already used in the client-server spec, and an
unstable prefix would just unnecessarily complicate things.

@ -0,0 +1,163 @@
# MSC2918: Refresh tokens
In Matrix, requests to the Client-Server API are currently authenticated using non-expiring, revocable access tokens.
An access token might leak for various reasons, including:
- leaking from the server database (and its backups)
- intercepting it with a man-in-the-middle attack
- leaking from the client storage (and its backups)
In the OAuth 2.0 world, this vector of attack is partly mitigated by having expiring access tokens with short lifetimes and rotating refresh tokens to renew them.
This MSC adds support for expiring access tokens and introduces refresh tokens to renew them.
A more [detailed rationale](#detailed-rationale) of what kind of attacks it mitigates lives at the end of this document.
## Proposal
Homeservers can choose to have access tokens expire after a short amount of time, forcing the client to renew them with a refresh token.
A refresh token is issued on login and rotates on each usage.
It allows homeservers to opt for signed and non-revocable access tokens (JWTs, Macaroon, etc.) for performance reasons if their expiration is short enough (less than 5 minutes).
It is heavily recommended for clients to support refreshing tokens for additional security.
They can advertise their support by adding a `"refresh_token": true` field in the request body on the `/login` and `/register` APIs.
Handling of clients that do *not* support refreshing access tokens is up to individual homeserver deployments.
For example, server administrators may choose to support such clients for backwards-compatibility, or to expire access tokens anyway for improved security at the cost of inferior user experience in legacy clients.
If a client uses an access token that has expired, the server will respond with an `M_UNKNOWN_TOKEN` error, preferably with the `soft_logout` parameter set to `true` to improve the user experience in legacy clients.
Thus, if a client receives an `M_UNKNOWN_TOKEN` error, and it has a refresh token available, it should no longer assume that it has been logged out, and instead attempt to refresh the token.
If the client was in fact logged out, then the server will respond with an `M_UNKNOWN_TOKEN` error to the token refresh request, possibly with the `soft_logout` parameter set.
### Login API changes
The login API returns two additional fields:
- `expires_in_ms`: The lifetime in milliseconds of the access token.
- `refresh_token`: The refresh token, which can be used to obtain new access tokens.
This also applies to logins done by application services.
Both fields are optional.
If `expires_in_ms` is missing, the client can assume the access token won't expire.
If `refresh_token` is missing but `expires_in_ms` is present, the client can assume the access token will expire but it won't have a way to refresh the access token without re-logging in.
Clients advertise their support for refreshing tokens by setting the `refresh_token` field to `true` in the request body.
### Account registration API changes
Unless `inhibit_login` is `true`, the account registration API returns two additional fields:
- `expires_in_ms`: The lifetime in milliseconds of the access token.
- `refresh_token`: The refresh token, which can be used to obtain new access tokens.
This also applies to registrations done by application services.
As in the login API, both field are optional.
Clients advertise their support for refreshing tokens by setting the `refresh_token` field to `true` in the request body.
### Token refresh API
This API lets the client refresh the access token.
A new refresh token is also issued.
The existing refresh token remains valid until the new access token (or refresh token) is used, at which point it is revoked.
This allows for the request to get lost in flight.
The Matrix server can revoke the old access token right away, but does not have to since its lifetime is short enough that it will expire anyway soon after.
`POST /_matrix/client/r0/refresh`
```json
{
"refresh_token": "aaaabbbbccccdddd"
}
```
response:
```json
{
"access_token": "xxxxyyyyzzz",
"expires_in_ms": 60000,
"refresh_token": "eeeeffffgggghhhh"
}
```
If the `refresh_token` is missing from the response, the client can assume the refresh token has not changed and use the same token in subsequent token refresh API requests.
The `refresh_token` parameter can be invalid for two reasons:
- if it does not exist
- if it was already used once
In both cases, the server must reply with a `401` HTTP status code and an `M_UNKNOWN_TOKEN` error code.
This new use case of the `M_UNKNOWN_TOKEN` error code must be reflected in the spec.
As with other endpoints, the server can include an extra `soft_logout` parameter in the response to signify the client it should do a soft logout.
This new API should be rate-limited and does not require authentication since only the `refresh_token` parameter is needed.
Identity assertion via the `user_id` query parameter as defined by the Application Service API specification is disabled on this endpoint.
### Device handling
The current spec states that "Matrix servers should record which device each access token is assigned to".
This must be updated to reflect that devices are bound to a session, which are created during login and stays the same after refreshing the token.
## Potential issues
The refresh token being rotated on each refresh is strongly recommended in the OAuth 2.0 world for unauthenticated clients to avoid token replay attacks.
This can however make the deployment of CLI tools for Matrix a bit harder, since the credentials can't be statically defined anymore.
This is not an issue in OAuth 2.0 because usually CLI tools use the client credentials flow, also known as service accounts.
An alternative would be to make the refresh token non-rotating for now but recommend clients to support rotation of refresh tokens and enforce it later on.
## Alternatives
This MSC defines a new endpoint for token refresh, but it could also be integrated as a new authentication mechanism.
## Security considerations
The time to live (TTL) of access tokens isn't enforced in this MSC but is advised to be kept relatively short.
Servers might choose to have stateless, digitally signed access tokens (JWT are good examples of this), which makes them non-revocable.
The TTL of access tokens should be around 15 minutes if they are revocable and should not exceed 5 minutes if they are not.
## Unstable prefix
While this MSC is not in a released version of the specification, clients should use the `org.matrix.msc2918.refresh_token` field in place of the `refresh_token` field in requests to the login and registration endpoints.
The refresh token endpoint should be served and used using the unstable prefix: `POST /_matrix/client/unstable/org.matrix.msc2918/refresh`.
## Detailed rationale
This MSC does not aim to protect against a completely compromised client.
More specifically, it does not protect against an attacker that managed to distribute an alternate, compromised version of the client to users.
In contrast, it protects against a whole range of attacks where the access token and/or refresh token get leaked but the client isn't completely compromised.
For example, those tokens can leak from user backups (user backs up his device on a NAS, the NAS gets compromised and leaks a backup of the client's secret storage), but one can assume those backups could be at least 5 min old.
If the leak only includes the access token, it is useless to the attacker since it would have expired.
If it also includes the refresh token, it is useless *if* the token was refreshed before (which will happen if the user just opens their Matrix client in between).
Worst case scenario, the leaked refresh token is still valid: in this case, the attacker would consume the refresh token to get a valid access token, but when the original client tries to use the same refresh token, the homeserver can detect it, consider the session has been compromised, end the session and warn the user.
This kind of attack also applies to leakage from the server, which could happen from database backups, for example.
The important thing here is while it does not completely prevent attacks in case of a token leakage, it does make this range of attack a lot more time-sensitive and detectable.
A homeserver will notice if a refresh token is being used twice.
The IETF has interesting [guidelines for refresh tokens](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2).
They recommend that either:
- the refresh tokens are sender-bound and require client authentication (making token leakage completely useless if the client credentials are not leaked at the same time)
- or make them rotate to make the attack a lot harder, as described just above.
Since all clients are "public" in the Matrix world, there are no client-bound credentials that could be used, hence the rotation of refresh tokens.
---
The other kind of scenario where this change makes sense is to help further changes in the homeservers.
A good, recent example of this, is in Synapse v1.34.0 [they moved away from macaroons for access tokens](https://github.com/matrix-org/synapse/pull/5588) to random, shorter, saved in database tokens, similar to [what GitHub did recently](https://github.blog/2021-04-05-behind-githubs-new-authentication-token-formats/).
Because there is no refresh token mechanism in the C2S API, most Synapse instances now have a mix of the two formats of tokens, and for a long time.
It makes it impossible to enforce the new format of tokens without invalidating all existing sessions, making it impossible to roll out changes like a web-app firewall in front of Synapse that verifies the shape and checksums of tokens even before reaching Synapse.
---
Lastly, expiring tokens already exist in Synapse (via the `session_lifetime` configuration parameter).
Before this MSC, clients had no idea when the session would end and relied on the server replying with a 401 error with `soft_logout: true` in the response on a random request to trigger a soft logout and go through the authentication process again.
A side effect of this MSC (although it could have been introduced separately) is that the login responses can now include a `expires_in_ms` to inform the clients when the token will expire.

@ -0,0 +1,389 @@
# MSC2946: Spaces Summary
This MSC depends on [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772), which
describes why a Space is useful:
> Collecting rooms together into groups is useful for a number of purposes. Examples include:
>
> * Allowing users to discover different rooms related to a particular topic: for example "official matrix.org rooms".
> * Allowing administrators to manage permissions across a number of rooms: for example "a new employee has joined my company and needs access to all of our rooms".
> * Letting users classify their rooms: for example, separating "work" from "personal" rooms.
>
> We refer to such collections of rooms as "spaces".
This MSC attempts to solve how a member of a space discovers rooms in that space. This
is useful for quickly exposing a user to many aspects of an entire community, using the
examples above, joining the "official matrix.org rooms" space might suggest joining a few
rooms:
* A room to discuss development of the Matrix Spec.
* An announcements room for news related to matrix.org.
* An off-topic room for members of the space.
## Proposal
A new client-server API (and corresponding server-server API) is added which allows
for querying for the rooms and spaces contained within a space. This allows a client
to efficiently display a hierarchy of rooms to a user (i.e. without having
to walk the full state of each room).
### Client-server API
An endpoint is provided to walk the space tree, starting at the provided room ID
("the root room"), and visiting other rooms/spaces found via `m.space.child`
events. It recurses into the children and into their children, etc.
Any child room that the user is joined or is potentially joinable (per
[MSC3173](https://github.com/matrix-org/matrix-doc/pull/3173)) is included in
the response. When a room with a `type` of `m.space` is found, it is searched
for valid `m.space.child` events to recurse into.
In order to provide a consistent experience, the space tree should be walked in
a depth-first manner, e.g. whenever a space is found it should be recursed into
by sorting the children rooms and iterating through them.
There could be loops in the returned child events; clients and servers should
handle this gracefully. Similarly, note that a child room might appear multiple
times (e.g. also be a grandchild). Clients and servers should handle this
appropriately.
This endpoint requires authentication and is subject to rate-limiting.
#### Request format
```text
GET /_matrix/client/v1/rooms/{roomID}/hierarchy
```
Query Parameters:
* **`suggested_only`**: Optional. If `true`, return only child events and rooms
where the `m.space.child` event has `suggested: true`. Must be a boolean,
defaults to `false`.
This applies transitively, i.e. if a `suggested_only` is `true` and a space is
not suggested then it should not be searched for children. The inverse is also
true, if a space is suggested, but a child of that space is not then the child
should not be included.
* **`limit`**: Optional: a client-defined limit to the maximum
number of rooms to return per page. Must an integer greater than zero.
Server implementations should impose a maximum value to avoid resource
exhaustion.
* **`max_depth`**: Optional: The maximum depth in the tree (from the root room)
to return. The deepest depth returned will not include children events. Defaults
to no-limit. Must be a non-negative integer.
Server implementations may wish to impose a maximum value to avoid resource
exhaustion.
* **`from`**: Optional. Pagination token given to retrieve the next set of rooms.
Note that if a pagination token is provided, then the parameters given for
`suggested_only` and `max_depth` must be the same.
#### Response Format
* **`rooms`**: `[object]` For each room/space, starting with the root room, a
summary of that room. The fields are the same as those returned by
`/publicRooms` (see
[spec](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-publicrooms)),
with the addition of:
* **`room_type`**: the value of the `m.type` field from the room's
`m.room.create` event, if any.
* **`children_state`**: The stripped state of the `m.space.child` events of
the room per [MSC3173](https://github.com/matrix-org/matrix-doc/pull/3173).
In addition to the standard stripped state fields, the following is included:
* **`origin_server_ts`**: `integer`. The `origin_server_ts` field from the
room's `m.space.child` event. This is required for sorting of rooms as
specified below.
* **`next_batch`**: Optional `string`. The token to supply in the `from` param
of the next `/hierarchy` request in order to request more rooms. If this is absent,
there are no more results.
#### Example request:
```text
GET /_matrix/client/v1/rooms/%21ol19s%3Ableecker.street/hierarchy?
limit=30&
suggested_only=true&
max_depth=4
```
#### Example response:
```jsonc
{
"rooms": [
{
"room_id": "!ol19s:bleecker.street",
"avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
"guest_can_join": false,
"name": "CHEESE",
"num_joined_members": 37,
"topic": "Tasty tasty cheese",
"world_readable": true,
"join_rules": "public",
"room_type": "m.space",
"children_state": [
{
"type": "m.space.child",
"state_key": "!efgh:example.com",
"content": {
"via": ["example.com"],
"suggested": true
},
"room_id": "!ol19s:bleecker.street",
"sender": "@alice:bleecker.street",
"origin_server_ts": 1432735824653
},
{ ... }
]
},
{ ... }
],
"next_batch": "abcdef"
}
```
#### Errors:
An HTTP response with a status code of 403 and an error code of `M_FORBIDDEN`
should be returned if the user doesn't have permission to view/peek the root room.
This should also be returned if that room does not exist, which matches the
behavior of other room endpoints (e.g.
[`/_matrix/client/r0/rooms/{roomID}/aliases`](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-rooms-roomid-aliases))
to not divulge that a room exists which the user doesn't have permission to view.
An HTTP response with a status code of 400 and an error code of `M_INVALID_PARAM`
should be returned if the `from` token provided is unknown to the server or if
the `suggested_only` or `max_depth` parameters are modified during pagination.
#### Server behaviour
The server should generate the response as discussed above, by doing a depth-first
search (starting at the "root" room) for any `m.space.child` events. Any
`m.space.child` with an invalid `via` are discarded (invalid is defined as in
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772): missing, not an
array or an empty array).
In the case of the homeserver not having access to the state of a room, the
server-server API (see below) can be used to query for this information over
federation from one of the servers provided in the `via` key of the
`m.space.child` event. It is recommended to cache the federation response for a
period of time. The federation results may contain information on a room
that the requesting server is already participating in; the requesting server
should use its local data for such rooms rather than the data returned over
federation.
When the current response page is full, the current state should be persisted
and a pagination token should be generated (if there is more data to return).
To prevent resource exhaustion, the server may expire persisted data that it
deems to be stale.
The persisted state will include:
* The processed rooms.
* Rooms to process (in depth-first order with rooms at the same depth
ordered [according to MSC1772, as updated to below](#msc1772-ordering)).
* Room information from federation responses for rooms which have yet to be
processed.
### Server-server API
The Server-Server API has a similar interface to the Client-Server API, but a
simplified response. It is used when a homeserver is not participating in a room
(and cannot summarize room due to not having the state).
The main difference is that it does *not* recurse into spaces and does not support
pagination. This is somewhat equivalent to a Client-Server request with a `max_depth=1`.
Additional federation requests are made to recurse into sub-spaces. This allows
for trivially caching responses for a short period of time (since it is not
easily known the room summary might have changed).
Since the server-server API does not know the requesting user, the response should
divulge information based on if any member of the requesting server could join
the room. The requesting server is trusted to properly filter this information
using the `world_readable`, `join_rules`, and `allowed_room_ids` fields from the
response.
If the target server is not a member of some children rooms (so would have to send
another request over federation to inspect them), no attempt is made to recurse
into them. They are simply omitted from the `children` key of the response.
(Although they will still appear in the `children_state`key of the `room`.)
Similarly, if a server-set limit on the size of the response is reached, additional
rooms are not added to the response and can be queried individually.
#### Request format
```text
GET /_matrix/federation/v1/hierarchy/{roomID}
```
Query Parameters:
* **`suggested_only`**: The same as the Client-Server API.
#### Response format
The response format is similar to the Client-Server API:
* **`room`**: `object` The summary of the requested room, see below for details.
* **`children`**: `[object]` For each room/space, a summary of that room, see
below for details.
* **`inaccessible_children`**: Optional `[string]`. A list of room IDs which are
children of the requested room, but are inaccessible to the requesting server.
Assuming the target server is non-malicious and well-behaved, then other
non-malicious servers should respond with the same set of inaccessible rooms.
Thus the requesting server can consider the rooms inaccessible from everywhere.
This is used to differentiate between rooms which the requesting server does
not have access to from those that the target server cannot include in the
response (which will simply be missing in the response).
For both the `room` and `children` fields the summary of the room/space includes
the fields returned by `/publicRooms` (see [spec](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-publicrooms)),
with the addition of:
* **`room_type`**: the value of the `m.type` field from the room's `m.room.create`
event, if any.
* **`allowed_room_ids`**: A list of room IDs which give access to this room per
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083).<sup id="a1">[1](#f1)</sup>
#### Example request:
```jsonc
GET /_matrix/federation/v1/hierarchy/{roomID}?
suggested_only=true
```
#### Errors:
An HTTP response with a status code of 404 and an error code of `M_NOT_FOUND` is
returned if the target server is not a member of the requested room or the
requesting server is not allowed to access the room.
### MSC1772 Ordering
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772) defines the ordering
of "default ordering of siblings in the room list" using the `order` key:
> Rooms are sorted based on a lexicographic ordering of the Unicode codepoints
> of the characters in `order` values. Rooms with no `order` come last, in
> ascending numeric order of the `origin_server_ts` of their `m.room.create`
> events, or ascending lexicographic order of their `room_id`s in case of equal
> `origin_server_ts`. `order`s which are not strings, or do not consist solely
> of ascii characters in the range `\x20` (space) to `\x7F` (~), or consist of
> more than 50 characters, are forbidden and the field should be ignored if
> received.
Unfortunately there are situations when a homeserver comes across a reference to
a child room that is unknown to it and must decide the ordering. Without being
able to see the `m.room.create` event (which it might not have permission to see)
no proper ordering can be given.
Consider the following case of a space with 3 child rooms:
```
Space A
|
+--------+--------+
| | |
Room B Room C Room D
```
HS1 has users in Space A, Room B, and Room C, while HS2 has users in Room D. HS1 has no users
in Room D (and thus has no state from it). Room B, C, and D do not have an
`order` field set (and default to using the ordering rules above).
When a user asks HS1 for the space summary with a `limit` equal to `2` it cannot
fulfill this request since it is unsure how to order Room B, Room C, and Room D,
but it can only return 2 of them. It *can* reach out over federation to HS2 and
request a space summary for Room D, but this is undesirable:
* HS1 might not have the permissions to know any of the state of Room D, so might
receive a 404 error.
* If we expand the example above to many rooms than this becomes expensive to
query a remote server simply for ordering.
This proposes changing the ordering rules from MSC1772 to the following:
> Rooms are sorted based on a lexicographic ordering of the Unicode codepoints
> of the characters in `order` values. Rooms with no `order` come last, in
> ascending numeric order of the `origin_server_ts` of their `m.space.child`
> events, or ascending lexicographic order of their `room_id`s in case of equal
> `origin_server_ts`. `order`s which are not strings, or do not consist solely
> of ascii characters in the range `\x20` (space) to `\x7E` (~), or consist of
> more than 50 characters, are forbidden and the field should be ignored if
> received.
This modifies the clause for calculating the order to use the `origin_server_ts`
of the `m.space.child` event instead of the `m.room.create` event. This allows
for a defined sorting of siblings based purely on the information available in
the state of the space while still allowing for a natural ordering due to the
age of the relationship.
## Potential issues
A large flat space (a single room with many `m.space.child` events) could cause
a large federation response.
Room version upgrades of rooms in a space are unsolved and left to a future MSC.
When upgrading a room it is unclear if the old room should be removed (in which
case users who have not yet joined the new room will no longer see it in the space)
or leave the old room (in which case users who have joined the new room will see
both). The current recommendation is for clients de-duplicate rooms which are
known old versions of rooms in the space.
## Alternatives
Peeking to explore the room state could be used to build the tree of rooms/spaces,
but this would be significantly more expensive for both clients and servers. It
would also require peeking over federation (which is explored in
[MSC2444](https://github.com/matrix-org/matrix-doc/pull/2444)).
## Security considerations
A space with many sub-spaces and rooms on different homeservers could cause
a large number of federation requests. A carefully crafted space with inadequate
server enforced limits could be used in a denial of service attack. Generally
this is mitigated by enforcing server limits and caching of responses.
The requesting server over federation is trusted to filter the response for the
requesting user. The alternative, where the requesting server sends the requesting
`user_id`, and the target server does the filtering, is unattractive because it
rules out a caching of the result. This does not decrease security since a server
could lie and make a request on behalf of a user in the proper space to see the
given information. I.e. the calling server must be trusted anyway.
## Unstable prefix
During development of this feature it will be available at unstable endpoints.
The client-server API will be:
`/_matrix/client/unstable/org.matrix.msc2946/rooms/{roomID}/hierarchy`
The server-server API will be:
`/_matrix/federation/unstable/org.matrix.msc2946/hierarchy/{roomID}`
## Footnotes
<a id="f1"/>[1]: As a worked example, in the context of
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083), consider that Alice
and Bob share a server; Alice is a member of a space, but Bob is not. A remote
server will not know whether the request is on behalf of Alice or Bob (and hence
whether it should share details of restricted rooms within that space).
Consider if the space is modified to include a restricted room on a different server
which allows access from the space. When summarizing the space, the homeserver must make
a request over federation for information on the room. The response should include
the room (since Alice is able to join it). Without additional information the
calling server does not know *why* they received the room and cannot properly
filter the returned results.
Note that there are still potential situations where each server individually
doesn't have enough information to properly return the full summary, but these
do not seem reasonable in what is considered a normal structure of spaces. (E.g.
in the above example, if the remote server is not in the space and does not know
whether the server is in the space or not it cannot return the room.)[↩](#a1)

@ -0,0 +1,286 @@
# MSC3030: Jump to date API endpoint
Add an API that makes it easy to find the closest messages for a given
timestamp.
The goal of this change is to have clients be able to implement a jump to date
feature in order to see messages back at a given point in time. Pick a date from
a calender, heatmap, or paginate next/previous between days and view all of the
messages that were sent on that date.
Alongside the [roadmap of feature parity with
Gitter](https://github.com/vector-im/roadmap/issues/26), we're also interested
in using this for a new better static Matrix archive. Our idea is to server-side
render [Hydrogen](https://github.com/vector-im/hydrogen-web) and this new
endpoint would allow us to jump back on the fly without having to paginate and
keep track of everything in order to display the selected date.
Also useful for archiving and backup use cases. This new endpoint can be used to
slice the messages by day and persist to file.
Related issue: [*URL for an arbitrary day of history and navigation for next and
previous days*
(vector-im/element-web#7677)](https://github.com/vector-im/element-web/issues/7677)
## Problem
These types of use cases are not supported by the current Matrix API because it
has no way to fetch or filter older messages besides a manual brute force
pagination from the most recent event in the room. Paginating is time-consuming
and expensive to process every event as you go (not practical for clients).
Imagine wanting to get a message from 3 years ago 😫
## Proposal
Add new client API endpoint `GET
/_matrix/client/v1/rooms/{roomId}/timestamp_to_event?ts=<timestamp>&dir=[f|b]`
which fetches the closest `event_id` to the given timestamp `ts` query parameter
in the direction specified by the `dir` query parameter. The direction `dir`
query parameter accepts `f` for forward-in-time from the timestamp and `b` for
backward-in-time from the timestamp. This endpoint also returns
`origin_server_ts` to make it easy to do a quick comparison to see if the
`event_id` fetched is too far out of range to be useful for your use case.
When an event can't be found in the given direction, the endpoint throws a 404
`"errcode":"M_NOT_FOUND",` (example error message `"error":"Unable to find event
from 1672531200000 in direction f"`).
In order to solve the problem where a homeserver does not have all of the history in a
room and no suitably close event, we also add a server API endpoint `GET
/_matrix/federation/v1/timestamp_to_event/{roomId}?ts=<timestamp>?dir=[f|b]` which other
homeservers can use to ask about their closest `event_id` to the timestamp. This
endpoint also returns `origin_server_ts` to make it easy to do a quick comparison to see
if the remote `event_id` fetched is closer than the local one. After the local
homeserver receives a response from the federation endpoint, it probably should
try to backfill this event via the federation `/event/<event_id>` endpoint so that it's
available to query with `/context` from a client in order to get a pagination token.
The heuristics for deciding when to ask another homeserver for a closer event if
your homeserver doesn't have something close, are left up to the homeserver
implementation, although the heuristics will probably be based on whether the
closest event is a forward/backward extremity indicating it's next to a gap of
events which are potentially closer.
A good heuristic for which servers to try first is to sort by servers that have
been in the room the longest because they're most likely to have anything we ask
about.
These endpoints are authenticated and should be rate-limited like similar client
and federation endpoints to prevent resource exhaustion abuse.
```
GET /_matrix/client/v1/rooms/<roomID>/timestamp_to_event?ts=<timestamp>&dir=<direction>
{
"event_id": ...
"origin_server_ts": ...
}
```
Federation API endpoint:
```
GET /_matrix/federation/v1/timestamp_to_event/<roomID>?ts=<timestamp>&dir=<direction>
{
"event_id": ...
"origin_server_ts": ...
}
```
---
In order to paginate `/messages`, we need a pagination token which we can get
using `GET /_matrix/client/r0/rooms/{roomId}/context/{eventId}?limit=0` for the
`event_id` returned by `/timestamp_to_event`.
We can always iterate on `/timestamp_to_event` later and return a pagination
token directly in another MSC ⏩
## Potential issues
### Receiving a rogue random delayed event ID
Since `origin_server_ts` is not enforcably accurate, we can only hope that an event's
`origin_server_ts` is relevant enough to its `prev_events` and descendants.
If you ask for "the message with `origin_server_ts` closest to Jan 1st 2018" you
might actually get a rogue random delayed one that was backfilled from a
federated server, but the human can figure that out by trying again with a
slight variation on the date or something.
Since there isn't a good or fool-proof way to combat this, it's probably best to just go
with `origin_server_ts` and not let perfect be the enemy of good.
### Receiving an unrenderable event ID
Another issue is that clients could land on an event they can't/won't render,
such as a reaction, then they'll be forced to desperately seek around the
timeline until they find an event they can do something with.
Eg:
- Client wants to jump to January 1st, 2022
- Server says there's an event on January 2nd, 2022 that is close enough
- Client finds out there's a ton of unrenderable events like memberships, poll responses, reactions, etc at that time
- Client starts paginating forwards, finally finding an event on January 27th it can render
- Client wasn't aware that the actual nearest neighbouring event was backwards on December 28th, 2021 because it didn't paginate in that direction
- User is confused that they are a month past the target date when the message is *right there*.
Clients can be smarter here though. Clients can see when events were sent as
they paginate and if they see they're going more than a couple days out, they
can also try the other direction before going further and further away.
Clients can also just explain to the user what happened with a little toast: "We
were unable to find an event to display on January 1st, 2022. The closest event
after that date is on January 27th."
### Abusing the `/timestamp_to_event` API to get the `m.room.create` event
Although it's possible to jump to the start of the room and get the first event in the
room (`m.room.create`) with `/timestamp_to_event?dir=f&ts=0`, clients should still use
`GET /_matrix/client/v3/rooms/{roomId}/state/m.room.create/` to get the room creation
event.
In the future, with things like importing history via
[MSC2716](https://github.com/matrix-org/matrix-spec-proposals/pull/2716), the first
event you encounter with `/timestamp_to_event?dir=f&ts=0` could be an imported event before
the room was created.
## Alternatives
We chose the current `/timestamp_to_event` route because it sounded like the
easist path forward to bring it to fruition and get some real-world experience.
And was on our mind during the [initial discussion](https://docs.google.com/document/d/1KCEmpnGr4J-I8EeaVQ8QJZKBDu53ViI7V62y5BzfXr0/edit#bookmark=id.qu9k9wje9pxm) because there was some prior art with a [WIP
implementation](https://github.com/matrix-org/synapse/pull/9445/commits/91b1b3606c9fb9eede0a6963bc42dfb70635449f)
from @erikjohnston. The alternatives haven't been thrown out for a particular
reason and we could still go down those routes depending on how people like the
current design.
### Paginate `/messages?around=<timestamp>` from timestamp
Add the `?around=<timestamp>` query parameter to the `GET
/_matrix/client/r0/rooms/{roomId}/messages` endpoint. This will start the
response at the message with `origin_server_ts` closest to the provided `around`
timestamp. The direction is determined by the existing `?dir` query parameter.
Use topological ordering, just as Element would use if you follow a permalink.
This alternative could be confusing to the end-user around how this plays with
the existing query parameters
`/messages?from={paginationToken}&to={paginationToken}` which also determine
what part of the timeline to query. Those parameters could be extended to accept
timestamps in addition to pagination tokens but then could get confusing again
when you start mixing timestamps and pagination tokens. The homeserver also has
to disambiguate what a pagination token looks like vs a unix timestamp. Since
pagination tokens don't follow a certain convention, some homeserver
implementations may already be using arbitrary number tokens already which would
be impossible to distinguish from a timestamp.
A related alternative is to use `/messages` with a `from_time`/`to_time` (or
`from_ts`/`to_ts`) query parameters that only accept timestamps which solves the
confusion and disambigution problem of trying to re-use the existing `from`/`to`
query paramters. Re-using `/messages` would reduce the number of round-trips and
potentially client-side implementations for the use case where you want to fetch
a window of messages from a given time. But has the same round-trip problem if
you want to use the returned `event_id` with `/context` or another endpoint
instead.
### Filter by date in `RoomEventFilter`
Extend `RoomEventFilter` to be able to specify a timestamp or a date range. The
`RoomEventFilter` can be passed via the `?filter` query param on the `/messages`
endpoint.
This suffers from the same confusion to the end-user of how it plays with how
this plays with `/messages?from={paginationToken}&to={paginationToken}` which
also determines what part of the timeline to query.
### Return the closest event in any direction
We considered omitting the `dir` parameter (or allowing `dir=c`) to have the server
return the closest event to the timestamp, regardless of direction. However, this seems
to offer little benefit.
Firstly, for some usecases (such as archive viewing, where we want to show all the
messages that happened on a particular day), an explicit direction is important, so this
would have to be optional behaviour.
For a regular messaging client, "directionless" search also offers little benefit: it is
easy for the client to repeat the request in the other direction if the returned event
is "too far away", and in any case it needs to manage an iterative search to handle
unrenderable events, as discussed above.
Implementing a directionless search on the server carries a performance overhead, since
it must search both forwards and backwards on every request. In short, there is little
reason to expect that a single `dir=c` request would be any more efficient than a pair of
requests with `dir=b` and `dir=f`.
### New `destination_server_ts` field
Add a new field and index on messages called `destination_server_ts` which
indicates when the message was received from federation. This gives a more
"real" time for how someone would actually consume those messages.
The contract of the API is "show me messages my server received at time T"
rather than the messy confusion of showing a delayed message which happened to
originally be sent at time T.
We've decided against this approach because the backfill from federated servers
could be horribly late.
---
Related issue around `/sync` vs `/messages`,
https://github.com/matrix-org/synapse/issues/7164
> Sync returns things in the order they arrive at the server; backfill returns
> them in the order determined by the event graph.
>
> *-- @richvdh, https://github.com/matrix-org/synapse/issues/7164#issuecomment-605877176*
> The general idea is that, if you're following a room in real-time (ie,
> `/sync`), you probably want to see the messages as they arrive at your server,
> rather than skipping any that arrived late; whereas if you're looking at a
> historical section of timeline (ie, `/messages`), you want to see the best
> representation of the state of the room as others were seeing it at the time.
>
> *-- @richvdh , https://github.com/matrix-org/synapse/issues/7164#issuecomment-605953296*
## Security considerations
We're only going to expose messages according to the existing message history
setting in the room (`m.room.history_visibility`). No extra data is exposed,
just a new way to sort through it all.
## Unstable prefix
While this MSC is not considered stable, the endpoints are available at `/unstable/org.matrix.msc3030` instead of their `/v1` description from above.
```
GET /_matrix/client/unstable/org.matrix.msc3030/rooms/<roomID>/timestamp_to_event?ts=<timestamp>&dir=<direction>
{
"event_id": ...
"origin_server_ts": ...
}
```
```
GET /_matrix/federation/unstable/org.matrix.msc3030/timestamp_to_event/<roomID>?ts=<timestamp>&dir=<direction>
{
"event_id": ...
"origin_server_ts": ...
}
```
Servers will indicate support for the new endpoint via a non-empty value for feature flag
`org.matrix.msc3030` in `unstable_features` in the response to `GET
/_matrix/client/versions`.

@ -0,0 +1,28 @@
# MSC3069: Allow guests to use /account/whoami
Currently the [/account/whoami](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-account-whoami)
endpoint does not mention anything about guests, which is a bit of an oversight. The implementation
of the endpoint got created such that guest access was declined.
## Proposal
Guests are allowed to use `/account/whoami`. When a guest makes a request, the response will have
an added `is_guest: true` field - this field is optional (default `false`) otherwise.
## Potential issues
None forseen. This corrects a mistake.
## Alternatives
None relevant.
## Security considerations
Guests will be able to know their user ID, as they would when they registered in the first place.
## Unstable prefix
While this MSC is not in a stable version of the specification, implementations should use
`org.matrix.msc3069.is_guest` in place of `is_guest`. Callers should note that they might see
`M_GUEST_ACCESS_FORBIDDEN` errors if the server is not implementing this MSC.

@ -0,0 +1,291 @@
# Restricting room membership based on membership in other rooms
A desirable feature is to give room admins the power to restrict membership of
their room based on the membership of one or more rooms.
Potential usecases include:
* Private spaces (allowing any member of a [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772)
space to join child rooms in that space), for example:
> members of the #doglovers:example.com space can join this room without an invitation<sup id="a1">[1](#f1)</sup>
* Room upgrades for private rooms (instead of issuing invites to each user).
* Allowing all users in a private room to be able to join a private breakout room.
This does not preclude members from being directly invited to the room, which is
still a useful discovery feature.
## Proposal
In a future room version a new `join_rule` (`restricted`) will be used to reflect
a cross between `invite` and `public` join rules. The content of the join rules
would include the rooms to trust for membership. For example:
```json
{
"type": "m.room.join_rules",
"state_key": "",
"content": {
"join_rule": "restricted",
"allow": [
{
"type": "m.room_membership",
"room_id": "!mods:example.org"
},
{
"type": "m.room_membership",
"room_id": "!users:example.org"
}
]
}
}
```
This means that a user must be a member of the `!mods:example.org` room or
`!users:example.org` room in order to join without an invite<sup id="a2">[2](#f2)</sup>.
Membership in a single allowed room is enough.
If the `allow` key is an empty list (or not a list at all), then no users are
allowed to join without an invite. Each entry is expected to be an object with the
following keys:
* `type`: `"m.room_membership"` to describe that we are allowing access via room
membership. Future MSCs may define other types.
* `room_id`: The room ID to check the membership of.
Any entries in the list which do not match the expected format are ignored. Thus,
if all entries are invalid, the list behaves as if empty and all users without
an invite are rejected.
The `allow` key is to be protected when redacting an event.
When a homeserver receives a `/join` request from a client or a `/make_join` /
`/send_join` request from another homeserver, the request should only be permitted
if the user is invited to this room, or is joined to one of the listed rooms. If
the user is not a member of at least one of the rooms, the homeserver should return
an error response with HTTP status code of 403 and an `errcode` of `M_FORBIDDEN`.
It is possible for a resident homeserver (one which receives a `/make_join` /
`/send_join` request) to not know if the user is in some of the allowed rooms (due
to not participating in them). If the user is not in any of the allowed rooms that
are known to the homeserver, and the homeserver is not participating in all listed
rooms, then it should return an error response with HTTP status code of 400 with an `errcode` of `M_UNABLE_TO_AUTHORISE_JOIN`. The joining server should
attempt to join via another resident homeserver. If the resident homeserver knows
that the user is not in *any* of the allowed rooms it should return an error response
with HTTP status code of 403 and an `errcode` of `M_FORBIDDEN`. Note that it is a
configuration error if there are allowed rooms with no participating authorised
servers.
A chosen resident homeserver might also be unable to issue invites (which, as below,
is a pre-requisite for generating a correctly-signed join event). In this case
it should return an error response with HTTP status code of 400 and an `errcode`
of `M_UNABLE_TO_GRANT_JOIN`. The joining server should attempt to join via another
resident homeserver.
From the perspective of the [auth rules](https://spec.matrix.org/unstable/rooms/v1/#authorization-rules),
the `restricted` join rule has the same behavior as `public`, with the additional
caveat that servers must ensure that, for `m.room.member` events with a `membership` of `join`:
* The user's previous membership was `invite` or `join`, or
* The join event has a valid signature from a homeserver whose users have the
power to issue invites.
When generating a join event for `/join` or `/make_join`, the server should
include the MXID of a local user who could issue an invite in the content with
the key `join_authorised_via_users_server`. The actual user chosen is arbitrary.
The changes to the auth rules imply that:
* A join event issued via `/send_join` is signed by not just the requesting
server, but also the resident server.<sup id="a3">[3](#f3)</sup>
In order for the joining server to receive the proper signatures the join
event will be returned via `/send_join` in the `event` field.
* The auth chain of the join event needs to include events which prove
the homeserver can be issuing the join. This can be done by including:
* The `m.room.power_levels` event.
* The join event of the user specified in `join_authorised_via_users_server`.
It should be confirmed that the authorising user is in the room. (This
prevents situations where any homeserver could process the join, even if
they weren't in the room, under certain power level conditions.)
The above creates a new restriction on the relationship between the resident
servers used for `/make_join` and `/send_join` -- they must now both go to
the same server (since the `join_authorised_via_users_server` is added in
the call to `/make_join`, while the final signature is added during
the call to `/send_join`). If a request to `/send_join` is received that includes
an event from a different resident server it should return an error response with
HTTP status code of 400.
Note that the homeservers whose users can issue invites are trusted to confirm
that the `allow` rules were properly checked (since this cannot easily be
enforced over federation by event authorisation).<sup id="a4">[4](#f4)</sup>
To better cope with joining via aliases, homeservers should use the list of
authorised servers (not the list of candidate servers) when a user attempts to
join a room.
## Summary of the behaviour of join rules
See the [join rules](https://matrix.org/docs/spec/client_server/r0.6.1#m-room-join-rules)
specification for full details; the summary below is meant to highlight the differences
between `public`, `invite`, and `restricted` from a user perspective. Note that
all join rules are subject to `ban` and `server_acls`.
* `public`: anyone can join, as today.
* `invite`: only people with membership `invite` can join, as today.
* `knock`: the same as `invite`, except anyone can knock. See
[MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
* `private`: This is reserved, but unspecified.
* `restricted`: the same as `invite`, except users may also join if they are a
member of a room listed in the `allow` rules.
## Security considerations
Increased trust to enforce the join rules during calls to `/join`, `/make_join`,
and `/send_join` is placed in the homeservers whose users can issue invites.
Although it is possible for those homeservers to issue a join event in bad faith,
there is no real-world benefit to doing this as those homeservers could easily
side-step the restriction by issuing an invite first anyway.
## Unstable prefix
The `restricted` join rule will be included in a future room version to allow
servers and clients to opt-into the new functionality.
During development, an unstable room version of `org.matrix.msc3083.v2` will be used.
Since the room version namespaces the behaviour, the `allow` key and value, as well
as the `restricted` join rule value do not need unstable prefixes.
An unstable key of `org.matrix.msc3083.v2.event` will be used in the response
from `/send_join` in place of `event` during development.
## Alternatives
It may seem that just having the `allow` key with `public` join rules is enough
(as originally suggested in [MSC2962](https://github.com/matrix-org/matrix-doc/pull/2962)),
but there are concerns that changing the behaviour of a pre-existing `public`
join rule may cause security issues in older implementations (that do not yet
understand the new behaviour). This could be solved by introducing a new room
version, thus it seems clearer to introduce a new join rule -- `restricted`.
Using an `allow` key with the `invite` join rules to broaden who can join was rejected
as an option since it requires weakening the [auth rules](https://spec.matrix.org/unstable/rooms/v1/#authorization-rules).
From the perspective of the auth rules, the `restricted` join rule is identical
to `public` with additional checks on the signature of the event.
## Future extensions
### Checking room membership over federation
If a homeserver is not in an allowed room (and thus doesn't know the
membership of it) then the server cannot enforce the membership checks while
generating a join event. Peeking over federation, as described in
[MSC2444](https://github.com/matrix-org/matrix-doc/pull/2444),
could be used to establish if the user is in any of the proper rooms.
This would then delegate power out to a (potentially) untrusted server, giving that
peek server significant power. For example, a poorly chosen peek
server could lie about the room membership and add an `@evil_user:example.org`
to an allowed room to gain membership to a room.
As iterated above, this MSC recommends rejecting the join, potentially allowing
the requesting homeserver to retry via another homeserver.
### Kicking users out when they leave the allowed room
In the above example, suppose `@bob:server.example` leaves `!users:example.org`:
should they be removed from the room? Likely not, by analogy with what happens
when you switch the join rules from `public` to `invite`. Join rules currently govern
joins, not existing room membership.
It is left to a future MSC to consider this, but some potential thoughts are
given below.
If you assume that a user *should* be removed in this case, one option is to
leave the departure up to Bob's server `server.example`, but this places a
relatively high level of trust in that server. Additionally, if `server.example`
were offline, other users in the room would still see Bob in the room (and their
servers would attempt to send message traffic to it).
Another consideration is that users may have joined via a direct invite, not via
access through a room.
Fixing this is thorny. Some sort of annotation on the membership events might
help, but it's unclear what the desired semantics are:
* Assuming that users in an allowed room are *not* kicked when that room is
removed from `allow`, are those users then given a pass to remain
in the room indefinitely? What happens if the room is added back to
`allow` and *then* the user leaves it?
* Suppose a user joins a room via an allowed room (RoomA). Later, RoomB is added
to the `allow` list and RoomA is removed. What should happen when the
user leaves RoomB? Are they exempt from the kick?
It is possible that completely different state should be kept, or a different
`m.room.member` state could be used in a more reasonable way to track this.
### Inheriting join rules
If an allowed room is a space and you make a parent space invite-only, should that
(optionally?) cascade into child rooms? This would have some of the same problems
as inheriting power levels, as discussed in [MSC2962](https://github.com/matrix-org/matrix-doc/pull/2962).
### Additional allow types
Future MSCs may wish to define additional values for the `type` argument, potentially
restricting access via:
* MXIDs or servers.
* A shared secret (room password).
These are just examples and are not fully thought through for this MSC, but it should
be possible to add these behaviors in the future.
### Client considerations
[MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772) defines a `via`
key in the content of `m.space.child` events:
> the content must contain a via `key` which gives a list of candidate servers
> that can be used to join the room.
It is possible for the list of candidate servers and the list of authorised
servers to diverge. It may not be possible for a user to join a room if there's
no overlap between these lists.
If there is some overlap between the lists of servers the join request should
complete successfully.
Clients should also consider the authorised servers when generating candidate
servers to embed in links to the room, e.g. via matrix.to.
A future MSC may define a way to override or update the `via` key in a coherent
manner.
## Footnotes
<a id="f1"/>[1]: The converse restriction, "anybody can join, provided they are not members
of the #catlovers:example.com space" is less useful since:
1. Users in the banned room could simply leave it at any time
2. This functionality is already partially provided by
[Moderation policy lists](https://matrix.org/docs/spec/client_server/r0.6.1#moderation-policy-lists). [](#a1)
<a id="f2"/>[2]: Note that there is nothing stopping users sending and
receiving invites in `public` rooms today, and they work as you might expect.
The only difference is that you are not *required* to hold an invite when
joining the room. [](#a2)
<a id="f3"/>[3]: This seems like an improvement regardless since the resident server
is accepting the event on behalf of the joining server and ideally this should be
verifiable after the fact, even for current room versions. Requiring all events
to be signed and verified in this way is left to a future MSC. [](#a3)
<a id="f4"/>[4]: This has the downside of increased centralisation, as some
homeservers that are already in the room may not issue a join event for another
user on that server. (It must go through the `/make_join` / `/send_join` flow of
a server whose users may issue invites.) This is considered a reasonable
trade-off. [](#a4)

@ -0,0 +1,195 @@
# MSC3173: Expose stripped state events to any potential joiner
It can be useful to view the partial state of a room before joining to allow a user
to know *what* they're joining. For example, it improves the user experience to
show the user the room name and avatar before joining.
It is already allowed to partially view the room state without being joined to
the room in some situations:
* If the room has `history_visibility: world_readable`, then anyone can inspect
it (by calling `/state` on it).
* Rooms in the [room directory](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms)
expose some of their state publicly.
* [Invited users](https://matrix.org/docs/spec/server_server/r0.1.4#put-matrix-federation-v2-invite-roomid-eventid)
and [knocking users](https://github.com/matrix-org/matrix-doc/pull/2403)
receive stripped state events to display metadata to users.
This MSC proposes formalizing that the stripped state that is currently available
to invited and knocking users is available to any user who could potentially join
a room. It also defines "stripped state" and consolidates the recommendation on
which events to include in the stripped state.
## Background
When creating an invite it is [currently recommended](https://matrix.org/docs/spec/server_server/r0.1.4#put-matrix-federation-v2-invite-roomid-eventid)
to include stripped state events which are useful for displaying the invite to a user:
> 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.
The invited user receives these [stripped state events](https://spec.matrix.org/unstable/client-server-api/#get_matrixclientr0sync)
as part of the `/sync` response:
> The state of a room that the user has been invited to. These state events may
> only have the `sender`, `type`, `state_key` and `content` keys present. These
> events do not replace any state that the client already has for the room, for
> example if the client has archived the room.
These are sent as part of the [`unsigned` content of the `m.room.member` event](https://spec.matrix.org/unstable/client-server-api/#mroommember)
containing the invite.
[MSC2403: Add "knock" feature](https://github.com/matrix-org/matrix-doc/pull/2403)
extends this concept to also include the stripped state events in the `/sync` response
for knocking users:
> It is proposed to add a fourth possible key to rooms, called `knock`. Its value
> is a mapping from room ID to room information. The room information is a mapping
> from a key `knock_state` to another mapping with key events being a list of
> `StrippedStateEvent`.
It is also provides an extended rationale of why this is useful:
> These stripped state events contain information about the room, most notably the
> room's name and avatar. A client will need this information to show a nice
> representation of pending knocked rooms. The recommended events to include are the
> join rules, canonical alias, avatar, name and encryption state of the room, rather
> than all room state. This behaviour matches the information sent to remote
> homeservers when remote users are invited to a room.
[MSC1772: Spaces](https://github.com/matrix-org/matrix-doc/pull/1772) additionally
recommends including the `m.room.create` event as one of the stripped state events:
> Join rules, invites and 3PID invites work as for a normal room, with the exception
> that `invite_state` sent along with invites should be amended to include the
> `m.room.create` event, to allow clients to discern whether an invite is to a
> space-room or not.
## Proposal
The specification does not currently define what "stripped state" is or formally
describe who can access it, instead it is specified that certain situations (e.g.
an invite or knock) provide a potential joiner with the stripped state of a room.
This MSC clarifies what "stripped state" is and formalizes who can access the
stripped state of a room in future cases.
Potential ways that a user might be able to join a room include, but are not
limited to, the following mechanisms:
* A room that has `join_rules` set to `public` or `knock`.
* A room that the user is in possession of an invite to (regardless of the `join_rules`).
This MSC proposes a formal definition for the stripped state of a room<sup id="a1">[1](#f1)</sup>:
> The stripped state of a room is a list of simplified state events to help a
> potential joiner identify the room. These state events may only have the
> `sender`, `type`, `state_key` and `content` keys present.
### Client behavior
These events do not replace any state that the client already has for the room,
for example if the client has archived the room. Instead the client should keep
two separate copies of the state: the one from the stripped state and one from the
archived state. If the client joins the room then the current state will be given
as a delta against the archived state not the stripped state.
### Server behavior
It is recommended (although not required<sup id="a2">[2](#f2)</sup>) that
homeserver implementations include the following events as part of the stripped
state of a room:
* Create event (`m.room.create`)<sup id="a3">[3](#f3)</sup>
* Join rules (`m.room.join_rules`)
* Canonical alias (`m.room.canonical_alias`)
* Room avatar (`m.room.avatar`)
* Room name (`m.room.name`)
* Encryption information (`m.room.encryption`)<sup id="a4">[4](#f4)</sup>
* Room topic (`m.room.topic`)<sup id="a5">[5](#f5)</sup>
## Potential issues
This is a formalization of current behavior and should not introduce new issues.
## Alternatives
A different approach would be to continue with what is done today for invites,
knocking, the room directory: separately specify that a user is allowed to see
the stripped state (and what events the stripped state should contain).
## Security considerations
This would allow for invisibly accessing the stripped state of a room with `public`
or `knock` join rules.
In the case of a public room, if the room has `history_visibility` set to `world_readable`
then this is no change. Additionally, this information is already provided by the
room directory (if the room is listed there). Otherwise, it is trivial to access
the state of the room by joining, but currently users in the room would know
that the join occurred.
Similarly, in the case of knocking, a user is able to trivially access the
stripped state of the room by knocking, but users in the room would know that
the knock occurred.
This does not seem to weaken the security expectations of either join rule.
## Future extensions
### Revisions to the room directory
A future MSC could include additional information from the stripped state events
in the [room directory](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms).
The main missing piece seems to be the encryption information, but there may also
be other pieces of information to include.
### Additional ways to access the stripped state
[MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946) proposes including
the stripped state in the spaces summary. Not needing to rationalize what state
can be included for a potential joiner would simplify this (and future) MSCs.
### Additional ways to join a room
[MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083) proposes a new
join rule due to membership in a space. This MSC would clarify that the stripped
state of a room is available to those joiners.
### Dedicated API for accessing the stripped state
Dedicated client-server and server-server APIs could be added to request the
stripped state events, but that is considered out-of-scope for the current
proposal.
This API would allow any potential joiner to query for the stripped state. If
the server does not know the room's state it would need to query other servers
for it.
## Unstable prefix
N/A
## Footnotes
<a id="f1"/>[1]: No changes are proposed to
[the definition of `history_visibility`](https://matrix.org/docs/spec/client_server/latest#room-history-visibility).
The state of a room which is `world_readable` is available to anyone. This somewhat
implies that the stripped state is also available to anyone, regardless of the join
rules, but having a `world_readable`, `invite` room does not seem valuable. [](#a1)
<a id="f2"/>[2]: Privacy conscious deployments may wish to limit the metadata
available to users who are not in a room as the trade-off against user experience.
There seems to be no reason to not allow this. [](#a2)
<a id="f3"/>[3]: As updated in [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772). [](#a3)
<a id="f4"/>[4]: The encryption information (`m.room.encryption`) is already sent
from Synapse and generally seems useful for a user to know before joining a room.
[](#a4)
<a id="f5"/>[5]: The room topic (`m.room.topic`) is included as part of the
[room directory](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-publicrooms)
response for public rooms. It is also planned to be included as part of [MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946)
in the spaces summary response. [](#a5)

@ -0,0 +1,138 @@
# MSC3231: Token Authenticated Registration
Currently, homeserver administrators must choose between allowing anyone to
register and completely disabling registrations. This is a problem for
administrators who want to let certain people have an account on their server,
but do not want to register the accounts manually (possibly because the
initial password may not be changed by the user).
There are some existing external solutions (see the **Alternatives** section),
but these require additional effort from administrators and are disconnected
from Matrix clients. It would be useful for administrators to have a convenient
method of limiting registrations to certain people which requires minimal setup
and is integrated with existing clients.
## Proposal
The [/\_matrix/client/r0/register](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-register)
endpoint uses the [User-Interactive Authentication API](https://matrix.org/docs/spec/client_server/r0.6.1#user-interactive-authentication-api).
A new authentication type `m.login.registration_token` will be defined which requires
a `token` key to be present in the submitted `auth` dict. The token will be a
string of no more than 64 characters, and contain only characters matched by the
regex `[A-Za-z0-9._~-]`.
This will avoid URL encoding issues with the validity checking endpoint, and
prevent DoS attacks from extremely long tokens.
For example, when a client attempts registration with no `auth` dict, a server
may respond with:
```
HTTP/1.1 401 Unauthorized
{
"flows": [
{
"stages": [ "m.login.registration_token" ]
}
],
"params": {},
"session": "xxxxx"
}
```
The client would then prompt the user to enter a token and send a new request
with an `auth` dict:
```
POST /_matrix/client/r0/register
{
"auth": {
"type": "m.login.registration_token",
"token": "fBVFdqVE",
"session": "xxxxx"
}
"device_id": "ABC",
"initial_device_display_name": "Some Client",
"password": "badpassword",
"username": "bob"
}
```
If the server verifies that `fBVFdqVE` is a valid token then the account is
registered as normal assuming all other required auth stages have been completed, otherwise a `401` status is returned. Once registration of
the user has completed, the server may alter the validity of the token.
For example, the token may be completely invalidated, or its number of permitted
uses reduced. Management of the tokens is left to the server implementation.
Using the User-Interactive Authentication API means clients' existing
registration logic will be unaffected, with a fallback available for clients
without native support for the new authentication type.
### Checking the validity of a token
A Client may wish to present username and password inputs only after it has
checked the token is valid.
Clients would be able to check the validity of a token in advance of
registration with a `GET` request to
`/_matrix/client/r0/register/m.login.registration_token/validity`.
This endpoint would take a required `token` query parameter, and validity would be
indicated by the boolean `valid` key in the response.
For example, a client would send:
```
GET /_matrix/client/r0/register/m.login.registration_token/validity?token=abcd
```
If `abcd` is a valid token, the server would respond with:
```
HTTP/1.1 200 OK
{
"valid": true
}
```
This does not perform any actual authentication, and the validity of the token
may change between the check and the User-Interactive Authentication.
This endpoint should be rate limited in order to prevent dictionary attacks.
## Potential issues
The new authentication type would only apply to the
`/_matrix/client/r0/register` endpoint. This should not cause problems, but it
would be worth noting in any change to the specification.
## Alternatives
[matrix-registration](https://github.com/ZerataX/matrix-registration/) is an
application which provides token management and a standalone web interface.
It uses Synapse's admin API to do registrations.
[Midnight](https://github.com/KombuchaPrivacy/midnight) sits in front of a
homeserver and handles the `/_matrix/client/r0/register` endpoint. It provides
token management and does additional checks on the requested username.
Registration requests are forwarded to the homeserver once authenticated.
It uses the User-Interactive Authentication API in a very similar way to this
MSC, which could allow existing Matrix clients to be used.
[matrix-register-bot](https://github.com/krombel/matrix-register-bot) is a
Matrix bot which allows registrations done through the provided web interface
to be accepted or denied by users in a certain room. When a registration is
approved temporary credentials are sent to the user's verified email address.
It can use Synapse's admin API or [matrix-synapse-rest-auth](https://github.com/kamax-matrix/matrix-synapse-rest-password-provider#integrate)
to do the registration.
## Unstable prefix
Implementations should use `org.matrix.msc3231.login.registration_token` as the
authentication type until this MSC has passed FCP and been merged.
Similarly, `/_matrix/client/unstable/org.matrix.msc3231/register/org.matrix.msc3231.login.registration_token/validity`
should be used as the endpoint for checking the validity of a token in advance.

@ -0,0 +1,92 @@
# MSC3267: reference relationships
## Proposal
This proposal defines a relation type (using
[MSC2674 relations](https://github.com/matrix-org/matrix-doc/pull/2674))
for events to make a reference to another event.
A `rel_type` of `m.reference` is defined as a generic way to associate an
event with another event. As a bundle, `m.reference` relations appear as
an object with a single `chunk` field. The `chunk` is an array of objects
with a single `event_id` field for all the child events which `m.reference`
the parent.
There are no implied semantics by a reference relation: the feature or event
type which makes use of the `rel_type` should specify what sort of semantic
behaviour there is, if any. For example, describing that a poll response event
*references* the poll start event, or that a location update *references* a
previous location update.
Reference relations are used by [MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)
to tie all events together for the same verification request.
For instance, an `m.room.message` which references an existing event
would look like:
```json5
{
// Unimportant fields omitted
"type": "m.room.message",
"content": {
"msgtype": "m.text",
"body": "i <3 shelties",
"m.relates_to": {
"rel_type": "m.reference",
"event_id": "$another_event_id"
}
}
}
```
## Server aggregation
[MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674) states
that values for `rel_type` should define how the server should aggregate the
`rel_type`. For `m.reference`, child events are bundled to appear as follows
under `m.relations`:
```json5
{
"m.reference": {
"chunk": [
{"event_id": "$one"},
{"event_id": "$two"},
{"event_id": "$three"}
]
}
}
```
Note that currently only the `event_id` is noted in the chunk, however a future
MSC might add more fields.
## Limitations
Different subtypes of references could be defined through additional fields on
the `m.relates_to` object, to distinguish between other forms of semantic behaviour
independent of type (hypothetical threads, replies, etc if we didn't have a system
for them already). This MSC doesn't attempt to define these subtypes.
This relation cannot be used in conjunction with another relation due to `rel_type`
being a single value. This is known and unfortunately not resolved by this MSC.
A future MSC might address the concern.
## Edge Cases
Can you reference an event which doesn't have logical meaning? Eg to a [reaction](https://github.com/matrix-org/matrix-doc/pull/2677)?
* Yes, at the protocol level. But you shouldn't expect clients to do anything
useful with it.
* The relationship is effectively pointless, so the event would appear as though
there was no reference relationship.
Do we need to support retrospective references?
* For something like "m.duplicate" to retrospectively declare that one event
dupes another, we might need to bundle two-levels deep (subject+ref and then
ref+target). We can cross this bridge when we get there though, as another
aggregation type
## Unstable prefix
Unfortunately not applicable - this MSC was used in production and appears in the
specified version of the [key verification framework](https://spec.matrix.org/v1.2/client-server-api/#key-verification-framework).

@ -0,0 +1,46 @@
# MSC3283: Expose enable_set_displayname, enable_set_avatar_url and enable_3pid_changes in capabilities response
Some home servers like [Synapse](https://github.com/matrix-org/synapse/blob/756fd513dfaebddd28bf783eafa95b4505ce8745/docs/sample_config.yaml#L1207)
can be configured to `enable_set_displayname: false`, `enable_set_avatar_url: false` or `enable_3pid_changes: false`.
To enable clients to handle that gracefully in the UI this setting should be exposed.
## Proposal
The `/_matrix/client/r0/capabilities` endpoint should be decorated to provide more information on capabilities.
```jsonc
{
"capabilities": {
"m.set_displayname": { "enabled": false },
"m.set_avatar_url": { "enabled": false },
"m.3pid_changes": { "enabled": false },
"m.room_versions": {...},
}
}
```
As part of this MSC, a capability for each setting will be added that exposes the server setting:
- `m.set_displayname`
Whether users are allowed to change their displayname after it has been initially set.
Useful when provisioning users based on the contents of a third-party directory.
- `m.set_avatar_url`
Whether users are allowed to change their avatar after it has been initially set.
Useful when provisioning users based on the contents of a third-party directory.
- `m.3pid_changes`
Whether users can change the 3PIDs associated with their accounts
(email address and msisdn).
Useful when provisioning users based on the contents of a third-party directory.
## Client recommendations
When presenting profile settings, clients should use capabilities in order to display the correct UI.
Capability should always be present.
Servers should always send these capabilities. If they aren't (because the server does not support
a new enough spec version or for any other reason), clients should behave as if they were present and set to true.
## Unstable prefix
While this MSC is not considered stable, implementations should use `org.matrix.msc3283.` in place of `m.` throughout this proposal.

@ -0,0 +1,69 @@
# MSC3288: Add room type to `/_matrix/identity/v2/store-invite` API
Currently when inviting via 3pid, the Identity Server receives some information about the room,
like for example the room name and avatar as well as the inviter name.
This allows the identity server to generate a rich email to the invitee.
Now that the matrix spec supports spaces, it would be nice to also provide this information to the identity server
so that the email invite could be customized for spaces. The current implementation would say wrongly that
you are invited to a room when the room is actually a space.
The goal of this proposal is to make 3pid invites space aware.
## Proposal
Homeservers should also send the `room_type` to the identity server when performing a third party invite (__Invitation storage__).
__Proposed change:__
Add a new `room_type` field in json body of [`POST /_matrix/identity/v2/store-invite`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-store-invite):
| Parameter | Type | Description |
|--|--|--|
| room_type | string | The room type for the room to which the user is invited. This should be retrieved from the value of `type` in the `m.room.create` event's `content`. Do not include parameter if `type` is not present in `m.room.create`.
````
POST /_matrix/identity/v2/store-invite HTTP/1.1
Content-Type: application/json
{
"medium": "email",
"address": "foo@example.com",
"room_id": "!something:example.org",
"sender": "@bob:example.com",
"room_alias": "#somewhere:exmaple.org",
"room_avatar_url": "mxc://example.org/s0meM3dia",
"room_join_rules": "public",
"room_name": "The Bob Project",
"room_type": "m.space",
"sender_display_name": "Bob Smith",
"sender_avatar_url": "mxc://example.org/an0th3rM3dia"
}
````
The identity server could then use room type to customize the email depending on the room type.
__Email Generation__
The link in the generated email should also pass over the `room_type` to clients ( like it is doing for
`inviter_name`, `room_name`, `room_avatar`)
## Potential issues
None.
## Security considerations
None.
## Unstable prefix
The following mapping will be used for identifiers in this MSC during development:
Proposed final identifier | Purpose | Development identifier
------------------------------- | ------- | ----
`room_type` | POST body | `org.matrix.msc3288.room_type`

@ -0,0 +1,15 @@
# MSC3289: Room Version 8
A new room version, `8`, is proposed using [room version 7](https://spec.matrix.org/unstable/rooms/v7/)
as a base and incorporating the following MSCs:
* [MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083) - Restricting room
membership based on membership in other rooms
Though other MSCs are capable of being included in this version, they do not have
sufficient implementation to be considered for this room version. A future room
version may include them.
Room version `8` upon being added to the specification shall be considered stable.
No other room versions are affected by this MSC. Before room version `8` can enter
the specification, MSC3083 needs to complete its final comment period.

@ -0,0 +1,51 @@
# Proposal to add timestamp massaging to the spec
Bridges often want to override message timestamps to preserve the timestamps from
the remote network. The spec used to have a concept of [timestamp massaging], but
it was excluded from the release due to not being properly specified. Synapse
still implements it and it is widely used in bridges.
[MSC2716] was originally going to add timestamp massaging to the spec, but it
pivoted to focusing solely on batch sending history. This MSC simply copies the
proposed `ts` query param from the [original MSC2716].
[timestamp massaging]: https://matrix.org/docs/spec/application_service/r0.1.2#timestamp-massaging
[MSC2716]: https://github.com/matrix-org/matrix-doc/pull/2716
[original MSC2716]: https://github.com/matrix-org/matrix-doc/blob/94514392b118dfae8ee6840b13b83d2f8ce8fcfc/proposals/2716-importing-history-into-existing-rooms.md
## Proposal
As per the original version of MSC2716:
> We let the AS API override ('massage') the `origin_server_ts` timestamp
> applied to sent events. We do this by adding a `ts` querystring parameter on
> the `PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}`
> and `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}`
> endpoints, specifying the value to apply to `origin_server_ts` on the event
> (UNIX epoch milliseconds).
>
> We consciously don't support the `ts` parameter on the various helper
> syntactic-sugar APIs like /kick and /ban. If a bridge/bot is smart enough to
> be faking history, it is already in the business of dealing with raw events,
> and should not be using the syntactic sugar APIs.
The spec should also make it clear that the `ts` query param won't affect DAG
ordering, and MSC2716's batch sending should be used when the intention is to
insert history somewhere else than the end of the room.
## Potential issues
None.
## Alternatives
The new MSC2716 could technically be considered an alternative, but it can only
be used for history, while this proposal also supports overriding timestamps of
new messages. In practice, bridges will likely use both: Batch sending for
filling history and timestamp massaging for new messages.
## Security considerations
Timestamps should already be considered untrusted over federation, and
application services are trusted server components, so allowing appservices
to override timestamps does not create any new security considerations.
## Unstable prefix
`org.matrix.msc3316.ts` may be used as the query parameter. However, the `ts`
parameter is already used in production for the `/send` endpoint, which means
the unstable prefix should only be used for the `/state` endpoint.

@ -1,24 +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
example: {
"example.com": {
"ed25519:0": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus"
}
}
additionalProperties:
type: object
title: Server Signatures
additionalProperties:
type: string

@ -98,10 +98,9 @@ for filename in os.listdir(cs_api_dir):
path = (basePath + path).replace('%CLIENT_MAJOR_VERSION%',
major_version)
for method, spec in methods.items():
if "tags" in spec.keys():
if path not in output["paths"]:
output["paths"][path] = {}
output["paths"][path][method] = spec
if path not in output["paths"]:
output["paths"][path] = {}
output["paths"][path][method] = spec
print("Generating %s" % output_file)

@ -22,9 +22,9 @@ python3 download_google_fonts_css.py \
Which would pop out a `Inter.css` file that should be `@import url("Inter.css")`d
somewhere in the site's SCSS (currently in
[/assets-hugo/scss/_variables_project.scss](/assets-hugo/scss/_variables_project.scss)).
[/assets/scss/_variables_project.scss](/assets/scss/_variables_project.scss)).
Re-running the script and committing any new files is only necessary when a desired
font updates (not very often), or we want to change the font we're using. In that case,
remove the existing font files at `/static/fonts/*.woff2` and re-run the script with a
different URL.
different URL.

Loading…
Cancel
Save