diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml
index 6e5951207f9..b73542fe344 100644
--- a/.buildkite/pipeline.yaml
+++ b/.buildkite/pipeline.yaml
@@ -1,16 +1,39 @@
steps:
- - label: ":books: Build spec"
+ - label: ":snake: Build swagger definitions for matrix.org"
command:
- - python3 -m venv env
- - env/bin/pip install -r scripts/requirements.txt
- - ". env/bin/activate; scripts/generate-matrix-org-assets"
+ # Install the python dependencies necessary to build the spec
+ - python3 -m venv env && . env/bin/activate
+ - pip install -r scripts/requirements.txt
+ # Build the spec
+ - scripts/generate-matrix-org-assets
artifact_paths:
- assets.tar.gz
plugins:
- - docker#v3.0.1:
- image: "python:3.6"
+ - docker#v3.7.0:
+ image: python:3.9
- label: "rebuild matrix.org"
trigger: "matrix-dot-org"
async: true
branches: "master"
+
+ - label: ":books: Build the spec"
+ command:
+ # Install package dependencies
+ - apk add nodejs npm git hugo
+ # Install the node dependencies necessary to build the spec
+ - npm i
+ # Pull all git submodules, required for the hugo theme
+ - git submodule update --init --recursive
+ # Pull current proposal information
+ - npm run get-proposals
+ # Build the spec, will build to './spec'
+ # Set the baseURL as we're deploying to https://spec.matrix.org/unstable
+ - hugo --baseURL "/unstable" -d "spec"
+ # Compress the result and make it available as an artifact
+ - tar -czf spec.tar.gz spec
+ artifact_paths:
+ - spec.tar.gz
+ plugins:
+ - docker#v3.7.0:
+ image: alpine
\ No newline at end of file
diff --git a/.circleci/config.yml b/.circleci/config.yml
index bf4404ce95f..e0815d7623b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,13 +1,19 @@
gendoc: &gendoc
name: Generate the docs
+ # Note: Node dependencies are required for the hugo build.
+ # Note: We use a custom config file for circleci due to some specifics with hosting the
+ # site using CircleCI's artifacts platform. See config-circleci.toml for details.
command: |
- source /env/bin/activate
- scripts/gendoc.py
+ apk add nodejs npm git hugo
+ npm i
+ cat config-circleci.toml config.toml > hugo-config.toml
+ hugo --config hugo-config.toml --baseURL "/${CIRCLE_NODE_INDEX}/public"
genswagger: &genswagger
- name: Generate the swagger
+ name: Validate sources and generate swagger json
command: |
source /env/bin/activate
+ scripts/check-swagger-sources.py
scripts/dump-swagger.py
buildswaggerui: &buildswaggerui
@@ -27,23 +33,14 @@ checkexamples: &checkexamples
name: Check Event Examples
command: |
source /env/bin/activate
- cd event-schemas
- ./check_examples.py
- cd ../api
- ./check_examples.py
-
-genmatrixassets: &genmatrixassets
- name: Generate/Verify matrix.org assets
- command: |
- source /env/bin/activate
- ./scripts/generate-matrix-org-assets
+ scripts/check-event-schema-examples.py
validateapi: &validateapi
name: Validate OpenAPI specifications
command: |
- cd api
+ cd scripts
npm install
- node validator.js -s "client-server"
+ node validator.js -s "../data/api/client-server"
buildspeculator: &buildspeculator
name: Build Speculator
@@ -51,12 +48,6 @@ buildspeculator: &buildspeculator
cd scripts/speculator
go build -v
-buildcontinuserv: &buildcontinuserv
- name: Build Continuserv
- command: |
- cd scripts/continuserv
- go build -v
-
version: 2
jobs:
validate-docs:
@@ -71,18 +62,21 @@ jobs:
steps:
- checkout
- run: *checkexamples
- - run: *genmatrixassets # We don't actually use the assets, but we do want to make sure they build
build-docs:
docker:
- - image: uhoreg/matrix-doc-build
+ - image: alpine
steps:
+ # Note: We install git in the image so we can pull git submodules. The hugo theme in use
+ # is a git submodule, which has its own submodules, and all need to be loaded.
+ - run: apk add git
- checkout
+ - run: git submodule update --init --recursive
- run: *gendoc
- store_artifacts:
- path: scripts/gen
+ path: public
- run:
name: "Doc build is available at:"
- command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/scripts/gen/index.html"; echo $DOCS_URL
+ command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/public/index.html"; echo $DOCS_URL
build-swagger:
docker:
- image: uhoreg/matrix-doc-build
@@ -104,8 +98,6 @@ jobs:
name: Install Dependencies
command: |
go get -v github.com/hashicorp/golang-lru
- go get -v gopkg.in/fsnotify/fsnotify.v1
- - run: *buildcontinuserv
- run: *buildspeculator
workflows:
diff --git a/.github/ISSUE_TEMPLATE/clarification.md b/.github/ISSUE_TEMPLATE/clarification.md
new file mode 100644
index 00000000000..1aaa35c6123
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/clarification.md
@@ -0,0 +1,13 @@
+---
+name: Clarity problem
+about: Report an area of the spec that is unclear.
+title: ''
+labels: 'clarification'
+assignees: ''
+
+---
+
+**Link to problem area**:
+
+**Issue**
+What is wrong? How can we improve?
diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml
new file mode 100644
index 00000000000..79bc995d473
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yaml
@@ -0,0 +1,8 @@
+blank_issues_enabled: true
+contact_links:
+ - name: Matrix Spec Discussion
+ url: "https://matrix.to/#/#matrix-spec:matrix.org"
+ about: Questions about the spec and proposal process can be asked here.
+ - name: Matrix Security Policy
+ url: https://www.matrix.org/security-disclosure-policy/
+ about: Learn more about our security disclosure policy.
diff --git a/.github/ISSUE_TEMPLATE/cosmetic-bug.md b/.github/ISSUE_TEMPLATE/cosmetic-bug.md
new file mode 100644
index 00000000000..1012302b26c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/cosmetic-bug.md
@@ -0,0 +1,13 @@
+---
+name: Cosmetic issue
+about: Report an issue with how the spec looks.
+title: ''
+labels: 'aesthetic'
+assignees: ''
+
+---
+
+**Link to problem area**:
+
+**Issue**
+What is wrong? What can we do to improve?
diff --git a/.github/ISSUE_TEMPLATE/idea.md b/.github/ISSUE_TEMPLATE/idea.md
new file mode 100644
index 00000000000..028012a576c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/idea.md
@@ -0,0 +1,12 @@
+---
+name: Spec idea
+about: Suggest a future MSC idea.
+title: ''
+labels: 'improvement'
+assignees: ''
+
+---
+
+**Suggestion**
+What would you like to see in Matrix? If your idea is vaguely complete enough, we
+recommend submitting [an MSC](https://matrix.org/docs/spec/proposals) instead.
diff --git a/.github/ISSUE_TEMPLATE/spec-bug.md b/.github/ISSUE_TEMPLATE/spec-bug.md
new file mode 100644
index 00000000000..590234cac16
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/spec-bug.md
@@ -0,0 +1,16 @@
+---
+name: Documentation error
+about: Report an issue with the spec itself (incorrect text).
+title: ''
+labels: 'spec-bug'
+assignees: ''
+
+---
+
+**Link to problem area**:
+
+**Issue**
+What is wrong?
+
+**Expected behaviour**
+How can the issue be fixed? Links to implementations/documents which prove the spec is wrong are appreciated.
diff --git a/.gitignore b/.gitignore
index 9cc27b85582..a0c5cef073b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,20 @@
/api/node_modules
-/assets
/assets.tar.gz
+/data/msc
/env*
+/node_modules
+/resources
/scripts/gen
/scripts/continuserv/continuserv
/scripts/speculator/speculator
/scripts/swagger
/scripts/tmp
/templating/out
+/hugo-config.toml
+/public
*.pyc
*.swp
_rendered.rst
/.vscode/
/.idea/
+/spec/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000000..5e606ab20e5
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "themes/docsy"]
+ path = themes/docsy
+ url = https://github.com/matrix-org/docsy.git
+ branch = master
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 2d26e8a8ca7..2e72e1860d7 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -29,9 +29,7 @@ some time to complete.
Changes to the protocol (new endpoints, ideas, etc) need to go through the
`proposals process `_. Other changes,
such as fixing bugs, typos, or clarifying existing behaviour do not need a proposal.
-If you're not sure, visit us at `#matrix-spec:matrix.org`_
-and ask.
-
+If you're not sure, visit us at `#matrix-spec:matrix.org`_ and ask.
Other changes
~~~~~~~~~~~~~
@@ -64,12 +62,17 @@ following:
to fix. On the other hand, introducing new behaviour is best represented by a
proposal.
+* Design or aesthetic changes, such as improving accessibility, colour schemes,
+ etc. Please check in with us at `#matrix-docs:matrix.org`_ with your proposed
+ design change before opening a PR so we can work with you on it.
+
For such changes, please do just open a `pull request`_. If you're not sure if
your change is covered by the above, please visit `#matrix-spec:matrix.org` and
ask.
.. _`pull request`: https://help.github.com/articles/about-pull-requests
.. _`#matrix-spec:matrix.org`: https://matrix.to/#/#matrix-spec:matrix.org
+.. _`#matrix-docs:matrix.org`: https://matrix.to/#/#matrix-docs:matrix.org
Adding to the changelog
@@ -86,8 +89,8 @@ To create a changelog entry, create a file named in the format ``prNumber.type``
the ``newsfragments`` directory. The ``type`` can be one of the following:
* ``new`` - Used when adding new endpoints. Please have the file contents be the
- method and route being added, surrounded in RST code tags. For example: ``POST
- /accounts/whoami``
+ method and route being added, surrounded in markdown code tags. For example: \`POST
+ /accounts/whoami\`.
* ``feature`` - Used when adding backwards-compatible changes to the API.
@@ -100,8 +103,7 @@ the ``newsfragments`` directory. The ``type`` can be one of the following:
All news fragments must have a brief summary explaining the change in the
contents of the file. The summary must end in a full stop to be in line with
-the style guide and and formatting must be done using `Restructured Text
-`_.
+the style guide and formatting must be done using Markdown.
Changes that do not change the spec, such as changes to the build script, formatting,
CSS, etc should not get a news fragment.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000000..75f55affaac
--- /dev/null
+++ b/README.md
@@ -0,0 +1,102 @@
+# Matrix Specification
+
+This repository contains the Matrix Specification, rendered at [spec.matrix.org](http://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).
+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/).
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ defined here are available in templates. All these directories above are configurable via `config.toml` settings.
+
+Additionally, the following directories may be of interest:
+
+* `/attic`: Here contains historical sections of specification and legacy drafts for the specification.
+* `/changelogs`: Various bits of changelog for the specification areas.
+* `/data-definitions`: Bits of structured data consumable by Matrix implementations.
+* `/meta`: Documentation relating to the spec's processes that are otherwise untracked (release instructions, etc).
+* `/scripts`: Various scripts for generating the spec and validating its contents.
+* `/proposals`: Matrix Spec Change (MSC) proposals. See .
+
+## Authoring changes to the spec
+
+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:
+ . 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` (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
+[#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"`
+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,
+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 .
+* You can host the swagger UI yourself. See for advice on how to
+ do so.
+
+## Issue tracking
+
+Specification issues are tracked on github at .
+
+See [meta/github-labels.rst](./meta/github-labels.rst) for information on what the labels mean.
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 61c27f15079..00000000000
--- a/README.rst
+++ /dev/null
@@ -1,143 +0,0 @@
-This repository contains the Matrix specification.
-
-If you want to ask more about the specification, join us on
-`#matrix-dev:matrix.org `_.
-
-We welcome contributions to the spec! See the notes below on `Building the
-specification`_, and ``_ to get started making contributions.
-
-Note that the Matrix Project lists, which were previously kept in this
-repository, are now in https://github.com/matrix-org/matrix.org.
-
-Structure of this repository
-============================
-
-- ``api`` : `OpenAPI`_ (swagger) specifications for the the HTTP APIs.
-- ``attic``: historical sections of specification for reference
- purposes.
-- ``changelogs``: change logs for the various parts of the
- specification.
-- ``drafts``: Previously, contained documents which were under discussion for
- future incusion into the specification and/or supporting documentation. This
- is now historical, as we use separate discussion documents (see
- ``_).
-- ``event-schemas``: the `JSON Schema`_ for all Matrix events
- contained in the specification, along with example JSON files.
-- ``meta``: documents outlining the processes involved when writing
- documents, e.g. documentation style, guidelines.
-- ``scripts``: scripts to generate formatted versions of the
- documentation, typically HTML.
-- ``specification``: the specification split up into sections.
-
-.. _OpenAPI: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
-.. _JSON Schema: http://json-schema.org/
-
-Building the specification
-==========================
-
-The Matrix Spec is generated by a set of scripts, from the RST documents, API
-specs and event schemas in this repository.
-
-Preparation
------------
-
-To use the scripts, it is best to create a Python 3.4+ virtualenv as follows::
-
- virtualenv -p python3 env
- env/bin/pip install -r scripts/requirements.txt
-
-(Benjamin Saunders has contributed a script for `Nix`_ users, which can be
-invoked with ``nix-shell scripts/contrib/shell.nix``.)
-
-.. TODO: Possibly we need some libs installed; should record what they are.
-
-.. _`Nix`: https://nixos.org/nix/
-
-Generating the specification
-----------------------------
-
-To rebuild the specification, use ``scripts/gendoc.py``::
-
- source env/bin/activate
- ./scripts/gendoc.py
-
-The above will write the rendered version of the specification to
-``scripts/gen``. To view it, point your browser at ``scripts/gen/index.html``.
-
-Windows users
-~~~~~~~~~~~~~
-The ``source`` program does not exist on Windows, so instead run one of the
-``activate`` files in ``.\env\Scripts\`` to activate the virtual environment.
-
-If you're on Windows Vista or higher, be sure that the "Symbolic Links"
-option was selected when installing Git prior to cloning this repository. If
-you're still seeing errors about files not being found it is likely because
-the symlink at ``api/client-server/definitions/event-schemas`` looks like a
-file. To correct the problem, open an Administrative/Elevated Command Prompt in your
-cloned matrix-doc directory and run the following::
-
- cd api\client-server\definitions
- del event-schemas
- mklink /D event-schemas "..\..\..\event-schemas"
-
-This will delete the file and replace it with a symlink. Git should not detect
-this as a change, and you should be able to go back to building the project.
-
-Generating the OpenAPI (Swagger) specs
---------------------------------------
-
-`Swagger`_ is a framework for representing RESTful APIs. We use it to generate
-interactive documentation for our APIs.
-
-Before the Swagger docs can be used in the Swagger UI (or other tool expecting
-a Swagger specs, they must be combined into a single json file. This can be
-done as follows::
-
- source env/bin/activate
- ./scripts/dump-swagger.py
-
-By default, ``dump-swagger`` will write 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
- 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 do
- so.
-
-.. _`Swagger`: http://swagger.io/
-
-Continuserv
------------
-
-Continuserv is a script which will rebuild the specification every time a file
-is changed, and will serve it to a browser over HTTP. It is intended for use by
-specification authors, so that they can quickly see the effects of their
-changes.
-
-It is written in Go, so you will need the ``go`` compiler installed on your
-computer. You will also need to install fsnotify by running::
-
- go get gopkg.in/fsnotify/fsnotify.v1
-
-Then, create a virtualenv as described above under `Preparation`_,
-and::
-
- source env/bin/activate
- go run ./scripts/continuserv/main.go
-
-You will then be able to view the generated spec by visiting
-http://localhost:8000/index.html.
-
-Issue tracking
-==============
-
-Issues with the Matrix specification are tracked in `GitHub
-`_.
-
-See `meta/github-labels.rst `_ for notes on what the labels mean.
diff --git a/api/README b/api/README
deleted file mode 100644
index 7b971fac956..00000000000
--- a/api/README
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory contains swagger-compatible representations of our APIs. See
-the main README.rst for details on how to make use of them.
diff --git a/api/client-server/definitions/event-schemas b/api/client-server/definitions/event-schemas
deleted file mode 120000
index 376cf90c03c..00000000000
--- a/api/client-server/definitions/event-schemas
+++ /dev/null
@@ -1 +0,0 @@
-../../../event-schemas
\ No newline at end of file
diff --git a/api/client-server/sso_login_redirect.yaml b/api/client-server/sso_login_redirect.yaml
deleted file mode 100644
index acbafc57892..00000000000
--- a/api/client-server/sso_login_redirect.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2019 New Vector Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-swagger: '2.0'
-info:
- title: "Matrix Client-Server SSO Login API"
- version: "1.0.0"
-host: localhost:8008
-schemes:
- - https
- - http
-basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
-paths:
- "/login/sso/redirect":
- get:
- summary: Redirect the user's browser to the SSO interface.
- description: |-
- A web-based Matrix client should instruct the user's browser to
- navigate to this endpoint in order to log in via SSO.
-
- The server MUST respond with an HTTP redirect to the SSO interface.
- operationId: redirectToSSO
- parameters:
- - in: query
- type: string
- name: redirectUrl
- description: |-
- URI to which the user will be redirected after the homeserver has
- authenticated the user with SSO.
- required: true
- responses:
- 302:
- description: A redirect to the SSO interface.
- headers:
- Location:
- type: "string"
diff --git a/api/files/backbone-min.js b/api/files/backbone-min.js
deleted file mode 100644
index c1c0d4fff28..00000000000
--- a/api/files/backbone-min.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Backbone.js 0.9.2
-
-// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
-// Backbone may be freely distributed under the MIT license.
-// For all details and documentation:
-// http://backbonejs.org
-(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
-{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
-z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
-{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
-b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
-b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
-a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
-h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
-return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
-{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
-!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
-this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c=b))this.iframe=i('').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
-this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
-stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
-function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
-this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
-f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
-for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b= 1.0.0'
-};
-
-Handlebars.helpers = {};
-Handlebars.partials = {};
-
-var toString = Object.prototype.toString,
- functionType = '[object Function]',
- objectType = '[object Object]';
-
-Handlebars.registerHelper = function(name, fn, inverse) {
- if (toString.call(name) === objectType) {
- if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
- Handlebars.Utils.extend(this.helpers, name);
- } else {
- if (inverse) { fn.not = inverse; }
- this.helpers[name] = fn;
- }
-};
-
-Handlebars.registerPartial = function(name, str) {
- if (toString.call(name) === objectType) {
- Handlebars.Utils.extend(this.partials, name);
- } else {
- this.partials[name] = str;
- }
-};
-
-Handlebars.registerHelper('helperMissing', function(arg) {
- if(arguments.length === 2) {
- return undefined;
- } else {
- throw new Error("Missing helper: '" + arg + "'");
- }
-});
-
-Handlebars.registerHelper('blockHelperMissing', function(context, options) {
- var inverse = options.inverse || function() {}, fn = options.fn;
-
- var type = toString.call(context);
-
- if(type === functionType) { context = context.call(this); }
-
- if(context === true) {
- return fn(this);
- } else if(context === false || context == null) {
- return inverse(this);
- } else if(type === "[object Array]") {
- if(context.length > 0) {
- return Handlebars.helpers.each(context, options);
- } else {
- return inverse(this);
- }
- } else {
- return fn(context);
- }
-});
-
-Handlebars.K = function() {};
-
-Handlebars.createFrame = Object.create || function(object) {
- Handlebars.K.prototype = object;
- var obj = new Handlebars.K();
- Handlebars.K.prototype = null;
- return obj;
-};
-
-Handlebars.logger = {
- DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
-
- methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
-
- // can be overridden in the host environment
- log: function(level, obj) {
- if (Handlebars.logger.level <= level) {
- var method = Handlebars.logger.methodMap[level];
- if (typeof console !== 'undefined' && console[method]) {
- console[method].call(console, obj);
- }
- }
- }
-};
-
-Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
-
-Handlebars.registerHelper('each', function(context, options) {
- var fn = options.fn, inverse = options.inverse;
- var i = 0, ret = "", data;
-
- var type = toString.call(context);
- if(type === functionType) { context = context.call(this); }
-
- if (options.data) {
- data = Handlebars.createFrame(options.data);
- }
-
- if(context && typeof context === 'object') {
- if(context instanceof Array){
- for(var j = context.length; i 2) {
- expected.push("'" + this.terminals_[p] + "'");
- }
- if (this.lexer.showPosition) {
- errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
- } else {
- errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
- }
- this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
- }
- }
- if (action[0] instanceof Array && action.length > 1) {
- throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
- }
- switch (action[0]) {
- case 1:
- stack.push(symbol);
- vstack.push(this.lexer.yytext);
- lstack.push(this.lexer.yylloc);
- stack.push(action[1]);
- symbol = null;
- if (!preErrorSymbol) {
- yyleng = this.lexer.yyleng;
- yytext = this.lexer.yytext;
- yylineno = this.lexer.yylineno;
- yyloc = this.lexer.yylloc;
- if (recovering > 0)
- recovering--;
- } else {
- symbol = preErrorSymbol;
- preErrorSymbol = null;
- }
- break;
- case 2:
- len = this.productions_[action[1]][1];
- yyval.$ = vstack[vstack.length - len];
- yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
- if (ranges) {
- yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
- }
- r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
- if (typeof r !== "undefined") {
- return r;
- }
- if (len) {
- stack = stack.slice(0, -1 * len * 2);
- vstack = vstack.slice(0, -1 * len);
- lstack = lstack.slice(0, -1 * len);
- }
- stack.push(this.productions_[action[1]][0]);
- vstack.push(yyval.$);
- lstack.push(yyval._$);
- newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
- stack.push(newState);
- break;
- case 3:
- return true;
- }
- }
- return true;
-}
-};
-/* Jison generated lexer */
-var lexer = (function(){
-var lexer = ({EOF:1,
-parseError:function parseError(str, hash) {
- if (this.yy.parser) {
- this.yy.parser.parseError(str, hash);
- } else {
- throw new Error(str);
- }
- },
-setInput:function (input) {
- this._input = input;
- this._more = this._less = this.done = false;
- this.yylineno = this.yyleng = 0;
- this.yytext = this.matched = this.match = '';
- this.conditionStack = ['INITIAL'];
- this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
- if (this.options.ranges) this.yylloc.range = [0,0];
- this.offset = 0;
- return this;
- },
-input:function () {
- var ch = this._input[0];
- this.yytext += ch;
- this.yyleng++;
- this.offset++;
- this.match += ch;
- this.matched += ch;
- var lines = ch.match(/(?:\r\n?|\n).*/g);
- if (lines) {
- this.yylineno++;
- this.yylloc.last_line++;
- } else {
- this.yylloc.last_column++;
- }
- if (this.options.ranges) this.yylloc.range[1]++;
-
- this._input = this._input.slice(1);
- return ch;
- },
-unput:function (ch) {
- var len = ch.length;
- var lines = ch.split(/(?:\r\n?|\n)/g);
-
- this._input = ch + this._input;
- this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
- //this.yyleng -= len;
- this.offset -= len;
- var oldLines = this.match.split(/(?:\r\n?|\n)/g);
- this.match = this.match.substr(0, this.match.length-1);
- this.matched = this.matched.substr(0, this.matched.length-1);
-
- if (lines.length-1) this.yylineno -= lines.length-1;
- var r = this.yylloc.range;
-
- this.yylloc = {first_line: this.yylloc.first_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.first_column,
- last_column: lines ?
- (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
- this.yylloc.first_column - len
- };
-
- if (this.options.ranges) {
- this.yylloc.range = [r[0], r[0] + this.yyleng - len];
- }
- return this;
- },
-more:function () {
- this._more = true;
- return this;
- },
-less:function (n) {
- this.unput(this.match.slice(n));
- },
-pastInput:function () {
- var past = this.matched.substr(0, this.matched.length - this.match.length);
- return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
- },
-upcomingInput:function () {
- var next = this.match;
- if (next.length < 20) {
- next += this._input.substr(0, 20-next.length);
- }
- return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
- },
-showPosition:function () {
- var pre = this.pastInput();
- var c = new Array(pre.length + 1).join("-");
- return pre + this.upcomingInput() + "\n" + c+"^";
- },
-next:function () {
- if (this.done) {
- return this.EOF;
- }
- if (!this._input) this.done = true;
-
- var token,
- match,
- tempMatch,
- index,
- col,
- lines;
- if (!this._more) {
- this.yytext = '';
- this.match = '';
- }
- var rules = this._currentRules();
- for (var i=0;i < rules.length; i++) {
- tempMatch = this._input.match(this.rules[rules[i]]);
- if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
- match = tempMatch;
- index = i;
- if (!this.options.flex) break;
- }
- }
- if (match) {
- lines = match[0].match(/(?:\r\n?|\n).*/g);
- if (lines) this.yylineno += lines.length;
- this.yylloc = {first_line: this.yylloc.last_line,
- last_line: this.yylineno+1,
- first_column: this.yylloc.last_column,
- last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
- this.yytext += match[0];
- this.match += match[0];
- this.matches = match;
- this.yyleng = this.yytext.length;
- if (this.options.ranges) {
- this.yylloc.range = [this.offset, this.offset += this.yyleng];
- }
- this._more = false;
- this._input = this._input.slice(match[0].length);
- this.matched += match[0];
- token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
- if (this.done && this._input) this.done = false;
- if (token) return token;
- else return;
- }
- if (this._input === "") {
- return this.EOF;
- } else {
- return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
- {text: "", token: null, line: this.yylineno});
- }
- },
-lex:function lex() {
- var r = this.next();
- if (typeof r !== 'undefined') {
- return r;
- } else {
- return this.lex();
- }
- },
-begin:function begin(condition) {
- this.conditionStack.push(condition);
- },
-popState:function popState() {
- return this.conditionStack.pop();
- },
-_currentRules:function _currentRules() {
- return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
- },
-topState:function () {
- return this.conditionStack[this.conditionStack.length-2];
- },
-pushState:function begin(condition) {
- this.begin(condition);
- }});
-lexer.options = {};
-lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
-
-var YYSTATE=YY_START
-switch($avoiding_name_collisions) {
-case 0: yy_.yytext = "\\"; return 14;
-break;
-case 1:
- if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
- if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
- if(yy_.yytext) return 14;
-
-break;
-case 2: return 14;
-break;
-case 3:
- if(yy_.yytext.slice(-1) !== "\\") this.popState();
- if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
- return 14;
-
-break;
-case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
-break;
-case 5: return 25;
-break;
-case 6: return 16;
-break;
-case 7: return 20;
-break;
-case 8: return 19;
-break;
-case 9: return 19;
-break;
-case 10: return 23;
-break;
-case 11: return 22;
-break;
-case 12: this.popState(); this.begin('com');
-break;
-case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
-break;
-case 14: return 22;
-break;
-case 15: return 37;
-break;
-case 16: return 36;
-break;
-case 17: return 36;
-break;
-case 18: return 40;
-break;
-case 19: /*ignore whitespace*/
-break;
-case 20: this.popState(); return 24;
-break;
-case 21: this.popState(); return 18;
-break;
-case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 31;
-break;
-case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 31;
-break;
-case 24: return 38;
-break;
-case 25: return 33;
-break;
-case 26: return 33;
-break;
-case 27: return 32;
-break;
-case 28: return 36;
-break;
-case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 36;
-break;
-case 30: return 'INVALID';
-break;
-case 31: return 5;
-break;
-}
-};
-lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}\/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
-lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"INITIAL":{"rules":[0,1,2,31],"inclusive":true}};
-return lexer;})()
-parser.lexer = lexer;
-function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
-return new Parser;
-})();;
-// lib/handlebars/compiler/base.js
-
-Handlebars.Parser = handlebars;
-
-Handlebars.parse = function(input) {
-
- // Just return if an already-compile AST was passed in.
- if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
-
- Handlebars.Parser.yy = Handlebars.AST;
- return Handlebars.Parser.parse(input);
-};
-;
-// lib/handlebars/compiler/ast.js
-Handlebars.AST = {};
-
-Handlebars.AST.ProgramNode = function(statements, inverse) {
- this.type = "program";
- this.statements = statements;
- if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
-};
-
-Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
- this.type = "mustache";
- this.escaped = !unescaped;
- this.hash = hash;
-
- var id = this.id = rawParams[0];
- var params = this.params = rawParams.slice(1);
-
- // a mustache is an eligible helper if:
- // * its id is simple (a single part, not `this` or `..`)
- var eligibleHelper = this.eligibleHelper = id.isSimple;
-
- // a mustache is definitely a helper if:
- // * it is an eligible helper, and
- // * it has at least one parameter or hash segment
- this.isHelper = eligibleHelper && (params.length || hash);
-
- // if a mustache is an eligible helper but not a definite
- // helper, it is ambiguous, and will be resolved in a later
- // pass or at runtime.
-};
-
-Handlebars.AST.PartialNode = function(partialName, context) {
- this.type = "partial";
- this.partialName = partialName;
- this.context = context;
-};
-
-Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
- var verifyMatch = function(open, close) {
- if(open.original !== close.original) {
- throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
- }
- };
-
- verifyMatch(mustache.id, close);
- this.type = "block";
- this.mustache = mustache;
- this.program = program;
- this.inverse = inverse;
-
- if (this.inverse && !this.program) {
- this.isInverse = true;
- }
-};
-
-Handlebars.AST.ContentNode = function(string) {
- this.type = "content";
- this.string = string;
-};
-
-Handlebars.AST.HashNode = function(pairs) {
- this.type = "hash";
- this.pairs = pairs;
-};
-
-Handlebars.AST.IdNode = function(parts) {
- this.type = "ID";
-
- var original = "",
- dig = [],
- depth = 0;
-
- for(var i=0,l=parts.length; i 0) { throw new Handlebars.Exception("Invalid path: " + original); }
- else if (part === "..") { depth++; }
- else { this.isScoped = true; }
- }
- else { dig.push(part); }
- }
-
- this.original = original;
- this.parts = dig;
- this.string = dig.join('.');
- this.depth = depth;
-
- // an ID is simple if it only has one part, and that part is not
- // `..` or `this`.
- this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
-
- this.stringModeValue = this.string;
-};
-
-Handlebars.AST.PartialNameNode = function(name) {
- this.type = "PARTIAL_NAME";
- this.name = name.original;
-};
-
-Handlebars.AST.DataNode = function(id) {
- this.type = "DATA";
- this.id = id;
-};
-
-Handlebars.AST.StringNode = function(string) {
- this.type = "STRING";
- this.original =
- this.string =
- this.stringModeValue = string;
-};
-
-Handlebars.AST.IntegerNode = function(integer) {
- this.type = "INTEGER";
- this.original =
- this.integer = integer;
- this.stringModeValue = Number(integer);
-};
-
-Handlebars.AST.BooleanNode = function(bool) {
- this.type = "BOOLEAN";
- this.bool = bool;
- this.stringModeValue = bool === "true";
-};
-
-Handlebars.AST.CommentNode = function(comment) {
- this.type = "comment";
- this.comment = comment;
-};
-;
-// lib/handlebars/utils.js
-
-var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-
-Handlebars.Exception = function(message) {
- var tmp = Error.prototype.constructor.apply(this, arguments);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-};
-Handlebars.Exception.prototype = new Error();
-
-// Build out our basic SafeString type
-Handlebars.SafeString = function(string) {
- this.string = string;
-};
-Handlebars.SafeString.prototype.toString = function() {
- return this.string.toString();
-};
-
-var escape = {
- "&": "&",
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "`": "`"
-};
-
-var badChars = /[&<>"'`]/g;
-var possible = /[&<>"'`]/;
-
-var escapeChar = function(chr) {
- return escape[chr] || "&";
-};
-
-Handlebars.Utils = {
- extend: function(obj, value) {
- for(var key in value) {
- if(value.hasOwnProperty(key)) {
- obj[key] = value[key];
- }
- }
- },
-
- escapeExpression: function(string) {
- // don't escape SafeStrings, since they're already safe
- if (string instanceof Handlebars.SafeString) {
- return string.toString();
- } else if (string == null || string === false) {
- return "";
- }
-
- // Force a string conversion as this will be done by the append regardless and
- // the regex test will do this transparently behind the scenes, causing issues if
- // an object's to string has escaped characters in it.
- string = string.toString();
-
- if(!possible.test(string)) { return string; }
- return string.replace(badChars, escapeChar);
- },
-
- isEmpty: function(value) {
- if (!value && value !== 0) {
- return true;
- } else if(toString.call(value) === "[object Array]" && value.length === 0) {
- return true;
- } else {
- return false;
- }
- }
-};
-;
-// lib/handlebars/compiler/compiler.js
-
-/*jshint eqnull:true*/
-var Compiler = Handlebars.Compiler = function() {};
-var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {};
-
-// the foundHelper register will disambiguate helper lookup from finding a
-// function in a context. This is necessary for mustache compatibility, which
-// requires that context functions in blocks are evaluated by blockHelperMissing,
-// and then proceed as if the resulting value was provided to blockHelperMissing.
-
-Compiler.prototype = {
- compiler: Compiler,
-
- disassemble: function() {
- var opcodes = this.opcodes, opcode, out = [], params, param;
-
- for (var i=0, l=opcodes.length; i 0) {
- this.source[1] = this.source[1] + ", " + locals.join(", ");
- }
-
- // Generate minimizer alias mappings
- if (!this.isChild) {
- for (var alias in this.context.aliases) {
- if (this.context.aliases.hasOwnProperty(alias)) {
- this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
- }
- }
- }
-
- if (this.source[1]) {
- this.source[1] = "var " + this.source[1].substring(2) + ";";
- }
-
- // Merge children
- if (!this.isChild) {
- this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
- }
-
- if (!this.environment.isSimple) {
- this.source.push("return buffer;");
- }
-
- var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
-
- for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
- return this.topStackName();
- },
- topStackName: function() {
- return "stack" + this.stackSlot;
- },
- flushInline: function() {
- var inlineStack = this.inlineStack;
- if (inlineStack.length) {
- this.inlineStack = [];
- for (var i = 0, len = inlineStack.length; i < len; i++) {
- var entry = inlineStack[i];
- if (entry instanceof Literal) {
- this.compileStack.push(entry);
- } else {
- this.pushStack(entry);
- }
- }
- }
- },
- isInline: function() {
- return this.inlineStack.length;
- },
-
- popStack: function(wrapped) {
- var inline = this.isInline(),
- item = (inline ? this.inlineStack : this.compileStack).pop();
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- if (!inline) {
- this.stackSlot--;
- }
- return item;
- }
- },
-
- topStack: function(wrapped) {
- var stack = (this.isInline() ? this.inlineStack : this.compileStack),
- item = stack[stack.length - 1];
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- return item;
- }
- },
-
- quotedString: function(str) {
- return '"' + str
- .replace(/\\/g, '\\\\')
- .replace(/"/g, '\\"')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
- .replace(/\u2029/g, '\\u2029') + '"';
- },
-
- setupHelper: function(paramSize, name, missingParams) {
- var params = [];
- this.setupParams(paramSize, params, missingParams);
- var foundHelper = this.nameLookup('helpers', name, 'helper');
-
- return {
- params: params,
- name: foundHelper,
- callParams: ["depth0"].concat(params).join(", "),
- helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
- };
- },
-
- // the params and contexts arguments are passed in arrays
- // to fill in
- setupParams: function(paramSize, params, useRegister) {
- var options = [], contexts = [], types = [], param, inverse, program;
-
- options.push("hash:" + this.popStack());
-
- inverse = this.popStack();
- program = this.popStack();
-
- // Avoid setting fn and inverse if neither are set. This allows
- // helpers to do a check for `if (options.fn)`
- if (program || inverse) {
- if (!program) {
- this.context.aliases.self = "this";
- program = "self.noop";
- }
-
- if (!inverse) {
- this.context.aliases.self = "this";
- inverse = "self.noop";
- }
-
- options.push("inverse:" + inverse);
- options.push("fn:" + program);
- }
-
- for(var i=0; i/gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=(""+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g," ")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:true,sL:"css"}},{cN:"tag",b:"
diff --git a/layouts/partials/hooks/head-end.html b/layouts/partials/hooks/head-end.html
new file mode 100644
index 00000000000..925c43cc15d
--- /dev/null
+++ b/layouts/partials/hooks/head-end.html
@@ -0,0 +1,18 @@
+{{/*
+
+ This template is included at the end of each page's ``.
+
+ We're using it here to include the custom CSS for the Matrix spec.
+
+*/}}
+
+{{ $scss := "scss/custom.scss"}}
+{{ if .Site.IsServer }}
+{{/* Note the missing postCSS. This makes it snappier to develop in Chrome, but makes it look sub-optimal in other browsers. */}}
+{{ $css := resources.Get $scss | toCSS (dict "enableSourceMap" true) }}
+
+{{ else }}
+{{ $css := resources.Get $scss | toCSS (dict "enableSourceMap" false) | postCSS | minify | fingerprint }}
+
+
+{{ end }}
diff --git a/layouts/partials/json-schema/resolve-additional-types.html b/layouts/partials/json-schema/resolve-additional-types.html
new file mode 100644
index 00000000000..26df52e5992
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-additional-types.html
@@ -0,0 +1,92 @@
+{{/*
+
+ Finds and returns all nested objects, given:
+
+ * `this_object`: a JSON schema object
+
+ Given a schema object, this template finds all nested objects under that
+ schema.
+
+ It "cleans" each object by copying only the parts of the objects that
+ the renderer needs, and adds the result to an array, `additional_objects`.
+
+ Finally it returns the array of all the objects it found.
+
+ Note that the returned array may contain duplicate objects.
+
+*/}}
+
+{{ $this_object := partial "json-schema/resolve-allof" . }}
+{{ $additional_objects := slice }}
+
+{{ if eq $this_object.type "object" }}
+
+ {{/*
+ Add the object we were passed into the $additional_objects array
+ */}}
+ {{ $additional_objects = $additional_objects | append (partial "clean-object" $this_object) }}
+
+ {{/*
+ Add any nested objects referenced in this object's `additionalProperties`
+ */}}
+ {{ if $this_object.additionalProperties }}
+ {{ if reflect.IsMap $this_object.additionalProperties }}
+ {{ $additional_objects = $additional_objects | append (partial "clean-object" $this_object.additionalProperties) }}
+
+ {{ range $key, $property := $this_object.additionalProperties.properties }}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" $property "additional_objects" $additional_objects) }}
+ {{ end }}
+
+ {{ end }}
+ {{ end }}
+
+ {{/*
+ Add any nested objects referenced in this object's `properties`
+ */}}
+ {{ range $key, $property := $this_object.properties}}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" $property "additional_objects" $additional_objects) }}
+ {{ end }}
+
+{{ end }}
+
+{{ if eq $this_object.type "array" }}
+ {{/*
+ Add any nested objects referenced in this object's `items`
+ */}}
+ {{ if reflect.IsSlice $this_object.items}}
+ {{ range $this_object.items }}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" . "additional_objects" $additional_objects) }}
+ {{ end }}
+ {{ else }}
+ {{ $additional_objects = partial "get-additional-objects" (dict "this_object" $this_object.items "additional_objects" $additional_objects) }}
+ {{ end }}
+{{ end }}
+
+{{ return $additional_objects }}
+
+
+{{/*
+ This actually makes the recursive call and adds the returned objects to the array
+*/}}
+{{ define "partials/get-additional-objects" }}
+ {{ $additional_objects := .additional_objects }}
+
+ {{ $this_object := partial "json-schema/resolve-allof" .this_object }}
+ {{ $more_objects := partial "json-schema/resolve-additional-types" $this_object }}
+ {{/*
+ As far as I know we don't have something like Array.concat(), so add them one at a time
+ */}}
+ {{ range $more_objects}}
+ {{ $additional_objects = $additional_objects | append (partial "clean-object" .) }}
+ {{ end }}
+ {{ return $additional_objects }}
+{{ end }}
+
+{{/*
+ Only copy the bits of the object that we actually care about.
+ This is needed for uniqify to work - otherwise objects that are the same
+ but with (for example) different examples will be considered different.
+*/}}
+{{ define "partials/clean-object" }}
+ {{ return (dict "title" .title "properties" .properties "required" .required "enum" .enum) }}
+{{ end }}
diff --git a/layouts/partials/json-schema/resolve-allof.html b/layouts/partials/json-schema/resolve-allof.html
new file mode 100644
index 00000000000..0150cfdeb6c
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-allof.html
@@ -0,0 +1,76 @@
+{{/*
+
+ Resolves the `allOf` keyword (https://swagger.io/specification/v2/#composition-and-inheritance-polymorphism),
+ given a JSON schema object.
+
+ `allOf` is used to support a kind of inheritance for JSON schema objects.
+
+ An object can reference a "parent" object using `allOf`, and it then inherits
+ its parent's properties. If the same property is present in the child, then
+ we use the child's version (the child overrides the parent).
+
+ Of course the parent can itself inherit from *its* parent, so we recurse to
+ handle that.
+
+*/}}
+
+{{ $ret := . }}
+{{ $original := . }}
+
+{{ $required := .required }}
+{{ if not $required }}
+ {{ $required := slice }}
+{{ end }}
+
+{{ with $ret.allOf }}
+
+ {{ $all_of_values := dict }}
+
+ {{/*
+ allOf is always an array
+ */}}
+ {{ range . }}
+
+ {{ with .required }}
+ {{ $required = union $required . }}
+ {{ end }}
+
+ {{/*
+ With merge, values from the second argument override those from the first argument.
+ So this order will accumulate values from allOf items, allowing later ones to override earlier
+ */}}
+ {{ $all_of_values = merge $all_of_values . }}
+
+ {{ end }}
+
+ {{/*
+ Then apply allOf values to the original, but allow the original to override allOf.
+ */}}
+ {{ $ret = merge $all_of_values $ret }}
+
+ {{/*
+ Except that if allOf *itself* contains allOf, we do want to override the original for that field only.
+ */}}
+ {{ with $all_of_values.allOf }}
+ {{ $ret = merge $ret (dict "allOf" . ) }}
+ {{ end }}
+
+ {{ with $ret.required }}
+ {{ $required = union $required $ret.required }}
+ {{ end }}
+
+ {{ $ret = merge $ret (dict "required" $required) }}
+
+{{ end }}
+
+{{/*
+ Recurse while we are finding new allOf entries to resolve
+*/}}
+{{ if ne $ret.allOf $original.allOf }}
+
+ {{ $resolved := partial "json-schema/resolve-allof" $ret }}
+ {{ $ret = merge $ret $resolved }}
+
+{{ end }}
+
+{{ return $ret }}
diff --git a/layouts/partials/json-schema/resolve-example.html b/layouts/partials/json-schema/resolve-example.html
new file mode 100644
index 00000000000..51ce6d5320d
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-example.html
@@ -0,0 +1,29 @@
+{{/*
+
+ For complex objects, example content is sometimes attached to the
+ object's individual properties (and subproperties...), so to get
+ a complete example for the whole object we need to iterate through
+ its properties (and subproperties...) and assemble them.
+
+ That's what this template does.
+
+*/}}
+
+{{ $this_object := partial "json-schema/resolve-allof" . }}
+
+{{ if eq $this_object.type "object" }}
+
+ {{ if not $this_object.example }}
+ {{ $this_object := merge (dict "example" dict ) $this_object }}
+ {{ end }}
+
+ {{ range $key, $property := $this_object.properties}}
+ {{ $this_property_example := partial "json-schema/resolve-example" $property }}
+ {{ if $this_property_example }}
+ {{ $this_object = merge (dict "example" (dict $key $this_property_example)) $this_object }}
+ {{ end }}
+ {{ end }}
+
+{{ end }}
+
+{{ return $this_object.example }}
diff --git a/layouts/partials/json-schema/resolve-refs.html b/layouts/partials/json-schema/resolve-refs.html
new file mode 100644
index 00000000000..1d99201dbf6
--- /dev/null
+++ b/layouts/partials/json-schema/resolve-refs.html
@@ -0,0 +1,65 @@
+{{/*
+
+ Resolves the `$ref` JSON schema keyword, by recursively replacing
+ it with the object it points to.
+
+ This template uses [`Scratch`](https://gohugo.io/functions/scratch/)
+ rather than a normal `dict` because with `dict` you can't replace key values:
+ https://discourse.gohugo.io/t/how-can-i-add-set-and-delete-keys-in-a-dictionary/29661
+
+*/}}
+
+{{ $schema := .schema }}
+{{ $path := .path}}
+
+{{ $ret := $schema }}
+
+{{ if reflect.IsMap $schema }}
+
+ {{ $scratch := newScratch }}
+ {{ $scratch.Set "result_map" dict }}
+
+ {{ $ref_value := index $schema "$ref"}}
+ {{ if $ref_value}}
+ {{ $full_path := path.Join $path $ref_value }}
+ {{/*
+ Apparently Hugo doesn't give us a nice way to split the extension off a filename.
+ */}}
+ {{ $without_ext := replaceRE "\\.[^\\.]*$" "" $full_path }}
+ {{ $pieces := split $without_ext "/" }}
+
+ {{ $ref := index site.Data $pieces }}
+
+ {{ $new_path := (path.Split $full_path).Dir}}
+ {{ $result_map := partial "json-schema/resolve-refs" (dict "schema" $ref "path" $new_path)}}
+ {{ if $result_map}}
+ {{ $scratch.Set "result_map" $result_map }}
+ {{end }}
+ {{ end }}
+
+
+ {{ range $key, $value := $schema }}
+ {{ if ne $key "$ref" }}
+ {{ $resolved := partial "json-schema/resolve-refs" (dict "schema" $value "path" $path) }}
+ {{ $scratch.SetInMap "result_map" $key $resolved }}
+ {{ end }}
+ {{ end }}
+
+ {{ $ret = $scratch.Get "result_map" }}
+
+{{ end }}
+
+{{ if reflect.IsSlice $schema }}
+
+ {{ $result_slice := slice }}
+
+ {{ range $schema }}
+ {{ $resolved := partial "json-schema/resolve-refs" (dict "schema" . "path" $path) }}
+ {{ $result_slice = $result_slice | append $resolved }}
+ {{ end }}
+
+ {{ $ret = $result_slice }}
+
+{{ end }}
+
+{{ return $ret }}
diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html
new file mode 100644
index 00000000000..e78ee9c410c
--- /dev/null
+++ b/layouts/partials/navbar.html
@@ -0,0 +1,55 @@
+{{/*
+
+ A version of the navbar.html partial in Docsy, only modified
+ to include the spec version, which is calculated using an
+ inline `version-string` partial.
+
+*/}}
+
+{{ $cover := .HasShortcode "blocks/cover" }}
+
+
+
+{{ define "partials/version-string" }}
+ {{ $ret := "unstable version"}}
+
+ {{ $status := .Site.Params.version.status }}
+
+ {{ if ne $status "unstable"}}
+ {{ $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 }}
+{{ end }}
diff --git a/layouts/partials/openapi/render-api.html b/layouts/partials/openapi/render-api.html
new file mode 100644
index 00000000000..db10b98c0a9
--- /dev/null
+++ b/layouts/partials/openapi/render-api.html
@@ -0,0 +1,54 @@
+{{/*
+
+ Render an HTTP API, given:
+
+ * `api_data`: the OpenAPI/Swagger data
+ * `base_url`: the base URL: that is, the part we glue onto the front
+ of each value in `paths` to get a complete URL.
+ * `path`: the directory under /data where we found this API definition.
+ We use this to resolve "$ref" values, since they are relative to the schema's
+ location.
+
+*/}}
+
+{{ $api_data := index .api_data }}
+{{ $base_url := .base_url }}
+{{ $path := .path }}
+
+{{ range $path_name, $path_data := $api_data.paths }}
+
+ {{ $endpoint := delimit (slice $base_url $path_name ) "" }}
+
+ {{/* note that a `paths` entry can be a $ref */}}
+
+ {{ $params := dict "endpoint" $endpoint "path" $path }}
+
+ {{ with $path_data.get }}
+
+ {{ $operation_params := merge $params (dict "method" "GET" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+ {{ with $path_data.post }}
+
+ {{ $operation_params := merge $params (dict "method" "POST" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+ {{ with $path_data.put }}
+
+ {{ $operation_params := merge $params (dict "method" "PUT" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+ {{ with $path_data.delete }}
+
+ {{ $operation_params := merge $params (dict "method" "DELETE" "operation_data" . ) }}
+ {{ partial "openapi/render-operation" $operation_params }}
+
+ {{ end }}
+
+{{ end }}
diff --git a/layouts/partials/openapi/render-object-table.html b/layouts/partials/openapi/render-object-table.html
new file mode 100644
index 00000000000..bf275ed6ec1
--- /dev/null
+++ b/layouts/partials/openapi/render-object-table.html
@@ -0,0 +1,92 @@
+{{/*
+
+ Render a table listing the properties of an object, given:
+
+ * `caption`: optional caption for the table
+ * `properties`: dictionary of the properties to list, each given as:
+ `property_name` : `property_data`
+ * `required`: array containing the names of required properties.
+ In some cases (such as response body specifications) this isn't used, and
+ instead properties have a `required` boolean attribute. We support this too.
+
+*/}}
+
+{{ $caption := .caption }}
+{{ $properties := .properties}}
+{{ $required := .required}}
+
+{{ if $properties }}
+
+
+ {{ with $caption }}
+
{{ . }}
+ {{ end }}
+
+
Name
+
Type
+
Description
+
+
+ {{ range $property_name, $property := $properties }}
+
+ {{ $property := partial "json-schema/resolve-allof" $property }}
+
+ {{/*
+ If the property has a `title`, use that rather than `type`.
+ This means we can write things like `EventFilter` rather than `object`.
+ */}}
+ {{ $type := $property.type}}
+ {{ if $property.title }}
+ {{ $type = $property.title }}
+ {{ end }}
+
+ {{/*
+ If the property is an array, indicate this with square brackets,
+ like `[type]`.
+ */}}
+ {{ if eq $type "array"}}
+ {{ $items := $property.items }}
+ {{ if $property.items }}
+ {{ $items = partial "json-schema/resolve-allof" $property.items }}
+ {{ end }}
+ {{ $inner_type := $items.type }}
+ {{ if $items.title }}
+ {{ $inner_type = $items.title }}
+ {{ end }}
+ {{ $type = delimit (slice "[" $inner_type "]") "" }}
+ {{ end }}
+
+ {{/*
+ If the property is an enum, indicate this.
+ */}}
+ {{ if (and (eq $type "string") ($property.enum)) }}
+ {{ $type = "enum" }}
+ {{ end }}
+
+ {{/*
+ If the property uses `additionalProperties` to describe its
+ internal structure, handle this.
+ */}}
+ {{ if reflect.IsMap $property.additionalProperties }}
+ {{ if $property.additionalProperties.title }}
+ {{ $type = delimit (slice "{ string: " $property.additionalProperties.title "}" ) "" }}
+ {{ end }}
+ {{ end }}
+
+ {{/*
+ Handle two ways of indicating "required", one for simple parameters,
+ the other for request and response body objects.
+ */}}
+ {{ $required := cond (or (in $required $property_name) ( eq $property.required true )) true false }}
+
+
+
{{ $property_name }}
+
{{ $type }}
+
{{ if $required }}Required: {{end}}{{ $property.description | markdownify }}{{ if eq $type "enum"}}
One of: {{ $property.enum }}.
{{ end }}
+
+
+ {{ end }}
+
+
+
+{{ end }}
diff --git a/layouts/partials/openapi/render-operation.html b/layouts/partials/openapi/render-operation.html
new file mode 100644
index 00000000000..430c571a091
--- /dev/null
+++ b/layouts/partials/openapi/render-operation.html
@@ -0,0 +1,65 @@
+{{/*
+
+ Render a single HTTP API operation: that is, a method+endpoint combination, given:
+
+ * `method`: the method, e.g. GET, PUT
+ * `endpoint`: the endpoint
+ * `operation_data`: the OpenAPI/Swagger data for the operation
+ * `path`: the path where this definition was found, to enable us to resolve "$ref"
+
+ This template renders the operation as a `` containing:
+
+ * an `
` heading containing the method and endpoint
+ * a `` element containing the details, including:
+ * operation description
+ * basic info about the operation
+ * request details
+ * response details
+
+*/}}
+
+{{ $method := .method }}
+{{ $endpoint := .endpoint }}
+{{ $operation_data := .operation_data }}
+{{ $path := .path }}
+
+
+
+
+
+
+
+ {{ $method }}
+ {{ $endpoint }}
+
+
+
+
+{{ if $operation_data.deprecated }}
+ {{ partial "alert" (dict "type" "warning" "omit_title" "true" "content" "This API is deprecated and will be removed from a future release.") }}
+{{ end }}
+
+
{{ $operation_data.description | markdownify }}
+
+
+
+
+
+
Rate-limited:
+ {{ $rate_limited := index $operation_data.responses "429" }}
+
{{ if $rate_limited }}Yes{{ else }}No{{ end }}
+
+
+
Requires authentication:
+
{{ if $operation_data.security }}Yes{{ else }}No{{ end }}
+
+
+
+
+{{ partial "openapi/render-request" (dict "parameters" $operation_data.parameters "path" $path) }}
+
+{{ partial "openapi/render-responses" (dict "responses" $operation_data.responses "path" $path) }}
+
+
+
+
diff --git a/layouts/partials/openapi/render-parameters.html b/layouts/partials/openapi/render-parameters.html
new file mode 100644
index 00000000000..2b4fb09db1d
--- /dev/null
+++ b/layouts/partials/openapi/render-parameters.html
@@ -0,0 +1,30 @@
+{{/*
+
+ Render the parameters of a given type, given:
+
+ * `parameters`: OpenAPI/Swagger data specifying the parameters
+ * `type`: the type of parameters to render: "header, ""path", "query"
+ * `caption`: caption to use for the table
+
+ This template renders a single table containing parameters of the given type.
+
+*/}}
+
+{{ $parameters := .parameters }}
+{{ $type := .type }}
+{{ $caption := .caption }}
+
+{{ $parameters_of_type := where $parameters "in" $type }}
+
+{{ with $parameters_of_type }}
+
+ {{/* convert parameters into the format that render-object-table expects to see */}}
+ {{ $param_dict := dict }}
+ {{ range $parameter := . }}
+ {{ $param_dict = merge $param_dict (dict $parameter.name (dict "type" $parameter.type "description" $parameter.description "required" $parameter.required "enum" $parameter.enum) )}}
+ {{ end }}
+
+ {{/* and render the parameters */}}
+ {{ partial "openapi/render-object-table" (dict "caption" $caption "properties" $param_dict) }}
+
+{{ end }}
diff --git a/layouts/partials/openapi/render-request.html b/layouts/partials/openapi/render-request.html
new file mode 100644
index 00000000000..1695c2f83a7
--- /dev/null
+++ b/layouts/partials/openapi/render-request.html
@@ -0,0 +1,67 @@
+{{/*
+
+ Render the request part of a single HTTP API operation, given:
+
+ * `parameters`: OpenAPI/Swagger data specifying the parameters
+ * `path`: the path where this definition was found, to enable us to resolve "$ref"
+
+ This template renders:
+ * the "simple parameters" (header, path, query parameters)
+ * body parameters, which may be more complex, containing nested objects
+ * response body examples
+
+*/}}
+
+{{ $parameters := .parameters }}
+{{ $path := .path }}
+
+
Request
+
+{{ if $parameters }}
+
+ {{ $simple_parameters := where $parameters "in" "!=" "body"}}
+ {{ if $simple_parameters }}
+
+{{ end }}
diff --git a/layouts/partials/openapi/render-responses.html b/layouts/partials/openapi/render-responses.html
new file mode 100644
index 00000000000..c92aba8e357
--- /dev/null
+++ b/layouts/partials/openapi/render-responses.html
@@ -0,0 +1,97 @@
+{{/*
+
+ Render the response part of a single HTTP API operation, given:
+
+ * `responses`: OpenAPI/Swagger data specifying the responses
+ * `path`: the path where this definition was found, to enable us to resolve "$ref"
+
+ This template renders:
+ * a summary of all the different responses
+ * details of the body for each response code
+ * body parameters, which may be more complex, containing nested objects
+ * response body examples
+
+*/}}
+
+{{ $responses := .responses }}
+{{ $path := .path }}
+
+
+
+ {{/*
+ All this is to work out how to express the content of the response
+ in the case where it is an array.
+ */}}
+ {{ if eq $schema.type "array" }}
+ {{ $type_of := "" }}
+ {{ if reflect.IsSlice $schema.items }}
+ {{ $types := slice }}
+ {{ range $schema.items }}
+ {{ if .title }}
+ {{ $types = $types | append .title}}
+ {{ else }}
+ {{ $types = $types | append .type }}
+ {{ end }}
+ {{ end }}
+ {{ $type_of = delimit $types ", "}}
+ {{ else }}
+ {{ $type_of = $schema.items.title }}
+ {{ end }}
+
Array of {{ $type_of }}.
+ {{ end }}
+
+ {{ $additional_types := partial "json-schema/resolve-additional-types" $schema }}
+ {{ $additional_types = uniq $additional_types }}
+ {{ range $additional_types }}
+ {{ partial "openapi/render-object-table" (dict "caption" .title "properties" .properties "required" .required) }}
+ {{ end }}
+
+ {{ $example := partial "json-schema/resolve-example" $schema }}
+ {{ if $response.examples }}
+ {{ $example = partial "json-schema/resolve-refs" (dict "schema" $response.examples "path" $path) }}
+ {{ $example = index $example "application/json" }}
+ {{ end }}
+
+ {{ if $example }}
+ {{ $example_json := jsonify (dict "indent" " ") $example }}
+ {{ $example_json = replace $example_json "\\u003c" "<" }}
+ {{ $example_json = replace $example_json "\\u003e" ">" | safeHTML }}
+
+```json
+{{ $example_json }}
+```
+
+ {{ else }}
+ {{ partial "alert" (dict "type" "warning" "omit_title" "true" "color" "warning" "content" "Specification error: Example invalid or not present") }}
+ {{ end }}
+
+ {{ end }}
+ {{ end }}
+{{ end }}
diff --git a/layouts/partials/sidebar-tree.html b/layouts/partials/sidebar-tree.html
new file mode 100644
index 00000000000..17c181cc713
--- /dev/null
+++ b/layouts/partials/sidebar-tree.html
@@ -0,0 +1,64 @@
+{{/*
+
+ A version of the sidebar-tree.html partial in Docsy, with a few small
+ modifications:
+
+ * include `div#toc` for the ToC
+ * start the sidebar at the root (homepage) since for us that is the Matrix
+ overview page
+ * omit module pages, which we don't want to be directly accessible
+ (we only use them as raw material for the client-server spec)
+
+*/}}
+
+{{/* We cache this partial for bigger sites and set the active class client side. */}}
+{{ $shouldDelayActive := ge (len .Site.Pages) 2000 }}
+
+{{ end }}
diff --git a/layouts/shortcodes/boxes/note.html b/layouts/shortcodes/boxes/note.html
new file mode 100644
index 00000000000..48c9d3c9153
--- /dev/null
+++ b/layouts/shortcodes/boxes/note.html
@@ -0,0 +1 @@
+{{ partial "alert" (dict "type" "note" "content" .Inner) }}
diff --git a/layouts/shortcodes/boxes/rationale.html b/layouts/shortcodes/boxes/rationale.html
new file mode 100644
index 00000000000..385bd4b6f58
--- /dev/null
+++ b/layouts/shortcodes/boxes/rationale.html
@@ -0,0 +1 @@
+{{ partial "alert" (dict "type" "rationale" "content" .Inner) }}
diff --git a/layouts/shortcodes/boxes/warning.html b/layouts/shortcodes/boxes/warning.html
new file mode 100644
index 00000000000..6e884026992
--- /dev/null
+++ b/layouts/shortcodes/boxes/warning.html
@@ -0,0 +1 @@
+{{ partial "alert" (dict "type" "warning" "content" .Inner) }}
diff --git a/layouts/shortcodes/changelog/changelog-changes.html b/layouts/shortcodes/changelog/changelog-changes.html
new file mode 100644
index 00000000000..11a59858095
--- /dev/null
+++ b/layouts/shortcodes/changelog/changelog-changes.html
@@ -0,0 +1,87 @@
+{{/*
+
+ This template is used to render the set of changes in the changelog page.
+
+ It expects to find a directory "changelogs" containing a subdirectory for
+ each of the 5 APIs in the specification. Inside each of these directories
+ 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 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.`; 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" }}
+
+
+ {{ end }}
+{{ end }}
+
+{{ end }}
diff --git a/layouts/shortcodes/changelog/changelog-description.html b/layouts/shortcodes/changelog/changelog-description.html
new file mode 100644
index 00000000000..3c719725281
--- /dev/null
+++ b/layouts/shortcodes/changelog/changelog-description.html
@@ -0,0 +1,19 @@
+{{/*
+
+ This template is used to provide different content for the unstable spec
+ version and for a versioned release.
+
+*/}}
+
+{{ $status := .Site.Params.version.status }}
+
+{{ if eq $status "unstable"}}
+
+
This is the unstable version of the Matrix specification.
+
This changelog lists changes made since the last release of the specification.
+
+{{ else }}
+
+
This is version v{{ .Site.Params.version.major }}.{{ .Site.Params.version.minor }} of the Matrix specification.
+
+{{ end }}
diff --git a/layouts/shortcodes/cs-modules.html b/layouts/shortcodes/cs-modules.html
new file mode 100644
index 00000000000..eb294166262
--- /dev/null
+++ b/layouts/shortcodes/cs-modules.html
@@ -0,0 +1,14 @@
+{{/*
+
+ This template is used to embed module documentation in the client-server API spec.
+
+ It searches the site for pages of type "module", sorts them by weight, and
+ emits the page's rendered content.
+
+*/}}
+
+{{ $modules := where site.Pages "Type" "module" }}
+
+{{ range $modules.ByWeight }}
+{{ .Content }}
+{{ end }}
diff --git a/layouts/shortcodes/definition.html b/layouts/shortcodes/definition.html
new file mode 100644
index 00000000000..151e060607f
--- /dev/null
+++ b/layouts/shortcodes/definition.html
@@ -0,0 +1,59 @@
+{{/*
+
+ This template is used to render EDUs and PDUs in the server-server and room versions specs.
+
+ It expects to be passed a `path` parameter, which is a path, relative to /data,
+ pointing to a schema file. The file extension is omitted. For example:
+
+ {{% definition path="api/server-server/definitions/edu" %}}
+
+ This template replaces the old {{definition_*}} template.
+
+*/}}
+
+{{ $path := .Params.path }}
+{{ $compact := .Params.compact }}
+{{ $pieces := split $path "/" }}
+
+{{/* The definition is referenced by the .path parameter */}}
+{{ $definition := index .Site.Data $pieces }}
+
+{{/* The base path, which we use to resolve $ref, omits the last component */}}
+{{ $pieces = first (sub (len $pieces) 1) $pieces}}
+{{ $path = delimit $pieces "/" }}
+
+{{/* Resolve $ref and allOf */}}
+{{ $definition = partial "json-schema/resolve-refs" (dict "schema" $definition "path" $path) }}
+{{ $definition = partial "json-schema/resolve-allof" $definition }}
+
+
+
+
+
+
+
+
+{{ $example := partial "json-schema/resolve-example" $definition }}
+
+```json
+{{ jsonify (dict "indent" " ") $example }}
+```
+
+
diff --git a/layouts/shortcodes/event-fields.html b/layouts/shortcodes/event-fields.html
new file mode 100644
index 00000000000..83651c213dc
--- /dev/null
+++ b/layouts/shortcodes/event-fields.html
@@ -0,0 +1,45 @@
+{{/*
+
+ This template is used to render the fields that all basic events and room events
+ must or may contain.
+
+ It expects to be passed an `event_type` parameter, is the name of a file under
+ /data/event-schemas/core-event-schema. The file extension is omitted. For example:
+
+ {{% event-fields event_type="room_event" %}}
+
+ This template replaces the old {{common_event_fields}} and {{common_room_event_fields}} templates.
+
+*/}}
+
+{{ $event := index .Site.Data "event-schemas" "schema" "core-event-schema" .Params.event_type }}
+{{ $path := "event-schemas/schema/core-event-schema" }}
+
+{{ $event = partial "json-schema/resolve-refs" (dict "schema" $event "path" $path) }}
+{{ $event := partial "json-schema/resolve-allof" $event }}
+
+
+
+
+
+
+
+ {{ humanize $event.title }}
+
+
+
+
+{{ $event.description | markdownify }}
+
+
+
+{{ $event = merge $event (dict "title" "") }}
+
+{{ $additional_types := partial "json-schema/resolve-additional-types" $event }}
+{{ range $additional_types }}
+ {{ partial "openapi/render-object-table" (dict "caption" .title "properties" .properties "required" .required) }}
+{{end}}
+
+
+
+
diff --git a/layouts/shortcodes/event-group.html b/layouts/shortcodes/event-group.html
new file mode 100644
index 00000000000..5e2900f0439
--- /dev/null
+++ b/layouts/shortcodes/event-group.html
@@ -0,0 +1,32 @@
+{{/*
+
+ This template is used to render a group of events starting with a given prefix.
+
+ It expects to be passed a `group_name` parameter. For example:
+
+ {{% event-group group_name="m.call" %}}
+
+ The template will then render all events whose schema starts with the given name.
+
+ This template replaces the old {{*_events}} template.
+
+*/}}
+
+{{ $path := "event-schemas/schema" }}
+
+{{ $events := index .Site.Data "event-schemas" "schema" }}
+{{ $group_name := .Params.group_name }}
+
+{{ range $event_name, $event_data := $events }}
+
+ {{ $prefix := substr $event_name 0 (len $group_name) }}
+ {{ if eq $prefix $group_name }}
+
+ {{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
+ {{ $event_data := partial "json-schema/resolve-allof" $event_data }}
+
+ {{ partial "events/render-event" (dict "event_name" $event_name "event_data" $event_data)}}
+
+ {{ end }}
+
+{{ end }}
diff --git a/layouts/shortcodes/event.html b/layouts/shortcodes/event.html
new file mode 100644
index 00000000000..71c9c53490a
--- /dev/null
+++ b/layouts/shortcodes/event.html
@@ -0,0 +1,20 @@
+{{/*
+
+ This template is used to render an event.
+
+ It expects to be passed an `event` parameter, which is the name of a schema file under
+ "data/event-schemas/schema". The file extension is omitted. For example:
+
+ {{% event event="m.accepted_terms" %}}
+
+ This template replaces the old {{*_event}} template.
+
+*/}}
+
+{{ $event_data := index .Site.Data "event-schemas" "schema" .Params.event }}
+{{ $path := "event-schemas/schema" }}
+
+{{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
+{{ $event_data := partial "json-schema/resolve-allof" $event_data }}
+
+{{ partial "events/render-event" (dict "event_name" .Params.event "event_data" $event_data)}}
diff --git a/layouts/shortcodes/http-api.html b/layouts/shortcodes/http-api.html
new file mode 100644
index 00000000000..b4bc6d0dc81
--- /dev/null
+++ b/layouts/shortcodes/http-api.html
@@ -0,0 +1,26 @@
+{{/*
+
+ This template is used to render an HTTP API, given an OpenAPI/Swagger definition.
+
+ It expects to be passed two parameters:
+
+ * a `spec` parameter identifying the spec, which must be the name of
+ a directory under /data/api
+ * an `api` parameter, identifying an OpenAPI/Swagger definition,
+ which is the name of a schema file under "data/api/$spec".
+ The file extension is omitted. For example:
+
+ {{% http-api spec="server-server" api="public_rooms" %}}
+
+ This template replaces the old {{*_http_api}} template.
+
+*/}}
+
+{{ $spec := .Params.spec}}
+{{ $api := .Params.api}}
+
+{{ $api_data := index .Site.Data.api .Params.spec .Params.api }}
+{{ $base_url := replace $api_data.basePath "%CLIENT_MAJOR_VERSION%" "r0" }}
+{{ $path := delimit (slice "api" $spec) "/" }}
+
+{{ partial "openapi/render-api" (dict "api_data" $api_data "base_url" $base_url "path" $path) }}
diff --git a/layouts/shortcodes/msgtypes.html b/layouts/shortcodes/msgtypes.html
new file mode 100644
index 00000000000..ccdfda5fac7
--- /dev/null
+++ b/layouts/shortcodes/msgtypes.html
@@ -0,0 +1,48 @@
+{{/*
+
+ This template is used to render the `m.room.message` events.
+
+ It replaces the old {{msgtype_events}} template.
+
+*/}}
+
+{{ $path := "event-schemas/schema" }}
+{{ $compact := false }}
+
+{{/*
+ The old template starts with an explicit list of events, presumably
+ to define the order in which they are rendered.
+*/}}
+{{ $msgtypes := (slice "m.room.message$m.text" "m.room.message$m.emote" "m.room.message$m.notice" "m.room.message$m.image" "m.room.message$m.file") }}
+
+{{/*
+ It excludes `m.room.message$m.server_notice`
+*/}}
+{{ $excluded := slice "m.room.message$m.server_notice" }}
+
+{{/*
+ It then adds any other events that start with `m.room.message`.
+*/}}
+{{ $events := index .Site.Data "event-schemas" "schema" }}
+{{ $expected_prefix := "m.room.message$"}}
+{{ range $object_name, $event_data := $events }}
+
+ {{ $prefix := substr $object_name 0 (len $expected_prefix) }}
+ {{ if and (eq $prefix $expected_prefix) (not (in $excluded $object_name)) (not (in $msgtypes $object_name)) }}
+ {{ $msgtypes = $msgtypes | append $object_name }}
+ {{ end }}
+
+{{ end }}
+
+{{ $site_data := .Site.Data }}
+
+{{ range $msgtypes }}
+
+ {{ $event_data := index $site_data "event-schemas" "schema" . }}
+ {{ $event_data = partial "json-schema/resolve-refs" (dict "schema" $event_data "path" $path) }}
+ {{ $event_data := partial "json-schema/resolve-allof" $event_data }}
+
+ {{ $event_name := index (split . "$") 1 }}
+ {{ partial "events/render-event" (dict "event_name" $event_name "desired_example_name" . "event_data" $event_data)}}
+
+{{ end }}
diff --git a/layouts/shortcodes/proposal-tables.html b/layouts/shortcodes/proposal-tables.html
new file mode 100644
index 00000000000..f61f168b32f
--- /dev/null
+++ b/layouts/shortcodes/proposal-tables.html
@@ -0,0 +1,81 @@
+{{/*
+
+ This template is used to render tables of MSC proposals.
+
+ It expects there to be a "proposals.json" under /data/msc.
+ It expects "proposals.json" to contain an array of objects,
+ one for each MSC state. Each object contains:
+ * `title`: human-readable title for the state, like "Proposal In Review"
+ * `label`: the GitHub label used for the state, like "proposal-in-review"
+ * `proposals`: an array of objects, each of which represents an MSC and contains:
+ * `number`: GitHub issue number
+ * `url`: GitHub URL for this issue
+ * `title`: Issue title
+ * `created_at`: issue creation date
+ * `updated_at`: issue last-updated date
+ * `authors`: array of GitHub user names representing authors of this MSC
+ * `shepherd`: GitHub user name representing the shepherd of this MSC, or null
+ * `documentation`: Links to further documentation referenced in the GitHub issue
+
+ This data is scraped from GitHub using the /scripts/proposals.js Node script.
+ The script is run in CI: so typically if you run a local server the data will
+ be missing and no tables will be generated. If you do want to see the tables locally,
+ you can run the script locally:
+
+ npm install
+ npm run get-proposals
+
+ If this template does find the data, it renders one table for each MSC state,
+ containing a row for each MSC in that state.
+
+*/}}
+
+{{ $states := .Site.Data.msc.proposals }}
+
+{{ range $states }}
+
+ {{ end }}
+{{ end }}
diff --git a/layouts/shortcodes/sas-emojis.html b/layouts/shortcodes/sas-emojis.html
new file mode 100644
index 00000000000..5ce6a00939c
--- /dev/null
+++ b/layouts/shortcodes/sas-emojis.html
@@ -0,0 +1,26 @@
+{{/*
+
+ This template is used to render the table of SAS emoji table.
+
+ It replaces the old {{sas_emoji_table}} template.
+
+*/}}
+
+{{ $emoji_json := readFile "data-definitions/sas-emoji.json" | transform.Unmarshal }}
+
+
+
+
Number
+
Emoji
+
Unicode
+
Description
+
+{{ range $emoji_json }}
+
+
{{ .number }}
+
{{ .emoji }}
+
{{ .unicode }}
+
{{ .description }}
+
+{{ end }}
+
diff --git a/meta/documentation_style.rst b/meta/documentation_style.rst
index e3d363c4802..aea495bd3e0 100644
--- a/meta/documentation_style.rst
+++ b/meta/documentation_style.rst
@@ -8,52 +8,36 @@ in.
Format
------
-Documentation is written either in github-flavored markdown or RST.
+Documentation is written in Commonmark markdown.
Sections
--------
-RST support lots of different punctuation characters for underlines on sections.
-Content in the specification MUST use the same characters in order for the
-complete specification to be merged correctly. These characters are:
-
-- ``=``
-- ``-``
-- ``~``
-- ``+``
-- ``^``
-- \ `````
-- ``@``
-- ``:``
-
-If you find yourself using ``^`` or beyond, you should rethink your document
-layout if possible.
+Markdown supports headings through the `#` prefix on text. Please avoid heavily
+nested titles (h6, or 6 `#` characters) and instead re-evaluate the document structure.
Correct capitalisation for long section names
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Headings should start with a capital letter, and use lower-case otherwise.
-
+Headings should start with a capital letter, and use lower-case otherwise. This
+document is an example of what we mean.
TODOs
-----
-Any RST file in this repository may make it onto ``matrix.org``. We do not want
-``TODO`` markers visible there. For internal comments, notes, TODOs, use standard
-RST comments like so::
-
- .. TODO-Bob
- There is something to do here. This will not be rendered by something like
- rst2html.py so it is safe to put internal comments here.
-
-You SHOULD put your username with the TODO so we know who to ask about it.
+Any file in this repository might make it onto the matrix.org site, and as such
+we do not want ``TODO`` markers visible there. For internal comments, notes, TODOs,
+etc please use standard markdown comments (``). Please
+include your name in the TODO comment so we know who to ask about it in the future.
Line widths
-----------
-We use 80 characters for line widths. This is a guideline and can be flouted IF
+We use 80 characters for line widths. This is a guideline and can be ignored IF
AND ONLY IF it makes reading more legible. Use common sense.
+For proposals, please use 120 characters as a guide.
+
Stylistic notes
---------------
@@ -82,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
~~~~~~~
diff --git a/api/openapi_extensions.md b/openapi_extensions.md
similarity index 100%
rename from api/openapi_extensions.md
rename to openapi_extensions.md
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000000..73994715fea
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,986 @@
+{
+ "name": "matrix-spec",
+ "version": "0.0.1",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
+ "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.3",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
+ "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
+ "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.3",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true
+ },
+ "autoprefixer": {
+ "version": "9.8.6",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
+ "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.12.0",
+ "caniuse-lite": "^1.0.30001109",
+ "colorette": "^1.2.1",
+ "normalize-range": "^0.1.2",
+ "num2fraction": "^1.2.2",
+ "postcss": "^7.0.32",
+ "postcss-value-parser": "^4.1.0"
+ },
+ "dependencies": {
+ "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==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "binary-extensions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+ "dev": true
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.16.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
+ "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001219",
+ "colorette": "^1.2.2",
+ "electron-to-chromium": "^1.3.723",
+ "escalade": "^3.1.1",
+ "node-releases": "^1.1.71"
+ },
+ "dependencies": {
+ "caniuse-lite": {
+ "version": "1.0.30001230",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz",
+ "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==",
+ "dev": true
+ },
+ "colorette": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz",
+ "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.3.739",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz",
+ "integrity": "sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==",
+ "dev": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "1.1.72",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz",
+ "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==",
+ "dev": true
+ }
+ }
+ },
+ "caller-callsite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "dev": true,
+ "requires": {
+ "callsites": "^2.0.0"
+ }
+ },
+ "caller-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "dev": true,
+ "requires": {
+ "caller-callsite": "^2.0.0"
+ }
+ },
+ "callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001115",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001115.tgz",
+ "integrity": "sha512-NZrG0439ePYna44lJX8evHX2L7Z3/z3qjVLnHgbBb/duNEnGo348u+BQS5o4HTWcrb++100dHFrU36IesIrC1Q==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chokidar": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+ "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.4.0"
+ }
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "colorette": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
+ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
+ "dev": true
+ },
+ "cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "dev": true,
+ "requires": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "dependency-graph": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.9.0.tgz",
+ "integrity": "sha512-9YLIBURXj4DJMFALxXw9K3Y3rwb5Fk0X5/8ipCzaN84+gKxoHK43tVKRNakCQbiEx07E8Uwhuq21BpUagFhZ8w==",
+ "dev": true
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
+ "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.0",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.2",
+ "picomatch": "^2.2.1"
+ }
+ },
+ "fastq": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
+ "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "fs-extra": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
+ "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
+ "dev": true,
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^1.0.0"
+ }
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
+ "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
+ "dev": true
+ },
+ "glob-parent": {
+ "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"
+ }
+ },
+ "globby": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
+ "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.1.1",
+ "ignore": "^5.1.4",
+ "merge2": "^1.3.0",
+ "slash": "^3.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ },
+ "ignore": {
+ "version": "5.1.8",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
+ "dev": true
+ },
+ "import-cwd": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
+ "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
+ "dev": true,
+ "requires": {
+ "import-from": "^2.1.0"
+ }
+ },
+ "import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "dev": true,
+ "requires": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "import-from": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
+ "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
+ "dev": true,
+ "requires": {
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
+ "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^1.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
+ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1"
+ }
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+ "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.1",
+ "picomatch": "^2.0.5"
+ }
+ },
+ "node-fetch": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+ "dev": true
+ },
+ "num2fraction": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+ "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "postcss": {
+ "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",
+ "source-map": "^0.6.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "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"
+ }
+ }
+ }
+ },
+ "postcss-cli": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-7.1.2.tgz",
+ "integrity": "sha512-3mlEmN1v2NVuosMWZM2tP8bgZn7rO5PYxRRrXtdSyL5KipcgBDjJ9ct8/LKxImMCJJi3x5nYhCGFJOkGyEqXBQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0",
+ "chokidar": "^3.3.0",
+ "dependency-graph": "^0.9.0",
+ "fs-extra": "^9.0.0",
+ "get-stdin": "^8.0.0",
+ "globby": "^11.0.0",
+ "postcss": "^7.0.0",
+ "postcss-load-config": "^2.0.0",
+ "postcss-reporter": "^6.0.0",
+ "pretty-hrtime": "^1.0.3",
+ "read-cache": "^1.0.0",
+ "yargs": "^15.0.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "postcss-load-config": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz",
+ "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==",
+ "dev": true,
+ "requires": {
+ "cosmiconfig": "^5.0.0",
+ "import-cwd": "^2.0.0"
+ }
+ },
+ "postcss-reporter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz",
+ "integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "lodash": "^4.17.11",
+ "log-symbols": "^2.2.0",
+ "postcss": "^7.0.7"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
+ "dev": true
+ },
+ "pretty-hrtime": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
+ "dev": true
+ },
+ "read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.3.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "dev": true
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "run-parallel": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
+ "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==",
+ "dev": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "universalify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
+ "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
+ "dev": true
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+ "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "y18n": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
+ "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000000..5a4e3142faa
--- /dev/null
+++ b/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "matrix-spec",
+ "version": "0.0.1",
+ "description": "Hugo theme for the Matrix specification.",
+ "main": "none.js",
+ "scripts": {
+ "get-proposals": "node ./scripts/proposals.js",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/matrix-org/matrix-doc.git"
+ },
+ "author": "The Matrix.org Foundation C.I.C.",
+ "private": true,
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/matrix-org/matrix-doc"
+ },
+ "homepage": "https://github.com/matrix-org/matrix-doc#readme",
+ "dependencies": {},
+ "devDependencies": {
+ "autoprefixer": "^9.8.6",
+ "node-fetch": "^2.6.1",
+ "postcss-cli": "^7.1.2"
+ }
+}
diff --git a/proposals/1543-qr_code_key_verification.md b/proposals/1543-qr_code_key_verification.md
new file mode 100644
index 00000000000..ce620d92dac
--- /dev/null
+++ b/proposals/1543-qr_code_key_verification.md
@@ -0,0 +1,297 @@
+Bi-directional Key verification using QR codes
+==============================================
+
+Problem/Background
+------------------
+
+Key verification is essential in ensuring that end-to-end encrypted messages
+cannot be read by unauthorized parties. Traditionally, key verification is
+done by comparing long strings. To save users from the tedium of reading out
+long strings, some systems allow one party to verify the other party by
+scanning a QR code; by doing this twice, both parties can verify each other.
+In this proposal, we present a method for both parties to verify each other by
+only scanning one QR code.
+
+Proposal
+--------
+
+When Alice and Bob meet in person to verify keys, Alice will scan a QR code
+generated by Bob's device. The QR code will encode both Bob's key as well as what Bob
+thinks Alice's key is. When Alice scans the QR code, she will ensure that the
+keys match what is expected, in which case, she relays this information to Bob,
+who can then tell his device that the keys match.
+
+### Example flow
+
+1. Alice and Bob meet in person, and want to verify each other's keys.
+2. Alice requests a key verification through her device by sending an
+ `m.key.verification.request` message (see
+ [MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)), with
+ `m.qr_code.show.v1`, `m.qr_code.scan.v1`, and `m.reciprocate.v1` listed in
+ `methods`, and Bob responds with a `m.key.verification.ready` message.
+3. Alice's client displays a QR code that Bob is able to scan, and an option to
+ scan Bob's QR code.
+4. Bob's client prompts Bob to verify Alice's key. The prompt includes a QR
+ code that Alice can scan (if the `m.key.verification.request` message listed
+ `m.qr_code.scan.v1`), and an option to scan Alice's QR code (if the
+ `m.key.verification.request` message listed `m.qr_code.show.v1`). The QR
+ code encodes:
+ - Bob's master cross-signing public key,
+ - what Bob thinks Alice's master cross-signing public key is,
+ - a random shared secret.
+5. Alice scans Bob's QR code.
+6. Alice's device ensures that:
+ - Bob's key encoded in the QR code matches the key that she already has for
+ Bob, and
+ - Alice's cross-signing key matches the cross-signing key encoded in the QR
+ code.
+
+ If any of these checks fail, Alice's device displays an error message
+ indicating that the code is incorrect, and sends a
+ `m.key.verification.cancel` message to Bob's device.
+
+ Otherwise, at this point:
+ - Alice's device has now verified Bob's key, and
+ - Alice's device knows that Bob has the correct key for her.
+
+ Thus for Bob to verify Alice's key, Alice needs to tell Bob that he has the
+ right key.
+7. Alice's device displays a message saying that all is well. This message
+ tells Alice that she has the right key for Bob, and tells Bob that he has
+ the right key for Alice.
+8. Alice's device sends a `m.key.verification.start` message with `method` set
+ to `m.reciprocate.v1` to Bob (see below). The message includes the shared
+ secret from the QR code. This signals to Bob's device that Alice has
+ scanned Bob's QR code.
+
+ This message is merely a signal for Bob's device to proceed to the next
+ step, and is not used for verification purposes.
+9. Upon receipt of the `m.key.verification.start` message, Bob's device ensures
+ that the shared secret matches.
+
+ If the shared secret does not match, it should display an error message
+ indicating that an attack was attempted. (This does not affect Alice's
+ verification of Bob's keys.)
+
+ If the shared secret does match, it asks Bob to confirm that Alice
+ has scanned the QR code.
+10. Bob sees Alice's device confirm that the key matches, and presses the button
+ on his device to indicate that Alice's key is verified.
+
+ Bob's verification of Alice's key hinges on Alice telling Bob the result of
+ her scan. Since the QR code includes what Bob thinks Alice's key is,
+ Alice's device can check whether Bob has the right key for her. Alice has
+ no motivation to lie about the result, as getting Bob to trust an incorrect
+ key would only affect communications between herself and Bob. Thus Alice
+ telling Bob that the code was scanned successfully is sufficient for Bob to
+ trust Alice's key, under the assumption that this communication is done
+ over a trusted medium (such as in-person).
+11. Both devices send an `m.key.verification.done` message.
+
+This flow allows Alice to verify Bob's key, and Bob to verify Alice's key.
+Alice verifies Bob's key because she can trust the QR code that Bob displays
+for her, as this is done over a trusted medium. Bob verifies Alice's key
+because Alice can trust the QR code that Bob displays, and Bob can trust Alice
+to tell him the result of the verification.
+
+#### Self-verification
+
+QR codes can also be used by a user to verify their own devices. These examples
+shows Alice verifying two devices, one of them (Osborne2) having cross-signing
+already set up, and the other one (Dynabook) having just logged in.
+
+In the first example, Osborne2 scans Dynabook:
+
+1. Alice logs into her new Dynabook and wants other users to be able to trust
+ it via cross-signing, and to trust other devices via cross-signing.
+2. Dynabook retrieves Alice's public cross-signing key from the server, and
+ displays a QR code that encodes:
+ - Dynabook's device key,
+ - what it thinks Alice's master key is, and
+ - a random shared secret.
+
+ Note that in this case, the QR code does not include Alice's master key in a
+ `key_` parameter, since Dynabook does not know whether it is trusted
+ or not.
+3. Osborne2 scans the QR code displayed by Dynabook. At this point, Osborne2
+ knows Dynabook's device key and can sign it with the self-signing key and
+ upload the signature, and can trust Dynabook for sending secrets via SSSS.
+ It also knows that Dynabook has the correct cross-signing key.
+4. Osborne2 tells Alice that the scan was successful, and sends the
+ `reciprocate` message containing the shared secret.
+5. Upon receipt of the `reciprocate` message, Dynabook (after checking the
+ shared secret) confirms with Alice that she successfully scanned the QR
+ code.
+6. Alice confirms.
+7. Dynabook now knows that it can trust Alice's cross-signing keys that it
+ fetched from the server.
+
+In the second example, Dynabook scans Osborne2:
+
+1. Alice logs into her new Dynabook and wants other users to be able to trust
+ it via cross-signing, and to trust other devices via cross-signing.
+2. Osborne2 notices that Dynabook is a new device. Osborne2 fetches Dynabook's
+ identity key and displays a QR code that encodes:
+ - what it thinks Dynabook's key is,
+ - Alice's master key, and
+ - a random shared secret.
+3. Dynabook scans the QR code shown by Osborne2. At this point, Dynabook knows
+ Alice's cross-signing key, and so it can trust it to sign other devices. It
+ also knows that Osborne2 as the correct key for it.
+4. Dynabook tells Alice that the scan is successful, and sends the
+ `reciprocate` message containing the shared secret.
+5. Upon receipt of the `reciprocate` message, Osborne2 (after checking the
+ shared secret) confirms with Alice that she successfully scanned the QR
+ code.
+6. Alice confirms.
+7. Osborne2 now knows that it has the correct device key for Dynabook, and can
+ sign it with the self-signing key and upload the signature. Osborne2 can
+ also trust Dynabook for sending secrets via SSSS.
+
+### Verification methods
+
+This proposal defines three verification methods that can be used in
+`m.key.verification.request` messages (see
+[MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241)).
+
+- `m.qr_code.show.v2`: means that the sender of the
+ `m.key.verification.request` message can show a QR code that the recipient
+ can scan. If the recipient can scan the QR code, it should allow the user to
+ do so. This method is never sent as part of a `m.key.verification.start`
+ message.
+- `m.qr_code.scan.v2`: means that the sender of the
+ `m.key.verification.request` message can scan a QR code displayed by the
+ recipient. If the recipient can display a QR code, it should allow the user
+ to display it so that the sender can scan it. This method is never sent as
+ part of a `m.key.verification.start` message.
+- `m.reciprocate.v1`: means that the sender can participate in a reciprocal
+ verification, either as initiator or responder, as described in the [Message
+ types](#message-types) section below.
+
+### QR code format
+
+The QR codes to be displayed and scanned using this format will encode binary
+strings in the general form:
+
+- the ASCII string "MATRIX"
+- one byte indicating the QR code version (must be `0x02`)
+- one byte indicating the QR code verification mode. May be one of the
+ following values:
+ - `0x00` verifying another user with cross-signing
+ - `0x01` self-verifying in which the current device does trust the master key
+ - `0x02` self-verifying in which the current device does not yet trust the
+ master key
+- the event ID or `transaction_id` of the associated verification
+ request event, encoded as:
+ - two bytes in network byte order (big-endian) indicating the length in
+ bytes of the ID as a UTF-8 string
+ - the ID as a UTF-8 string
+- the first key, as 32 bytes. The key to use depends on the mode field:
+ - if `0x00` or `0x01`, then the current user's own master cross-signing public key
+ - if `0x02`, then the current device's device key
+- the second key, as 32 bytes. The key to use depends on the mode field:
+ - if `0x00`, then what the device thinks the other user's master
+ cross-signing key is
+ - if `0x01`, then what the device thinks the other device's device key is
+ - if `0x02`, then what the device thinks the user's master cross-signing key
+ is
+- a random shared secret, as a byte string. It is suggested to use a secret
+ that is about 8 bytes long. Note: as we do not share the length of the
+ secret, and it is not a fixed size, clients will just use the remainder of
+ binary string as the shared secret.
+
+For example, if Alice displays a QR code encoding the following binary string:
+
+```
+ "MATRIX" |ver|mode| len | event ID
+ 4D 41 54 52 49 58 02 00 00 2D 21 41 42 43 44 ...
+| user's cross-signing key | other user's cross-signing key | shared secret
+ 00 01 02 03 04 05 06 07 ... 10 11 12 13 14 15 16 17 ... 20 21 22 23 24 25 26 27
+```
+
+this indicates that Alice is verifying another user (say Bob), in response to
+the request from event "$ABCD...", her cross-signing key is
+`0001020304050607...` (which is "AAECAwQFBg..." in base64), she thinks that
+Bob's cross-signing key is `1011121314151617...` (which is "EBESExQVFh..." in
+base64), and the shared secret is `2021222324252627` (which is "ICEiIyQlJic" in
+base64).
+
+### Message types
+
+#### `m.key.verification.start`
+
+Alice's device tells Bob's device that the QR code has been scanned.
+
+message contents:
+
+- `method`: `m.reciprocate.v1`
+- `m.relates_to`: as per [key verification framework](https://github.com/matrix-org/matrix-doc/pull/2241)
+- `secret`: the shared secret from the QR code, encoded using unpadded base64
+
+Example:
+
+```json
+{
+ "method": "m.reciprocate.v1",
+ "m.relates_to": {
+ "rel_type": "m.reference",
+ "event_id": "$event_id_of_verification_request"
+ },
+ "secret": "shared+secret"
+}
+```
+
+Note that this message could be sent by either the sender or the recipient of
+the `m.key.verification.request` message, depending on which user scanned the
+QR code.
+
+### Cancellation
+
+In addition to the cancellation codes specified in [the spec for
+`m.key.verification.cancel`](https://matrix.org/docs/spec/client_server/r0.5.0#m-key-verification-cancel),
+the following cancellation codes may be used:
+
+- `m.qr_code.invalid`: The QR code is invalid (e.g. it is not a URL of the
+ required form)
+
+The verification can also be cancelled with the error codes:
+
+- `m.key_mismatch`: if the QR code has keys that do not match the expected
+ value
+- `m.user_mismatch`: if the QR code is for a different user from what was expected
+
+Tradeoffs/Alternatives
+----------------------
+
+Other methods of verifying keys, which do not require scanning QR codes, are
+needed for devices that are unable to scan QR codes. One such method is
+[MSC1267](https://github.com/matrix-org/matrix-doc/issues/1267). Since the key
+verification framework allows for multiple methods to be supported, clients can
+allow users to use different methods depending on their capability.
+
+Rather than embedding the keys in the QR codes directly, the two clients could
+perform an exchange similar to
+[MSC1267](https://github.com/matrix-org/matrix-doc/issues/1267), and encoding
+the Short Authentication String code in the QR code. However, this means that
+the clients must exchange several messages before they can verify each other,
+which would delay showing the QR codes. This proposal is also simpler to
+implement.
+
+This proposal does not support the case of asynchronous verification, such as
+printing a QR code on a business card for others to scan. That may be address
+in a separate MSC.
+
+Security Considerations
+-----------------------
+
+The security of verifying Alice's key depends on Bob not hitting the "Verified"
+button (step 10 in the example flow) until after Alice's device indicates
+success or failure. Users have a tendency to click on buttons without reading
+what the screen says, but this is partially mitigated by the fact that it is
+unlikely that Bob will be interacting with the device while Alice is scanning
+and Alice's device will display the verification results immediately upon
+scanning. Also, Bob's device will not display the button until it receives the
+`m.key.verification.start` message that contains the shared secret from the QR
+code, which means that an attacker would need to be physically present while
+Alice and Bob verify. This issue can also be addressed by allowing Bob to
+easily undo the verification if Alice's device displays an error.
diff --git a/proposals/1756-cross-signing.md b/proposals/1756-cross-signing.md
index de08422a5ae..2ac8f706d84 100644
--- a/proposals/1756-cross-signing.md
+++ b/proposals/1756-cross-signing.md
@@ -528,7 +528,7 @@ look like:
If Bob replaces his Dynabook without re-verifying with Alice, this will split
the graph and Alice will not be able to verify Bob's other devices. In
-contrast, in this proposal, Alice and Bob sign each other's self-signing key
+contrast, in this proposal, Alice and Bob sign each other's master keys
with their user-signing keys, and the attestation graph would look like:

@@ -543,7 +543,9 @@ devices, as there may be stale attestations and revocations lingering around.
the signature created previously by the device making the attestation, or
whether it should be a statement that the device should not be trusted at all.)
In contrast, with this proposal, if a device is stolen, then only the
-user-signing key must be re-issued.
+keys for which the device had access to the private keys must be re-issued,
+along with any associated signatures. When the new keys are distributed, the
+old keys and their signatures will no longer be part of the attestation graph.
## Security considerations
diff --git a/proposals/1772-groups-as-rooms.md b/proposals/1772-groups-as-rooms.md
new file mode 100644
index 00000000000..1d431f84158
--- /dev/null
+++ b/proposals/1772-groups-as-rooms.md
@@ -0,0 +1,427 @@
+# Proposal for Matrix "spaces" (formerly known as "groups as rooms (take 2)")
+
+This MSC, and related proposals, supercede
+[MSC1215](https://github.com/matrix-org/matrix-doc/issues/1215).
+
+## Background and objectives
+
+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".
+
+Synapse and Element-Web currently implement an unspecced "groups" API (referred
+to as "`/r0/groups`" in this document) which attempts to provide this
+functionality (see
+[MSC971](https://github.com/matrix-org/matrix-doc/issues/971)). However,
+this is a complex API which has various problems (see
+[appendix](#appendix-problems-with-the-r0groups-api)).
+
+This proposal suggests a new approach where spaces are themselves represented
+by rooms, rather than a custom first-class entity. This requires minimal
+server changes.
+
+The existing `/r0/groups` API would be deprecated in Synapse and remain
+unspecified.
+
+## Proposal
+
+Each space is represented by its own room, known as a "space-room". The rooms
+within the space are determined by state events within the space-room.
+
+Space-rooms are distinguished from regular messaging rooms by the presence of
+a `'type': 'm.space'` property in the content of the `m.room.create` event.
+The value of the `type` property uses the Standardised Identifier Grammar from
+[MSC2758](https://github.com/matrix-org/matrix-doc/pull/2758). This allows clients to offer slightly customised user experience
+depending on the purpose of the room. Currently, no server-side behaviour is
+expected to depend on this property. A `type` property on the `m.room.create`
+event is used to ensure that a room cannot change between being a space-room
+and a non-space room. For more information, see the "Rejected Alternatives"
+section below. Additionally, no client behaviour is recommended for handling
+unknown room types given the potential for legacy data: clients are free to
+make their own decisions about hiding unknown room types from users, though
+should note that a future conversation-like type (for example) might be
+introduced and could be considered "unknown" by older versions of their client.
+
+As with regular rooms, public spaces are expected to have an alias, for example
+`#foo:matrix.org`, which can be used to refer to the space.
+
+Space-rooms may have `m.room.name`, `m.room.avatar` and `m.room.topic` state
+events in the same way as a normal room.
+
+Normal messages within a space-room are discouraged (but not blocked by the
+server): user interfaces are not expected to have a way to enter or display
+such messages. Space-rooms should be created with a power level for
+`events_default` of 100, to prevent the rooms accidentally/maliciously
+clogging up with messages from random members of the space.
+
+### Membership of spaces
+
+Users can be members of spaces (represented by `m.room.member` state events as
+normal). The existing [`m.room.history_visibility`
+mechanism](https://matrix.org/docs/spec/client_server/r0.6.1#room-history-visibility)
+controls whether membership of the space is required to view the room list,
+membership list, etc. "Public" or "community" spaces would be set to
+`world_readable` to allow clients to see the directory of rooms within the
+space by peeking into the space-room (thus avoiding the need to add
+`m.room.member` events to the event graph within the room).
+
+Join rules, invites and 3PID invites work as for a normal room. In order for
+clients to distinguish space invites from room invites, all invites must now
+include the `m.room.create` event in their `invite_state` and `knock_state`.
+
+### Relationship between rooms and spaces
+
+The intention is that rooms and spaces form a hierarchy, which clients can use
+to structure the user's room list into a tree view. The parent/child
+relationship can be expressed in one of two ways:
+
+ 1. The admins of a space can advertise rooms and subspaces for their space by
+ setting `m.space.child` state events. The `state_key` is the ID of a child
+ room or space, and the content must contain a `via` key which gives a list
+ of candidate servers that can be used to join the room. Something like:
+
+ ```jsonc
+ // a child room
+ {
+ "type": "m.space.child",
+ "state_key": "!abcd:example.com",
+ "content": {
+ "via": ["example.com", "test.org"]
+ }
+ }
+
+ // a child room with an ordering.
+ {
+ "type": "m.space.child",
+ "state_key": "!efgh:example.com",
+ "content": {
+ "via": ["example.com"],
+ "order": "abcd"
+ }
+ }
+
+ // no longer a child room
+ {
+ "type": "m.space.child",
+ "state_key": "!jklm:example.com",
+ "content": {}
+ }
+ ```
+
+ Children where `via` is not present or invalid (not an array) are ignored.
+
+ The `order` key is a string which is used to provide a default ordering of
+ siblings in the room list. (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 `\x7E` (`~`), or
+ consist of more than 50 characters, are forbidden and the field should be
+ ignored if received.)
+
+ 2. Separately, rooms can claim parents via the `m.space.parent` state
+ event.
+
+ Similar to `m.space.child`, the `state_key` is the ID of the parent space,
+ and the content must contain a `via` key which gives a list of candidate
+ servers that can be used to join the parent.
+
+ ```jsonc
+ {
+ "type": "m.space.parent",
+ "state_key": "!space:example.com",
+ "content": {
+ "via": ["example.com"],
+ "canonical": true
+ }
+ }
+ ```
+
+ Parents where `via` is not present or invalid (not an array) are ignored.
+
+ `canonical` determines whether this is the main parent for the space. When
+ a user joins a room with a canonical parent, clients may switch to view the
+ room in the context of that space, peeking into it in order to find other
+ rooms and group them together. In practice, well behaved rooms should only
+ have one `canonical` parent, but given this is not enforced: if multiple
+ are present the client should select the one with the lowest room ID, as
+ determined via a lexicographic ordering of the Unicode code-points.
+
+ To avoid abuse where a room admin falsely claims that a room is part of a
+ space that it should not be, clients could ignore such `m.space.parent`
+ events unless either (a) there is a corresponding `m.space.child` event in
+ the claimed parent, or (b) the sender of the `m.space.child` event has a
+ sufficient power-level to send such an `m.space.child` event in the
+ parent. (It is not necessarily required that that user currently be a
+ member of the parent room - only the `m.room.power_levels` event is
+ inspected.) [Checking the power-level rather than requiring an *actual*
+ `m.space.child` event in the parent allows for "secret" rooms (see below).]
+
+ Where the parent space also claims a parent, clients can recursively peek
+ into the grandparent space, and so on.
+
+This structure means that rooms can end up appearing multiple times in the
+room list hierarchy, given they can be children of multiple different spaces
+(or have multiple parents in different spaces).
+
+In a typical hierarchy, we expect *both* parent->child and child->parent
+relationships to exist, so that the space can be discovered from the room, and
+vice versa. Occasions when the relationship only exists in one direction
+include:
+
+ * User-curated lists of rooms: in this case the space will not be listed as a
+ parent of the room.
+
+ * "Secret" rooms: rooms where the admin does not want the room to be
+ advertised as part of a given space, but *does* want the room to form part
+ of the hierarchy of that space for those in the know.
+
+Cycles in the parent->child and child->parent relationships are *not*
+permitted, but clients (and servers) should be aware that they may be
+encountered, and MUST spot and break cycles rather than infinitely looping.
+
+### Suggested children
+
+Space admins can mark particular children of a space as "suggested". This
+mainly serves as a hint to clients that that they can be displayed differently
+(for example by showing them eagerly in the room list), though future
+server-side interfaces (such as the summary API proposed in
+[MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946)) might also
+make use of it.
+
+A suggested child is identified by a `"suggested": true` property in the
+`m.space.child` event:
+
+
+```jsonc
+{
+ "type": "m.space.child",
+ "state_key": "!abcd:example.com",
+ "content": {
+ "via": ["example.com", "test.org"],
+ "suggested": true
+ }
+}
+```
+
+A child which is missing the `suggested` property is treated identically to a
+child with `"suggested": false`. A suggested child may be a room or a subspace.
+
+### Extended "room invite state"
+
+The specification is currently vague about what room state should be available
+to users that have been invited to a room, though the Federation API spec does
+recommend that the `invite_room_state` sent over federation via [PUT
+`/_matrix/federation/v2/invite`](https://matrix.org/docs/spec/server_server/r0.1.4#put-matrix-federation-v2-invite-roomid-eventid)
+should include "the join rules, canonical alias, avatar, and name of the room".
+
+This MSC proposes adding `m.room.create` to that list, so that the recipient of
+an invite can distinguish invites to spaces from other invites.
+
+## Future extensions
+
+The following sections are not blocking parts of this proposal, but are
+included as a useful reference for how we imagine it will be extended in future.
+
+### Auto-joined children
+
+We could add an `auto_join` flag to `m.space.child` events to allow a space
+admin to list the sub-spaces and rooms in that space which should be
+automatically joined by members of that space.
+
+This would be distinct from a force-join: the user could subsequently part any
+auto-joined room if they desire.
+
+Joining would be performed by the client. This could possibly be sped up by
+using a summary API (such as that proposed in
+[MSC2946](https://github.com/matrix-org/matrix-doc/pull/2946)) to get a summary
+of the spacetree to be joined, and then using a batch join API to join
+whichever subset of it makes most sense for the client's UX.
+
+Obviously auto-joining can be a DoS vector, and we consider it to be antisocial
+for a space to try to autojoin its members to more than 100 children (in total).
+
+Clients could display the auto-joined children in the room list whenever the
+space appears in the list - thus helping users discover other rooms in a space
+even if they're not joined to that space. For instance, if you join
+`#matrix:matrix.org`, your client could show that room in the context of its
+parent space, with that space's auto-joined children shown alongside it as
+siblings.
+
+### Restricting access to the spaces membership list
+
+In the existing `/r0/groups` API, the group server has total control over the
+visibility of group membership, as seen by a given querying user. In other
+words, arbitrary users can see entirely different views of a group at the
+server's discretion.
+
+Whilst this is very powerful for mapping arbitrary organisational structures
+into Matrix, it may be overengineered. Instead, the common case is (we believe)
+a space where some users are publicly visible as members, and others are not.
+
+One way of achieving this would be to create a separate space for the
+private members - e.g. have `#foo:matrix.org` and `#foo-private:matrix.org`.
+`#foo-private:matrix.org` is set up with `m.room.history_visibility` to not to
+allow peeking; you have to be joined to see the members.
+
+It's worth noting that any member of a space can currently see who else is a
+member of that space, which might pose privacy problems for sensitive spaces.
+While the server itself will inevitably track the space membership in state
+events, a future MSC could restrict the membership from being sent to clients.
+This is essentially the same as
+[matrix-doc#1653](https://github.com/matrix-org/matrix-doc/issues/1653).
+
+### Flair
+
+("Flair" is a term we use to describe a small badge which appears next to a
+user's displayname to advertise their membership of a space.)
+
+The flair image for a group is given by the room avatar. (In future it might
+preferable to use hand-crafted small resolution images: see
+[matrix-doc#1778](https://github.com/matrix-org/matrix-doc/issues/1778).
+
+One way this might be implemented is:
+
+ * User publishes the spaces they wish to announce on their profile
+ ([MSC1769](https://github.com/matrix-org/matrix-doc/issues/1769)
+ as an `m.flair` state event: it lists the spaces which they are advertising.
+
+ * When a client wants to know the current flair for a set of users (i.e.
+ those which it is currently displaying in the timeline), it peeks the
+ profile rooms of those users. (Ideally there would be an API to support
+ peeking multiple rooms at once to facilitate this.)
+
+ * The client must check that the user is *actually* a member of the advertised
+ spaces. Nominally it can do this by peeking the membership list of the
+ space; however for efficiency we could expose a dedicated Client-Server API
+ to do this check (and both servers and clients can cache the results fairly
+ aggressively.)
+
+## Related MSCs
+
+ * [MSC2946](https://github.com/matrix-org/matrix-doc/issues/2946): Spaces
+ Summary API.
+
+ * [MSC2962](https://github.com/matrix-org/matrix-doc/issues/2962): Managing
+ power levels via Spaces.
+
+ * [MSC3083](https://github.com/matrix-org/matrix-doc/issues/3083): Restricting
+ room membership based on space membership.
+
+ * [MSC2753](https://github.com/matrix-org/matrix-doc/issues/2753) for
+ effective peeking over the C/S API.
+
+ * [MSC2444](https://github.com/matrix-org/matrix-doc/issues/2444) (or similar)
+ for effective peeking over Federation.
+
+## Security considerations
+
+None at present.
+
+## Potential issues
+
+* If the membership of a space would be large (for example: an organisation of
+ several thousand people), this membership has to be copied entirely into the
+ room, rather than querying/searching incrementally.
+
+* If the membership list is based on an external service such as LDAP, it is
+ hard to keep the space membership in sync with the LDAP directory. In
+ practice, it might be possible to do so via a nightly "synchronisation" job
+ which searches the LDAP directory, or via "AD auditing".
+
+* No allowance is made for exposing different 'views' of the membership list to
+ different querying users. (It may be possible to simulate this behaviour
+ using smaller spaces).
+
+* The requirement that `m.space.parent` links be ignored unless the sender has a
+ high PL in the parent room could lead to suprising effects where a parent
+ link suddenly ceases to take effect because a user loses their PL in the
+ parent room. This is mitigated in the general case by honouring the parent
+ link when there is a corresponding `m.space.child` event, however it remains
+ a problem for "secret" rooms.
+
+* The `via` servers listed in the `m.space.child` and `m.space.parent` events
+ could get out of date, and will need to be updated from time to time. This
+ remains an unsolved problem.
+
+## Rejected alternatives
+
+### Use a separate state event for type of room
+
+[MSC1840](https://github.com/matrix-org/matrix-doc/pull/1840) proposes the use
+of a separate `m.room.type` state event to distinguish different room types.
+This implies that rooms can dynamically switch between being a Space, and
+being a regular non-Space room. That is not a usecase we consider useful, and
+allowing it would impose significant complexity on client and server
+implementations. Specifically, client and server implementations who store
+spaces separately from rooms would have to support migrating back and forth
+between them and dealing with the ambiguities of `room_id`s no longer pointing
+to valid spaces, etc.
+
+### Use a different sigil/twigil for spaces
+
+Groups used + as a sigil to differentiate them from rooms (e.g. +matrix:matrix.org).
+We considered doing similar for Spaces, e.g. a #+ twigil or reuse the + sigil,
+but concluded that the resulting complexity and exoticism is not worth it.
+This means that clients such as matrix.to have to peek into rooms to find out their
+`type` before being able to display an appropriate UI, and users will not know
+whether #matrix:matrix.org is a room or a space without using a client (e.g. if
+reading an advert). It also means that if the client UI requires a space alias the
+client will need to validate the entered data serverside.
+
+## Unstable prefix
+
+The following mapping will be used for identifiers in this MSC during
+development:
+
+Proposed final identifier | Purpose | Development identifier
+------------------------------- | ------- | ----
+`type` | property in `m.room.create` | `org.matrix.msc1772.type`
+`m.space` | value of `type` in `m.room.create` | `org.matrix.msc1772.space`
+`m.space.child` | event type | `org.matrix.msc1772.space.child`
+`m.space.parent` | event type | `org.matrix.msc1772.space.parent`
+
+## History
+
+ * This replaces [MSC1215](https://docs.google.com/document/d/1ZnAuA_zti-K2-RnheXII1F1-oyVziT4tJffdw1-SHrE).
+ * Other thoughts that led into this are [documented](https://docs.google.com/document/d/1hljmD-ytdCRL37t-D_LvGDA3a0_2MwowSPIiZRxcabs).
+
+## Appendix: problems with the `/r0/groups` API
+
+The existing `/r0/groups` API, as proposed in
+[MSC971](https://github.com/matrix-org/matrix-doc/issues/971), has various
+problems, including:
+
+ * It is a large API surface to implement, maintain and spec - particularly for
+ all the different clients out there.
+ * Much of the API overlaps significantly with mechanisms we already have for
+ managing rooms:
+ * Tracking membership identity
+ * Tracking membership hierarchy
+ * Inviting/kicking/banning user
+ * Tracking key/value metadata
+ * There are membership management features which could benefit rooms which
+ would also benefit groups and vice versa (e.g. "auditorium mode")
+ * The current implementations on Riot Web/iOS/Android all suffer bugs and
+ issues which have been solved previously for rooms.
+ * no local-echo of invites
+ * failures to set group avatars
+ * ability to specify multiple admins
+ * It doesn't support pushing updates to clients (particularly for flair
+ membership): https://github.com/vector-im/riot-web/issues/5235
+ * It doesn't support third-party invites.
+ * Groups could benefit from other features which already exist today for rooms
+ * e.g. Room Directories
+ * Groups are centralised, rather than being replicated across all
+ participating servers.
diff --git a/proposals/1960-integrations-openid.md b/proposals/1960-integrations-openid.md
new file mode 100644
index 00000000000..6f33001fb3a
--- /dev/null
+++ b/proposals/1960-integrations-openid.md
@@ -0,0 +1,192 @@
+# MSC1960: OpenID Connect information exchange for widgets
+
+Widgets are currently left with no options to verify the user's ID, making it hard for
+personalized and authenticated widgets to exist. The spec says the `$matrix_user_id`
+template variable cannot be relied upon due to how easy it is to faslify, which is true.
+
+This MSC aims to solve the problem with verifiably accurate OpenID Connect credentials.
+
+As of writing, the best resource to learn more about the widgets spec is the following
+spec PR: https://github.com/matrix-org/matrix-doc/pull/2764
+
+## Proposal
+
+Typically widgets which need to accurately verify the user's identity will also have a
+backend service of some kind. This backend service likely already uses the integration
+manager authentication APIs introduced by [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961).
+
+Through using the same concepts from MSC1961, the widget can verify the user's identity
+by requesting a fresh OpenID Connect credential object to pass along to its backend, like
+the integration manager which might be running it.
+
+The protocol sequence defined here is based upon the previous discussion in the Element Web
+issue tracker: https://github.com/vector-im/element-web/issues/7153
+
+It is proposed that after the capabilities negotation, the widget can ask the client for
+an OpenID Connect credential object so it can pass it along to its backend for validation.
+The request SHOULD result in the user being prompted to confirm that the widget can have
+their information. Because of this user interaction, it's not always possible for the user
+to complete the approval within the 10 second suggested timeout by the widget spec. As
+such, the initial request by the widget can have one of three states:
+
+1. The client indicates that the user is being prompted (to be followed up on).
+2. The client sends over credentials for the widget to verify.
+3. The client indicates the request was blocked/denied.
+
+The initial request from the widget looks as follows:
+
+```json
+{
+ "api": "fromWidget",
+ "action": "get_openid",
+ "requestId": "AAABBB",
+ "widgetId": "CCCDDD",
+ "data": {}
+}
+```
+
+Which then receives a response which has a `state` field alongside potentially the credentials
+to be verified. Matching the order of possible responses above, here are examples:
+
+```json
+{
+ "api": "fromWidget",
+ "action": "get_openid",
+ "requestId": "AAABBB",
+ "widgetId": "CCCDDD",
+ "data": {},
+ "response": {
+ "state": "request"
+ }
+}
+```
+
+```json
+{
+ "api": "fromWidget",
+ "action": "get_openid",
+ "requestId": "AAABBB",
+ "widgetId": "CCCDDD",
+ "data": {},
+ "response": {
+ "state": "allowed",
+ "access_token": "s3cr3t",
+ "token_type": "Bearer",
+ "matrix_server_name": "example.org",
+ "expires_in": 3600
+ }
+}
+```
+
+```json
+{
+ "api": "fromWidget",
+ "action": "get_openid",
+ "requestId": "AAABBB",
+ "widgetId": "CCCDDD",
+ "data": {},
+ "response": {
+ "state": "blocked"
+ }
+}
+```
+
+The credential information is directly copied from the `/_matrix/client/r0/user/:userId/openid/request_token`
+response.
+
+In the case of `state: "request"`, the user is being asked to approve the widget's attempt to
+verify their identity. To ensure that future requests are quicker, clients are encouraged to
+include a "remember this widget" option to make use of the immediate `state: "allowed"` or
+`state: "blocked"` responses above.
+
+There is no timeout associated with the user making their selection. Once a user does make
+a selection (allow or deny the request), the client sends a `toWidget` request to indicate the
+result, using a very similar structure to the above immediate responses:
+
+```json
+{
+ "api": "toWidget",
+ "action": "openid_credentials",
+ "requestId": "EEEFFF",
+ "widgetId": "CCCDDD",
+ "data": {
+ "state": "allowed",
+ "original_request_id": "AAABBB",
+ "access_token": "s3cr3t",
+ "token_type": "Bearer",
+ "matrix_server_name": "example.org",
+ "expires_in": 3600
+ }
+}
+```
+
+```json
+{
+ "api": "toWidget",
+ "action": "openid_credentials",
+ "requestId": "EEEFFF",
+ "widgetId": "CCCDDD",
+ "data": {
+ "state": "blocked",
+ "original_request_id": "AAABBB"
+ }
+}
+```
+
+`original_request_id` is the `requestId` of the `get_openid` request which started the prompt,
+for the widget's reference.
+
+The widget acknowledges receipt of the credentials with an empty `response` object.
+
+A typical sequence diagram for this flow is as follows:
+
+```
++-------+ +---------+ +---------+
+| User | | Client | | Widget |
++-------+ +---------+ +---------+
+ | | |
+ | | Capabilities negotiation |
+ | |----------------------------------------->|
+ | | |
+ | | Capabilities negotiation |
+ | |<-----------------------------------------|
+ | | |
+ | | fromWidget get_openid request |
+ | |<-----------------------------------------|
+ | | |
+ | | ack with state "request" |
+ | |----------------------------------------->|
+ | | |
+ | Ask if the widget can have information | |
+ |<--------------------------------------------| |
+ | | |
+ | Approve | |
+ |-------------------------------------------->| |
+ | | |
+ | | toWidget openid_credentials request |
+ | |----------------------------------------->|
+ | | |
+ | | acknowledge request (empty response) |
+ | |<-----------------------------------------|
+```
+
+Prior to this proposal, widgets could use an undocumented `scalar_token` parameter if the client chose to
+send it to the widget. Clients typically chose to send it if the widget's URL matched a whitelist for URLs
+the client trusts. With the widget specification as written, widgets cannot rely on this behaviour.
+
+Widgets may wish to look into cookies and other storage techniques to avoid continously requesting
+credentials. Widgets should also look into [MSC1961](https://github.com/matrix-org/matrix-doc/pull/1961)
+for information on how to properly verify the OpenID Connect credentials it will be receiving. The
+widget is ultimately responsible for how it deals with the credentials, though the author recommends
+handing it off to an integration manager's `/register` endpoint to acquire a single token string
+instead.
+
+An implementation of this proposal's early draft is here: https://github.com/matrix-org/matrix-react-sdk/pull/2781
+
+## Security considerations
+
+The user is explicitly kept in the loop to avoid automatic and silent harvesting of private information.
+Clients must ask the user for permission to send OpenID Connect information to a widget, but may optionally allow
+the user to always allow/deny the widget access. Clients are encouraged to notify the user when future
+requests are automatically handled due to the user's prior selection (eg: an unobtrusive popup saying
+"hey, your sticker picker asked for your information. [Block future requests]").
diff --git a/proposals/2010-spoilers.md b/proposals/2010-spoilers.md
index 1d9f4dc9f11..f327fbb579d 100644
--- a/proposals/2010-spoilers.md
+++ b/proposals/2010-spoilers.md
@@ -3,10 +3,10 @@ Sometimes, while you want to put text into a spoiler to not have people accident
For example, when discussing a new movie or a TV series, not everyone might have watched it yet.
In such cases it would make sense to add a spoiler so that only those who have seen the movie or
-don't mind spoilers read the content.
+don't mind spoilers read the content.
Another example would be e.g. in mental health communities where certain people have certain
triggers. People could put talking about abuse or the like into a spoiler, to not accidentally
-trigger anyone just reading along the conversation.
+trigger anyone just reading along the conversation.
Furthermore this is helpful for bridging to other networks that already have a spoiler feature.
To render the spoiler the content is hidden and then revealed once interacted somehow
@@ -14,7 +14,7 @@ To render the spoiler the content is hidden and then revealed once interacted so
## Proposal
This proposal is about adding a new attribute to the `formatted_body` of messages with type
-`m.room.message` and msgtype `m.text`.
+`m.room.message` and message types which support the `org.matrix.custom.html` format.
It adds a new attribute, `data-mx-spoiler`, to the `` tag. If the attribute is present the
contents of the span tag should be rendered as a spoiler. Optionally, you can specify a reason for
@@ -22,6 +22,9 @@ the spoiler by setting the attribute string. It could be rendered, for example,

+The plaintext fallback supported by the `body` is optional. A recommendation for clients is included
+below.
+
To preserve the semantics of a spoiler in the plaintext fallback it is recommended to upload the contents of the spoiler
as a text file and then link this: `[Spoiler](mxc://someserver/somefile)` and
`[Spoiler for reason](mxc://someserver/somefile)` respectively.
diff --git a/proposals/2184-allow-html-details.md b/proposals/2184-allow-html-details.md
new file mode 100644
index 00000000000..41e337221e2
--- /dev/null
+++ b/proposals/2184-allow-html-details.md
@@ -0,0 +1,45 @@
+# Allow the HTML `` tag in messages
+
+Currently, there's no available method for bot developers - among others - to provide larger informative
+messages in a room without disrupting the conversation for all other users. This often causes bots
+to appear needlessly "spammy".
+
+## Proposal
+
+This proposal suggests adding the existing HTML tags for `` and `` to the list of
+allowed tags in formatted Matrix messages. Which would allow for larger messages to be shown simply
+as smaller - and less intrusive - summaries for the users who are not interested in their full contents.
+
+## Tradeoffs
+
+An alternative method to provide a summary/details split could possibly be done through [MSC1767],
+with the details and summaries being specified through repeated bodies with added metadata. This could
+then also allow clients better autonomy in deciding what to display - or how to structure the information.
+
+However, allowing the use of the `` and `` tags would still offer richer formatting
+capabilities even in such messages, especially as more than one detail/summary block could be included
+in a single message.
+
+## Potential issues
+
+Allowing more HTML tags in formatted messages could cause more work for client developers, as they would
+have to fit a larger and more diverse corpus of input into their designs and user experience.
+However, these are both well documented - and implemented - HTML tags, so there is plenty of prior work
+available to take example from in how to incorporate them.
+
+Additionally, as the addition of these tags will make it possible to fit even more information into
+a single message without worry of overflowing the room, any client that doesn't render the formatting
+of the body might end up with a lessened user experience - from either an under- or overflow of information.
+
+The onus on ensuring that the unformatted body is a reasonable representation of the message has always
+been on the user or bot writing the formatted message though, so providing an improved ability for
+formatting should not negatively affect the experience for any clients that simply render unformatted
+text.
+
+## Security considerations
+
+Allowing more HTML tags in client rendering could lead to a wider attack surface for DOM-based exploits.
+However, these tags are very simple in both function and design, so any possible attack surface they
+would offer would be minimal at best.
+
+[MSC1767]: https://github.com/matrix-org/matrix-doc/blob/matthew/msc1767/proposals/1767-extensible-events.md
diff --git a/proposals/2241-e2e-verification-in-dms.md b/proposals/2241-e2e-verification-in-dms.md
new file mode 100644
index 00000000000..dc2797f218d
--- /dev/null
+++ b/proposals/2241-e2e-verification-in-dms.md
@@ -0,0 +1,314 @@
+# Key verification in DMs
+
+Currently, key verification is done using `to_device` messages. However, since
+`to_device` messages are not part of a timeline, there is no user-visible
+record of the key verification.
+
+As well, the current key verification framework does not provide any feedback
+when interacting with clients that do not support it; if a client does not
+support the key verification framework, there is no way for users to discover
+this other than waiting for a while and noticing that nothing is happening.
+
+This proposal will solve both problems.
+
+## Proposal
+
+The current [key verification
+framework](https://matrix.org/docs/spec/client_server/r0.5.0#key-verification-framework)
+will be replaced by a new framework that uses room messages rather than
+`to_device` messages. Key verification messages will be sent in a [Direct
+Messaging](https://matrix.org/docs/spec/client_server/r0.5.0#id185) room. If
+there is no Direct Messaging room between the two users involved, the client
+that initiates the key verification will create one.
+
+In this proposal, we use "Alice" to denote the user who initiates the key
+verification, and "Bob" to denote the other user involved in the key
+verification.
+
+### General framework
+
+#### Requesting a key verification
+
+To request a key verification, Alice will send an `m.room.message` event with the
+following properties in its contents:
+
+- `body`: a fallback message to alert users that their client does not support
+ the key verification framework, and that they should use a different method
+ to verify keys. For example, "Alice is requesting to verify keys with you.
+ However, your client does not support this method, so you will need to use
+ the legacy method of key verification."
+
+ Clients that do support the key verification framework should hide the body
+ and instead present the user with an interface to accept or reject the key
+ verification.
+
+ The event may also contain `format` and `formatted_body` properties as
+ described in the [m.room.message
+ msgtypes](https://matrix.org/docs/spec/client_server/r0.5.0#m-room-message-msgtypes)
+ section of the spec. Clients that support the key verification should
+ similarly hide these from the user.
+- `msgtype`: `m.key.verification.request`
+- `methods`: the verification methods supported by Alice's client
+- `to`: Bob's Matrix ID. Users should only respond to verification requests if
+ they are named in this field. Users who are not named in this field and who
+ did not send this event should ignore all other events that have a
+ `m.reference` relationship with this event.
+- `from_device`: Alice's device ID. This is required since some verification
+ methods may use the device IDs as part of the verification process.
+
+Key verifications will be identified by the event ID of the key verification
+request event.
+
+Clients should ignore verification requests that have been accepted or
+cancelled, or if they do not belong to the sending or target users.
+
+The way that clients display this event can depend on which user and device the
+client belongs to, and what state the verification is in. For example:
+
+- If the verification has been completed (there is an `m.key.verification.done`
+ or `m.key.verification.cancel` event), the client can indicate that the
+ verification was successful or had an error.
+- If the verification has been accepted (there is an `m.key.verification.start`
+ event) but has not been completed, the two devices involved can indicate that
+ the verification is in progress and can use this event as a place in the
+ room's timeline to display progress of the key verification and to interact
+ with the user as necessary. Other devices can indicate that the verification
+ is in progress on other devices.
+- If the verification has not been accepted, clients for the target user can
+ indicate that a verification has been requested and allow the user to accept
+ the verification on that device. The sending client can indicate that it is
+ waiting for the request to be accepted, and the sending user's other clients
+ can indicate the that a request was initiated on a different device.
+
+Clients may choose to display or not to display events of any other type that
+reference the original request event; but it must not have any effect on the
+verification itself.
+
+#### Accepting a key verification
+
+To accept a key verification, Bob will send an `m.key.verification.ready` event
+with the following properties in its contents:
+
+- `m.relates_to`: an object with the properties:
+ - `rel_type`: `m.reference`
+ - `event_id`: the event ID of the key verification request that is being
+ accepted
+- `methods`: an array of verification methods that the device supports
+- `from_device`: Bob's device ID. This is required since some verification
+ methods may use the device IDs as part of the verification process.
+
+(Note: the form of the `m.relates_to` property is based on the current state of
+[MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674), but is
+independent from it since this MSC does not rely on any aggregations features.)
+
+Clients should ignore `m.key.verification.ready` events that correspond to
+verification requests that they did not send.
+
+After this, either Alice or Bob may start the verification by sending an
+`m.key.verification.start` event with the following properties in its contents:
+
+- `m.relates_to`: an object with the properties:
+ - `rel_type`: `m.reference`
+ - `event_id`: the event ID of the key verification request that is being
+ started
+- `method`: the key verification method that is being used. This should be a
+ method that both Alice's and Bob's devices support.
+- `from_device`: The user's device ID.
+
+If both Alice and Bob send an `m.key.verification.start` message, and they both
+specify the same verification method, then the event sent by the user whose
+user ID is the smallest is used, and the other event is ignored. If they both
+send an `m.key.verification.start` message and the method is different, then
+the verification should be cancelled with a `code` of `m.unexpected_message`.
+
+After the `m.key.verification.start` event is sent, the devices may exchange
+messages (if any) according to the verification method in use.
+
+#### Rejecting a key verification
+
+To reject a key verification, Alice or Bob will send an
+`m.key.verification.cancel` event with the following properties in its
+contents:
+
+- `m.relates_to`: an object with the properties:
+ - `rel_type`: `m.reference`
+ - `event_id`: the event ID of the key verification that is being cancelled
+- `reason`: A human readable description of the `code`. The client should only
+ rely on this string if it does not understand the `code`.
+- `code`: The error code for why the process/request was cancelled by the
+ user. The contents are the same as the `code` property of the currently
+ defined [`m.key.verification.cancel` to-device
+ event](https://matrix.org/docs/spec/client_server/r0.5.0#m-key-verification-cancel),
+ or as defined for specific key verification methods.
+
+This message may be sent at any point in the key verification process. Any
+subsequent key verification messages relating to the same request are ignored.
+However, this does not undo any verifications that have already been done.
+
+#### Concluding a key verification
+
+When the other user's key is verified and no more messages are expected, each
+party will send an `m.key.verification.done` event with the following
+properties in its contents:
+
+- `m.relates_to`: an object with the properties:
+ - `rel_type`: `m.reference`
+ - `event_id`: the event ID of the key verification that is being concluded
+
+This provides a record within the room of the result of the verification.
+
+Any subsequent key verification messages relating to the same request are
+ignored.
+
+Although a client may have successfully completed its side of the verification,
+it may wait until receiving an `m.key.verification.done` (or
+`m.key.verification.cancel`) event from the other device before informing the
+user that the verification was successful or unsuccessful.
+
+#### Other events
+
+Key verification methods may define their own event types, or extensions to the
+above event types. All events sent as part of a key verification process
+should have an `m.relates_to` property as defined for
+`m.key.verification.accept` or `m.key.verification.cancel` events.
+
+Clients should ignore events with an `m.relates_to` that have a `rel_type` of
+`m.reference` that refer to a verification where it is neither the requester
+nor the accepter.
+
+Clients should not redact or edit verification messages. A client may ignore
+redactions or edits of key verification messages, or may cancel the
+verification with a `code` of `m.unexpected_message` when it receives a
+redaction or edit.
+
+### SAS verification
+
+The messages used in SAS verification are the same as those currently defined,
+except that instead of the `transaction_id` property, an `m.relates_to`
+property, as defined above, is used instead.
+
+If the key verification messages are encrypted, the hash commitment sent in the
+`m.key.verification.accept` message MUST be based on the decrypted
+`m.key.verification.start` message contents, and include the `m.relates_to`
+field, even if the decrypted message contents do not include that field. For
+example, if Alice sends a message to start the SAS verification:
+
+```json
+{
+ "content": {
+ "algorithm": "m.megolm.v1.aes-sha2",
+ "ciphertext": "ABCDEFG...",
+ "device_id": "Dynabook",
+ "sender_key": "alice+sender+key",
+ "session_id": "session+id",
+ "m.relates_to": {
+ "rel_type": "m.reference",
+ "event_id": "$verification_request_event"
+ }
+ },
+ "event_id": "$event_id",
+ "origin_server_ts": 1234567890,
+ "sender": "@alice:example.org",
+ "type": "m.room.encrypted",
+ "room_id": "!room_id:example.org"
+}
+```
+
+which, when decrypted, yields:
+
+```json
+{
+ "room_id": "!room_id:example.org",
+ "type": "m.key.verification.start",
+ "content": {
+ "from_device": "Dynabook",
+ "hashes": [
+ "sha256"
+ ],
+ "key_agreement_protocols": [
+ "curve25519"
+ ],
+ "message_authentication_codes": [
+ "hkdf-hmac-sha256"
+ ],
+ "method": "m.sas.v1",
+ "short_authentication_string": [
+ "decimal",
+ "emoji"
+ ]
+ }
+}
+```
+
+then the hash commitment will be based on the message contents:
+
+```json
+{
+ "from_device": "Dynabook",
+ "hashes": [
+ "sha256"
+ ],
+ "key_agreement_protocols": [
+ "curve25519"
+ ],
+ "message_authentication_codes": [
+ "hkdf-hmac-sha256"
+ ],
+ "method": "m.sas.v1",
+ "short_authentication_string": [
+ "decimal",
+ "emoji"
+ ],
+ "m.relates_to": {
+ "rel_type": "m.reference",
+ "event_id": "$verification_request_event"
+ }
+}
+```
+
+## Alternatives
+
+Messages sent by the verification methods, after the initial key verification
+request message, could be sent as to-device messages. The
+`m.key.verification.ready`, `m.key.verification.cancel`, and
+`m.key.verification.done` messages must be still be sent in the room, as the
+`m.key.verification.ready` notifies the sender's other devices that the request
+has been acknowledged, and the `m.key.verification.cancel` and
+`m.key.verification.done` provide a record of the status of the key
+verification.
+
+However, it seems more natural to have all messages sent via the same
+mechanism.
+
+## Potential issues
+
+If a user wants to verify their own device, this will require the creation of a
+Direct Messaging room with themselves. Instead, clients may use the current
+`to_device` messages for verifying the user's other devices.
+
+Direct Messaging rooms could have end-to-end encryption enabled, and some
+clients can be configured to only send decryption keys to verified devices.
+Key verification messages should be granted an exception to this (so that
+decryption keys are sent to all of the target user's devices), or should be
+sent unencrypted, so that unverified devices will be able to be verified.
+
+Users might have multiple Direct Messaging rooms with other users. In this
+case, clients could need to prompt the user to select the room in which they
+want to perform the verification, or could select a room.
+
+## Security considerations
+
+Key verification is subject to the room's visibility settings, and may be
+visible to other users in the room. However, key verification does not rely on
+secrecy, so this will no affect the security of the key verification. This may
+reveal to others in the room that Alice and Bob know each other, but this is
+already revealed by the fact that they share a Direct Messaging room.
+
+This framework allows users to see what key verifications they have performed
+in the past. However, since key verification messages are not secured, this
+should not be considered as authoritative.
+
+## Conclusion
+
+By using room messages to perform key verification rather than `to_device`
+messages, the user experience of key verification can be improved.
diff --git a/proposals/2265-email-lowercase.md b/proposals/2265-email-lowercase.md
index 5a1db682561..e4fe53139bd 100644
--- a/proposals/2265-email-lowercase.md
+++ b/proposals/2265-email-lowercase.md
@@ -23,8 +23,8 @@ Sydent.
This proposal suggests changing the specification of the e-mail 3PID type in
[the Matrix spec appendices](https://matrix.org/docs/spec/appendices#pid-types)
to mandate that, before any processing, e-mail addresses must go through a full
-case folding based on [the unicode mapping
-file](https://www.unicode.org/Public/8.0.0/ucd/CaseFolding.txt), on top of
+case folding as described under "Caseless Matching" in
+[chapter 5 of the unicode standard](https://www.unicode.org/versions/Unicode13.0.0/ch05.pdf#G21790), on top of
having their domain lowercased.
This means that `Strauß@Example.com` must be considered as being the same e-mail
diff --git a/proposals/2312-matrix-uri.md b/proposals/2312-matrix-uri.md
new file mode 100644
index 00000000000..45bb2187279
--- /dev/null
+++ b/proposals/2312-matrix-uri.md
@@ -0,0 +1,769 @@
+# URI scheme for Matrix
+
+This is a proposal of a URI scheme to identify Matrix resources in a wide
+range of applications (web, desktop, or mobile) both throughout Matrix software
+and (especially) outside it. It supersedes
+[MSC455](https://github.com/matrix-org/matrix-doc/issues/455) in order
+to continue the discussion in the modern GFM style.
+
+While Matrix has its own resource naming system that allows it to identify
+resources without resolving them, there is a common need to provide URIs
+to Matrix resources (e.g., rooms, users, PDUs) that could be transferred
+outside of Matrix and then resolved in a uniform way - matching URLs
+in World Wide Web.
+
+Specific use cases include:
+1. Representation: as a Matrix user I want to refer to Matrix entities
+ in the same way as for web pages, so that others could unambiguously identify
+ the resource, regardless of the context or used medium to identify it to them
+ (within or outside Matrix, e.g., in a web page or an email message).
+1. Inbound integration: as an author of Matrix software, I want to have a way
+ to invoke my software from the operating environment to resolve a Matrix URI
+ passed from another program. This is a case of, e.g.,
+ opening a Matrix client by clicking on a link from an email message.
+1. Outbound integration: as an author of Matrix software, I want to have a way
+ to export identifiers of Matrix resources to non-Matrix environment
+ so that they could be resolved in another time-place in a uniform way.
+ An example of this case is the "Share via…" action in a mobile Matrix client.
+
+Matrix identifiers as defined by the current specification have a form distinct
+enough from other identifiers to mostly fulfil the representation use case.
+Since they are not URIs, they can not cover the two integration use cases.
+https://matrix.to somehow compensates for this; however:
+* it requires a web browser to run JavaScript code that resolves identifiers
+ (basically limiting first-class support to browser-based clients), and
+* it relies on matrix.to as an intermediary that provides that JavaScript code.
+
+To cover the use cases above, the following scheme is proposed for Matrix URIs
+(`[]` enclose optional parts, `{}` enclose variables):
+```text
+matrix:[//{authority}/]{type}/{id without sigil}[/{type}/{id without sigil}...][?{query}][#{fragment}]
+```
+with `{type}` defining the resource type (such as `r`, `u` or `roomid` - see
+the "Path" section in the proposal) and `{query}` containing additional hints
+or request details on the Matrix entity (see "Query" in the proposal).
+`{authority}` and `{fragment}` parts are reserved for future use; this proposal
+does not define them and implementations SHOULD ignore them for now.
+
+This MSC does not introduce new Matrix entities, nor API endpoints -
+it merely defines a mapping between URIs with the scheme name `matrix:`
+and Matrix identifiers, as well as operations on them. The MSC should be
+sufficient to produce an implementation that would convert Matrix URIs to
+a series of [CS API](https://matrix.org/docs/spec/client_server/latest) calls,
+entirely on the client side. It is recognised, however, that most of
+the URI processing logic can and should (eventually) be on the server side
+in order to facilitate adoption of Matrix URIs; further MSCs are needed
+to define details for that, as well as to extend the mapping to more resources
+(including those without equivalent Matrix identifiers, such as room state or
+user profile data).
+
+The Matrix identifier (or identifiers) can be reconstructed from
+`{id without sigil}` by prepending a sigil character corresponding to `{type}`.
+To support a hierarchy of Matrix resources, more `/{type}/{id without sigil}`
+pairs can be appended, identifying resources within other resources.
+As of now, there's only one such case, with exactly one additional pair -
+pointing to an event in a room.
+
+Examples:
+* Room `#someroom:example.org`:
+ `matrix:r/someroom:example.org`
+* User `@me:example.org`:
+ `matrix:u/me:example.org`
+* Event in a room:
+ `matrix:r/someroom:example.org/e/Arbitrary_Event_Id`
+* [A commit like this](https://github.com/her001/steamlug.org/commit/2bd69441e1cf21f626e699f0957193f45a1d560f)
+ could make use of a Matrix URI in the form of
+ `{Matrix identifier}`.
+
+
+## Proposal
+
+### Definitions
+
+Further text uses the following terms:
+- Matrix identifier - one of identifiers defined by the current
+[Matrix Specification](https://matrix.org/docs/spec/appendices.html#identifier-grammar),
+- Matrix URI - a uniform resource identifier proposed hereby, following
+ the RFC-compliant URI format.
+- MUST/SHOULD/MAY etc. follow the conventions of
+ [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
+
+
+### Requirements
+
+The following considerations drive the requirements for Matrix URIs:
+1. Follow existing standards and practices.
+1. Endorse the principle of the least surprise.
+1. Humans first, machines second.
+1. Cover as many entities as practical.
+1. URIs are expected to be extremely portable and stable;
+ you cannot rewrite them once they are released to the world.
+1. Ease of implementation, allowing reuse of existing codes.
+
+The following requirements resulted from these drivers:
+1. Matrix URI MUST comply with
+ [RFC 3986](https://tools.ietf.org/html/rfc3986) and
+ [RFC 7595](https://tools.ietf.org/html/rfc7595).
+1. By definition, Matrix URI MUST unambiguously identify a resource
+ in a Matrix network, across servers and types of resources.
+ This means, in particular, that two Matrix identifiers distinct by
+ [Matrix Specification](https://matrix.org/docs/spec/appendices.html#identifier-grammar)
+ MUST NOT have Matrix URIs that are equal in
+ [RFC 3986](https://tools.ietf.org/html/rfc3986) sense
+ (but two distinct Matrix URIs MAY map to the same Matrix identifier).
+1. References to the following entities MUST be supported:
+ 1. User IDs (`@user:example.org`)
+ 1. Room IDs (`!roomid:example.org`)
+ 1. Room aliases (`#roomalias:example.org`)
+ 1. Event IDs (`$arbitrary_eventid_with_or_without_serverpart`)
+1. The mapping MUST take into account that some identifiers
+ (e.g. aliases) can have non-ASCII characters - reusing
+ [RFC 3987](https://tools.ietf.org/html/rfc3987) is RECOMMENDED,
+ but an alternative encoding can be used if there are reasons for that.
+1. The mapping between Matrix identifiers and Matrix URIs MUST
+ be extensible (without invalidating previous URIs) to:
+ 1. new classes of identifiers (there MUST be a meta-rule to produce
+ a new mapping for IDs following the `&somethingnew:example.org`
+ pattern assumed for Matrix identifiers);
+ 1. new ways to navigate to and interact with objects in Matrix
+ (e.g., we might eventually want to have a mapping for
+ room-specific user profiles).
+1. The mapping MUST support decentralised as well as centralised IDs.
+ This basically means that the URI scheme MUST have provisions
+ for mapping of identifiers with `:` but it MUST NOT require
+ `:` to be there.
+1. Matrix URI SHOULD allow encoding of action requests such as joining a room.
+1. Matrix URI SHOULD have a human-readable, if not necessarily
+ human-friendly, representation - to allow visual sanity-checks.
+ In particular, characters escaping/encoding should be reduced
+ to bare minimum in that representation. As food for thought, see
+ [Wikipedia: Clean URL, aka SEF URL](https://en.wikipedia.org/wiki/Clean_URL) and
+ [a use case from RFC 3986](https://tools.ietf.org/html/rfc3986#section-1.2.1).
+1. It SHOULD be easy to parse Matrix URI in popular programming
+ languages: e.g., one should be able to use `parseUri()`
+ to dissect a Matrix URI into components in JavaScript.
+1. The mapping SHOULD be consistent across different classes of
+ Matrix identifiers.
+1. The mapping SHOULD support linking to unfederated servers/networks
+ (see also
+ [matrix-doc#2309](https://github.com/matrix-org/matrix-doc/issues/2309)
+ that calls for such linking).
+
+The syntax and mapping discussed below meet all these requirements except
+the last one that will be addressed separately.
+Further extensions MUST NOT reduce the supported set of requirements.
+
+
+### Syntax and high-level processing
+
+The proposed generic Matrix URI syntax is a subset of the generic
+URI syntax
+[defined by RFC 3986](https://tools.ietf.org/html/rfc3986#section-3):
+```text
+MatrixURI = "matrix:" hier-part [ "?" query ] [ "#" fragment ]
+hier-part = [ "//" authority "/" ] path
+```
+As mentioned above, this MSC assumes client-side URI processing
+(i.e. mapping to Matrix identifiers and CS API requests).
+However, even when URI processing is shifted to the server side
+the client will still have to parse the URI at least to remove
+the authority and fragment parts (if either exists)
+before sending the request to the server (more on that below).
+
+#### Scheme name
+The proposed scheme name is `matrix`.
+[RFC 7595](https://tools.ietf.org/html/rfc7595) states:
+
+ if there’s one-to-one correspondence between a service name and
+ a scheme name then the scheme name should be the same as
+ the service name.
+
+Other considered options were `mx` and `web+matrix`;
+[comments to MSC455](https://github.com/matrix-org/matrix-doc/issues/455)
+mention two scheme names proposed and one more has been mentioned
+in `#matrix-core:matrix.org`.
+
+The scheme name is a definitive indication of a Matrix URI and MUST NOT
+be omitted. As can be seen below, Matrix URI rely heavily on [relative
+references](https://tools.ietf.org/html/rfc3986#section-4.2) and
+omitting the scheme name makes them indistinguishable from a local path
+that might have nothing to do with Matrix. Clients MUST NOT try to
+parse pieces like `r/MyRoom:example.org` as Matrix URIs; instead,
+users should be encouraged to use Matrix identifiers for in-text references
+(`#MyRoom:example.org`) and client applications SHOULD turn them into
+hyperlinks to Matrix URIs.
+
+#### Authority
+
+Basing on
+[the definition in RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2),
+this MSC restricts the authority part to never have a userinfo component,
+partially to prevent confusion concerned with the `@` character that has its
+own meaning in Matrix, but also because this component has historically been
+a popular target of abuse.
+```text
+authority = host [ ":" port ]
+```
+Further definition of syntax or semantics for the authority part is left for
+future MSCs. Clients MUST parse the authority part as per RFC 3986 (i.e.
+the presence of an authority part MUST NOT break URI parsing) but SHOULD NOT
+use data from the authority part other than for experiments or research.
+
+The authority part may eventually be used to indicate access to a Matrix
+resource (such as a room or a user) specifically through a given entity.
+See "Ideas for further evolution".
+
+#### Path
+This MSC restricts
+[the very wide definition of path in RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.3),
+to a simple pattern that allows to easily reconstruct a Matrix identifier or
+a chain of identifiers and also to locate a certain sub-resource in the scope
+of a given Matrix entity:
+```text
+path = entity-descriptor ["/" entity-descriptor]
+entity-descriptor = nonid-segment / type-qualifier id-without-sigil
+nonid-segment = segment-nz ; as defined in RFC 3986, see also below
+type-qualifier = segment-nz "/" ; as defined in RFC 3986, see also below
+id-without-sigil = string ; as defined in Matrix identifier spec, see below
+```
+The path component consists of 1 or more descriptors separated by a slash
+(`/`) character. This is a generic pattern intended for reusing in future
+extensions.
+
+This MSC only proposes mappings along `type-qualifier id-without-sigil` syntax;
+`nonid-segment` is unused and reserved for future use.
+For the sake of integrity future `nonid-segment` extensions must follow
+[the ABNF for `segment-nz` as defined in RFC 3986](https://tools.ietf.org/html/rfc3986#appendix-A).
+
+This MSC defines the following `type` specifiers: `u` (user id, sigil `@`),
+`r` (room alias, sigil `#`), `roomid` (room id, sigil `!`), and
+`e` (event id, sigil `$`). This MSC does not define a type specifier for sigil `+`
+([groups](https://github.com/matrix-org/matrix-doc/issues/1513) aka communities
+or, in the more recent incarnation,
+[spaces](https://github.com/matrix-org/matrix-doc/pull/1772)); a separate MSC
+can introduce the specifier, along with the parsing/construction logic and
+relevant CS API invocations, following the framework of this proposal.
+
+The following type specifiers proposed in earlier editions of this MSC and
+already in use in several implementations, are deprecated: `user`, `room`, and
+`event`. Client applications MAY parse these specifiers as if they were
+`u`, `r`, and `e` respectively; they MUST NOT emit URIs with the deprecated
+specifiers. The rationale behind the switch is laid out in "Alternatives".
+
+As of this MSC, `u`, `r`, and `roomid` can only be at the top
+level. The type `e` (event) can only be used on the 2nd level and only under
+`r` or `roomid`; this is driven by the current shape of Client-Server API
+that does not provide a non-deprecated way to retrieve an event without knowing
+the room (see [MSC2695](https://github.com/matrix-org/matrix-doc/pull/2695) and
+[MSC2779](https://github.com/matrix-org/matrix-doc/issues/2779) that may
+change this).
+
+Further MSCs may introduce navigation to more top-level as well as
+non-top-level objects; see "Ideas for further evolution" to get inspired. These
+new proposals SHOULD follow the generic grammar laid out above, adding new
+`type` and `nonid-segment` specifiers and/or allowing them in other levels,
+rather than introduce a new grammar. It is recommended to only use abbreviated
+single-letter specifiers if they are expected to be user visible and convenient
+for type-in; if a URI for a given resource type is usually generated
+(e.g. because the corresponding identifier is not human-friendly), it's
+RECOMMENDED to use full (though short) words to avoid ambiguity and confusion.
+
+`id-without-sigil` is defined as the `string` part of Matrix
+[Common identifier format](https://matrix.org/docs/spec/appendices#common-identifier-format)
+with percent-encoded characters that are NEITHER unreserved, sub-delimiters, `:` nor `@`,
+[as per RFC 3986 rule for pchar](https://tools.ietf.org/html/rfc3986#appendix-A).
+This notably exempts `:` from percent-encoding but includes `/`.
+
+See the rationale behind dropping sigils and the respective up/downsides in
+"Discussion points and tradeoffs" as well as "Alternatives" below.
+
+#### Query
+
+Matrix URI can optionally have
+[the query part](https://tools.ietf.org/html/rfc3986#section-3.4).
+This MSC defines the general form for the query and two "standard" query items;
+further MSCs may add to this as long as RFC 3986 is followed.
+```text
+query = query-element *( "&" query-item )
+query-item = action / routing / custom-query-item
+action = "action=" ( "join" / "chat" )
+routing = "via=” authority
+custom-query-item = custom-item-name "=" custom-item-value
+custom-item-name = 1*unreserved ; reverse-DNS name; see below
+custom-item-value = ; see below
+```
+
+The `action` query item is used in contexts where, on top of identifying
+the Matrix entity, a certain action is requested on it. This proposal
+describes two possible actions:
+* `action=join` is only valid in a URI resolving to a Matrix room;
+ applications MUST ignore it if found in other contexts and MUST NOT generate
+ it for other Matrix resources. This action means that a client application
+ SHOULD attempt to join the room specified by the URI path using the standard
+ CS API means.
+* `action=chat` is only valid in a URI resolving to a Matrix user;
+ applications MUST ignore it if found in other contexts and MUST NOT generate
+ it for other Matrix resources. This action means that a client application
+ SHOULD open a direct chat window with the user specified by the URI path;
+ clients supporting
+ [canonical direct chats](https://github.com/matrix-org/matrix-doc/pull/2199)
+ SHOULD open the canonical direct chat.
+
+For both actions, where applicable, client applications SHOULD ask for user
+confirmation or at least notify the user before joining or creating a new room.
+Conversely, no additional confirmation/notification is necessary when
+the action leads to opening a room the user is already a member of.
+
+It is worth reiterating on the (blurry) distinction between URIs with `action`
+and those without:
+- a URI with no `action` simply _identifies_ the resource; if the context
+ implies an operation, it is usually focused on the retrieval of the resource,
+ in line with RFC 3986 (see also the next paragraph);
+- a URI with `action` in the query means that a client application should (but
+ is not obliged to) perform that action, with precautions as described above.
+
+In some cases a client application may have no meaningful way to immediately
+perform the default operation suggested by this MSC (see below); e.g.,
+the client may be unable to display a room before joining it, while the URI
+doesn't have `action=join`. In these cases client applications are free to do
+what's best for user experience (e.g., suggest joining the room), even if that
+means performing an action on a URI with no `action` in the query.
+
+The routing query (`via=`) indicates servers that are likely involved in
+the room (see also
+[the feature of matrix.to](https://matrix.org/docs/spec/appendices#routing)).
+In the meantime, it is proposed that this routing query be used not only with
+room ids in a public federation but also when a URI refers to a resource in
+a non-public Matrix network (see the question about closed federations in
+"Discussion points and tradeoffs"). Note that `authority` in the definition
+above is only a part of the _query parameter_ grammar; it is not proposed here
+to generate or interpret the _authority part_ of the URI.
+
+Clients MAY introduce and recognise custom query items, according to
+the following rules:
+- the name of a custom item MUST follow the reverse-DNS (aka "Java package")
+ naming convention, as per
+ [MSC2758](https://github.com/matrix-org/matrix-doc/pull/2758) - e.g.,
+ a custom action item for Element clients would be named `io.element.action`,
+ for Quaternion - `com.github.quaternion.action`, etc.
+- the value of the item can be any content but its representation in the URI
+ MUST follow the general RFC requirements for the query part; on top of that,
+ if the raw value contains `&` it MUST be percent-encoded.
+- clients SHOULD respect standard query items over their own ones; e.g.,
+ if a URI contains both `action` and the custom client action, the standard
+ action should be respected as much as possible. Client authors SHOULD strive
+ for consistent experience across their and 3rd party clients, anticipating
+ that the same user may happen to have both their client and a 3rd party one.
+
+Client authors are strongly encouraged to standardise custom query elements
+that gain adoption by submitting an MSC defining them in a way compatible
+across the client ecosystem.
+
+
+### Recommended implementation
+
+#### URI parsing algorithm
+
+The reference algorithm of parsing a Matrix URI follows. Note that, although
+clients are encouraged to use lower-case strings in their URIs, all string
+comparisons are case-INsensitive.
+
+1. Parse the URI into main components (`scheme name`, `authority`, `path`,
+ `query`, and `fragment`), decoding special or international characters
+ as directed by [RFC 3986](https://tools.ietf.org/html/rfc3986) and
+ (for IRIs) [RFC 3987](https://tools.ietf.org/html/rfc3987). Authors are
+ strongly RECOMMENDED that they find an existing implementation of that step
+ for their language and SDK, rather than implement it from scratch based
+ on RFCs.
+
+1. Check that `scheme name` is exactly `matrix`, case-insensitive. If
+ the scheme name doesn't match, exit parsing: this is not a Matrix URI.
+
+1. Split the `path` into segments separated by `/` character; several
+ subsequent `/` characters delimit empty segments, as advised by RFC 3986.
+
+1. Check that the URI contains either 2 or 4 segments; if it's not the case,
+ fail parsing; the Matrix URI is invalid.
+
+1. To construct the top-level (primary) Matrix identifier:
+
+ a. Pick the leftmost segment of `path` until `/` (path segment) and match
+ it against the following list to produce `sigil-1`:
+ - `u` (or, optionally, `user` - see "Path") -> `@`
+ - `r` (or, optionally, `room`) -> `#`
+ - `roomid` -> `!`
+ - any other string, including an empty one -> fail parsing:
+ the Matrix URI is invalid.
+
+ b. Pick the next (2nd) leftmost path segment:
+ - if the segment is empty, fail parsing;
+ - otherwise, percent-decode the segment (unless the initial URI parse
+ has already done that) and make `mxid-1` by prepending `sigil-1`.
+
+1. If `sigil-1` is `!` or `#` and the URI path has exactly 4 segments,
+ it may be possible to construct the 2nd-level Matrix identifier to
+ point to an event inside the room identified by `mxid-1`:
+
+ a. Pick the next (3rd) path segment:
+ - if the segment is exactly `e` (or, optionally, `event`), proceed;
+ - otherwise, including the case of an empty segment (trailing `/`, e.g.),
+ fail parsing.
+
+ b. Pick the next (4th) leftmost path segment:
+ - if the segment is empty, fail parsing;
+ - otherwise, percent-decode the segment (unless the initial URI parse
+ has already done that) and make `mxid-2` by prepending `$`.
+
+1. Split the `query` into items separated by `&` character; several subsequent
+ `&` characters delimit empty items, ignored by this algorithm.
+
+ a. If `query` contains one or more items starting with `via=`: for each item, treat
+ the rest of the item as a percent-encoded homeserver name to be used in
+ [routing](https://matrix.org/docs/spec/appendices#routing).
+
+ b. If `query` contains one or more items starting with `action=`: treat
+ _the last_ such item as an instruction, as this proposal defines in [query](#query).
+
+Clients MUST implement proper percent-decoding of the identifiers; there's no
+liberty similar to that of matrix.to.
+
+#### Operations on Matrix URIs
+
+The main purpose of a Matrix URI is accessing the resource specified by the
+identifier. This MSC defines the "default" operation
+([in the sense of RFC 7595](https://tools.ietf.org/html/rfc7595#section-3.4))
+that a client application SHOULD perform when the user activates
+(e.g. clicks on) a URI; further MSCs may introduce additional operations
+enabled either by passing an `action` value in the query part, or by other
+means.
+
+The classes of URIs and corresponding default operations (along with relevant
+CS API calls) are collected below. The table assumes that the operations are
+performed on behalf (using the access token) of the user `@me:example.org`:
+
+| URI class/example | Interactive operation | Non-interactive operation / Involved CS API |
+| ----------------- | --------------------- | --------------------------------------------- |
+| User Id (no `action` in URI): `matrix:u/her:example.org` | _Outside the room context_: show user profile _Inside the room context:_ mention the user in the current room (client-local operation) | No default non-interactive operation `GET /profile/@her:example.org/display_name` `GET /profile/@her:example.org/avatar_url` |
+| User Id (`action=chat`): `matrix:u/her:example.org?action=chat` | 1. Confirm with the local user if needed (see "Query") 2. Open the room as defined in the next column | If [canonical direct chats](https://github.com/matrix-org/matrix-doc/pull/2199) are supported: `GET /_matrix/client/r0/user/@me:example.org/dm?involves=@her:example.org` Without canonical direct chats: 1. `GET /user/@me:example.org/account_data/m.direct` 2. Find the room id for `@her:example.org` in the event content 3. if found, return this room id; if not, `POST /createRoom` with `"is_direct": true` and return id of the created room |
+| Room (no `action` in URI): `matrix:roomid/rid:example.org` `matrix:r/us:example.org` | Attempt to "open" (usually: display the timeline at the latest or last remembered position) the room | No default non-interactive operation API: Find the respective room in the local `/sync` cache or `GET /rooms/!rid:example.org/...` |
+| Room (`action=join`): `matrix:roomid/rid:example.org?action=join&via=example2.org` `matrix:r/us:example.org?action=join` | 1. Confirm with the local user if needed (see "Query") 2. Attempt to join the room | `POST /join/!rid:example.org?server_name=example2.org` `POST /join/#us:example.org` |
+| Event: `matrix:r/us:example.org/e/lol823y4bcp3qo4` `matrix:roomid/rid:example.org/event/lol823y4bcp3qo4?via=example2.org` | 1. For room aliases, resolve an alias to a room id (see the next column) 2. Attempt to retrieve (see the next column) and display the event; 3. If the event could not be retrieved due to access denial and the current user is not a member of the room, the client MAY offer the user to join the room and try to open the event again | Non-interactive operation: return event or event content, depending on context API: find the event in the local `/sync` cache or `GET /directory/room/%23us:example.org` (to resolve alias to id) `GET /rooms/!rid:example.org/event/lol823y4bcp3qo4?server_name=example2.org` |
+
+
+#### URI construction algorithm
+
+The following algorithm assumes a Matrix identifier that follows
+the high-level grammar described in the specification. Clients MUST ensure
+compliance of identifiers passed to this algorithm.
+
+For room and user identifiers (including room aliases):
+1. Remove the sigil character from the identifier and match it against
+ the following list to produce `prefix-1`:
+ - `@` -> `u/`
+ - `#` -> `r/`
+ - `!` -> `roomid/`
+2. Build the Matrix URI as a concatenation of:
+ - literal `matrix:`;
+ - `prefix-1`;
+ - the remainder of identifier (`id without sigil`), percent-encoded as per
+ [RFC 3986](https://tools.ietf.org/html/rfc3986).
+
+For event identifiers (assuming they need the room context, see
+[MSC2695](https://github.com/matrix-org/matrix-doc/pull/2695) and
+[MSC2779](https://github.com/matrix-org/matrix-doc/issues/2779) that
+may change this):
+1. Take the event's room id or canonical alias and build a Matrix URI for them
+ as described above.
+2. Append to the result of previous step:
+ - literal `e/`;
+ - the event id after removing the sigil (`$`) and percent-encoding.
+
+Clients MUST implement proper percent-encoding of the identifiers; there's no
+liberty similar to that of matrix.to.
+
+
+## Discussion and non-normative statements
+
+### Ideas for further evolution
+
+This MSC is obviously just the first step, keeping the door open for
+extensions. Here are a few ideas:
+
+* Add new actions; e.g. leaving a room (`action=leave`).
+
+* Add specifying a segment of the room timeline (`from=$evtid1&to=$evtid2`).
+
+* Unlock bare event ids (`matrix:e/$event_id`) - subject to change in
+ other areas of the specification.
+
+* Bring tangible semantics to the authority part. The main purpose of
+ the authority part,
+ [as per RFC 3986](https://tools.ietf.org/html/rfc3986#section-3.2),
+ is to identify the entity governing the namespace for the rest of the URI.
+ The current MSC rules out the userinfo component but leaves it to a separate
+ MSC to define semantics of the remaining`host[:port]` piece.
+
+ Importantly, future MSCs are advised against using the authority part for
+ _routing over federation_ (the case for `via=` query items), as it would be
+ against the spirit of RFC 3986. The authority part can be used in cases when
+ a given Matrix entity is only available from certain servers (the case of
+ closed federations or non-federating servers).
+
+ While being a part of the original proposal in an attempt to address
+ [the respective case](https://github.com/matrix-org/matrix-doc/issues/2309),
+ the definition of the authority semantics has been dropped as a result of
+ [the subsequent discussion](https://github.com/matrix-org/matrix-doc/pull/2312#discussion_r348960282).
+ A further MSC may approach the same case (and/or others) and define the
+ meaning of the authority part (either on the client- or even on
+ the server-side - provided that using Matrix URIs on the server-side brings
+ some other value along the way). This might not necessarily be actual DNS
+ hostnames even - one (quite far-fetched for now) idea to entertain might be
+ introducing some decentralised system of "network names" in order to equalise
+ "public" and "non-public" federations.
+
+ Along the same lines, if providing any part of user credentials via
+ the authority part is found to be of considerable value in some case,
+ a separate MSC could both reinstate it in the grammar and define how
+ to construct, parse, and use it - provided that the same MSC addresses
+ the security concerns associated with such URIs.
+
+* One could conceive a URI mapping of avatars in the form of
+ `matrix:u/uid:matrix.org/avatar/room:matrix.org`
+ (a user’s avatar for a given room).
+
+* As described in "Alternatives", a synonymous system can be introduced that
+ uses Matrix identifiers with sigils by adding another path prefix (e.g.,
+ `matrix:id/%23matrix:matrix.org`). However, such MSC would have to address
+ the concerns of possible confusion arising from having two similar but
+ distinct notations.
+
+* Interoperability of Matrix URIs with
+ [Linked Data](https://en.wikipedia.org/wiki/Linked_data).
+
+
+### Past discussion points and tradeoffs
+
+The below documents the discussion and outcomes in various prior forums;
+further discussion should happen in GitHub comments.
+1. _Why no double-slashes in a typical URI?_
+ Because `//` is used to mark the beginning of an authority
+ part. RFC 3986 explicitly forbids to start the path component with
+ `//` if the URI doesn't have an authority component. In other words,
+ `//` implies a centre of authority, and the (public) Matrix
+ federation is not supposed to have one; hence no `//` in most URIs.
+1. ~~_Why do type specifiers use singular rather than plural
+ as is common in RESTful APIs?_~~
+ This is no more relevant with single-letter type specifiers. The answer
+ below is provided for history only.
+ Unlike in actual RESTful APIs, this MSC does not see `rooms/` or
+ `users/` as collections to browse. The type specifier completes
+ the id specification in the URI, defining a very specific and
+ easy to parse syntax for that. Future MSCs may certainly add
+ collection URIs, but it is recommended to use more distinct naming
+ for such collections. In particular, `rooms/` is ambiguous, as
+ different sets of rooms are available to any user at any time
+ (e.g., all rooms known to the user; or all routable rooms; or
+ public rooms known to the user's homeserver).
+1. _Should we advise using the query part for collections then?_
+ Not in this MSC but that can be considered in the future.
+1. _Why can't event URIs use the fragment part for the event ID?_
+ Because fragment is a part processed exclusively by the client
+ in order to navigate within a larger document, and room cannot
+ be considered a "document". Each event can be retrieved from the server
+ individually, so each event can be viewed as a self-contained document.
+ When/if URI processing is shifted to the server-side, servers are not even
+ going to receive fragments (as per RFC 3986), which is why usage of
+ fragments to remove the need for percent-encoding in other identifiers
+ would lead to URIs that cannot be resolved on servers. Effectively, all
+ clients would have to implement full URI processing with no chance
+ to offload that to the server. For that reason fragments, if/when ever
+ employed in Matrix, only should be used to pinpoint a position within events
+ and for similar strictly client-side operations.
+1. _How does this MSC work with closed federations?_ ~~If you need to
+ communicate a URI to the bigger world where you cannot expect
+ the consumer to know in advance which federation they should use -
+ supply any server of the closed federation in the authority part.
+ Users inside the closed federation can omit the authority part if
+ they know the URI is not going to be used outside this federation.
+ Clients can facilitate that by having an option to always add or omit
+ the authority part in generated URIs for a given user account.~~
+ As of now, use `via=` in order to point to a homeserver in the closed
+ federation. The authority part may eventually be used for that (or for some
+ other case - see the previous section).
+
+
+### Alternatives
+
+#### Using full words for all types
+
+During its draft state, this MSC was proposing type specifiers using full words
+(`user`, `room`, `event` etc.), arguing that abbreviations can be introduced
+separately as synonyms. Full words have several shortcomings pointed out in
+discussions across the whole period of preparation, namely:
+- The singular vs. plural choice (see also "Past discussion points")
+- Using English words raises a question about eventual support of localised
+ URI variants (`matrix:benutzer/...`, `matrix:usuario/...` etc.) catering to
+ international audience, that would add complication to the Matrix technology.
+- Abbreviated forms are popularised by Reddit and make URIs shorter which is
+ crucial for the outbound integration case (see the introduction).
+
+Meanwhile, using `u`/`r`/`e` for users, rooms and events has the following
+advantages:
+1. there's a strong Reddit legacy, with users across the world quite familiar
+ with the abbreviated forms (and `r/` coincidentally standing for sub-Reddits
+ links to which have basically the same place in the Reddit ecosystem as
+ Matrix room aliases have in the Matrix ecosystem);
+2. matrix.to links to users and room aliases are heavily used throughout Matrix,
+ specifically in end-user-facing contexts (see also use cases in the
+ introductory section of this MSC);
+3. the singular vs. plural (`room` or `rooms`?) confusion is avoided;
+4. it's shorter, which is crucial for typing the URI in an external medium.
+
+The rationale behind not abbreviating `roomid/` is a better distinction between
+room aliases and room ids; also, since room ids are almost never typed in
+manually, the advantages (3) and (4) above don't hold.
+
+For these reasons, it was decided in the end to use the single-letter style
+for types most used in the outbound integration case. It's still possible to
+reinstate full words as synonyms some time down the road, with the caveat that
+a canonicalisation service from homeservers may be needed to avoid having
+to enable synonyms at each client individually.
+
+#### URNs
+
+The discussion in
+[MSC455](https://github.com/matrix-org/matrix-doc/issues/455)
+mentions an option to standardise URNs rather than URLs/URIs,
+with the list of resolvers being user-specific. While a URN namespace
+such as `urn:matrix:`, along with a URN scheme, might be deemed useful
+once we shift to (even) more decentralised structure of the network,
+`urn:` URIs must be managed entities (see
+[RFC 8141](https://tools.ietf.org/html/rfc8141)) which is not always
+the case in Matrix (consider room aliases, e.g.).
+
+With that said, a URN-styled (`matrix:room:example.org:roomalias`)
+option was considered. However, Matrix already uses colon (`:`) as
+a delimiter of id parts and, as can be seen above, reversing the parts
+to meet the URN's hierarchical order would look confusing for Matrix
+users (as in example above - is `room` a part of the identifier or
+the type signifier?).
+
+#### "Full REST"
+
+Yet another alternative considered was to go "full REST" and structure
+URLs in a more traditional way with serverparts coming first, followed
+by type grouping (sic - not specifiers), and then by localparts,
+i.e. `matrix://example.org/rooms/roomalias`. This is even more difficult
+to comprehend for a Matrix user than the previous alternative and besides it
+conflates the notion of an authority server with that of a namespace
+discriminator: clients would not connect to `example.org` to resolve the alias
+above, they would still connect to their own homeserver.
+
+#### Minimal syntax
+
+One early proposal was to simply prepend `matrix:` to a Matrix identifier
+(without encoding it), assuming that it will only be processed on the client
+side. The massive downside of this option is that such strings are not actual
+URIs even though they look like ones: most URI parsers won't handle them
+correctly. As laid out in the beginning of this proposal, Matrix URIs are
+not striving to preempt Matrix identifiers; instead of trying to produce
+an equally readable string, one should just use identifiers where they work.
+Why Matrix identifiers look the way they look is way out of the MSC scope
+to discuss here.
+
+#### Minimal syntax based on the path component and percent-encoding
+
+A simple modification of the previous option is much more viable:
+proper percent-encoding of the Matrix identifier allows to use it as
+a URI path part. A single identifier packed in a URI could look like
+`matrix:/encoded_id_with_sigil`; an event-in-a-room URI would be something
+like `matrix:/roomid_or_alias/$event_id` (NB: RFC 3986 doesn't require `$`
+to be encoded). This is considerably more concise and encoding is only
+needed for `#`.
+
+Quite unfortunately, `#` is one of the two sigils in Matrix most relevant
+to integration cases. The other one is `@`; it doesn't need encoding except
+in the authority part - which is why the form above uses a leading `/` that
+puts the identifier in the path part instead of what parsers treat as
+the authority part. `#` has to be encoded wherever it appears, making a URI
+for Matrix HQ, the first chat room many new users join, look like
+`matrix:/%23matrix:matrix.org`. Beyond first-time usage, this generally impacts
+[the "napkin" case](https://tools.ietf.org/html/rfc3986#section-1.2.1) from
+RFC 3986 that the Requirements section of this MSC mentions. Until we have
+applications generally recognising Matrix identifiers in the same way e-mail
+addresses are recognised without prefixing `mailto:`, we should live with
+the fact that people will have to produce Matrix URIs by hand in various
+instances, from pen-and-paper to other instant messengers.
+
+Putting the whole id to the URI fragment (`matrix:#id_with_sigil` or,
+following on the `matrix.to` tradition, `matrix:#/id_with_sigil` for
+readability) allows using `#` without encoding on many URI parsers. It is
+still not fully RFC-compliant and rules out using URIs by homeservers
+(see also "Past discussion points" on using fragments to address events).
+
+Regardless of the placement (the fragment or the path), one more consideration
+is that the character space for sigils is extremely limited and
+Matrix identifiers are generally less expressive than full-blown URI paths.
+Not that Matrix showed a tendency to produce many classes of objects that would
+warrant a dedicated sigil but that cannot be ruled out. Rather than rely
+on the institute of sigils, this proposal gives an alternative more
+extensible syntax that can be used for more advanced cases - as a uniform way
+to represent arbitrary sub-objects (with or without Matrix identifier) such as
+user profiles, or a notifications feed for the room - and also, if ever needed,
+as an escape hatch to a bigger namespace if we hit shortage of sigils.
+
+The current proposal is also flexible enough to incorporate the minimal
+syntax of this option as an alternative to its own notation - e.g., a further
+MSC could enable `matrix:id/%23matrix:matrix.org` as a synonym for
+`matrix:room/matrix:matrix.org`.
+
+
+## Potential issues
+
+Despite the limited functionality of URIs as proposed in this MSC,
+Matrix authors are advised to use tools that would process URIs just
+like an HTTP(S) URI instead of making home-baked parsers/emitters.
+Even with that in mind, not all tools normalise and sanitise all cases
+in a fully RFC-compliant way. This MSC tries to keep the required
+transformations to the minimum and will likely not bring much grief even
+with naive implementations; however, as functionality of Matrix URI grows,
+the number of corner cases will increase.
+
+
+## Security/privacy considerations
+
+This MSC mostly builds on RFC 3986 but tries to reduce the scope
+as much as possible. Notably, it avoids introducing complex traversable
+structures and further restricts the URI grammar to the necessary subset.
+In particular, dot path segments (`.` and `..`), while potentially useful
+when URIs become richer, would come too much ahead of time for now. Care
+is taken to not make essential parts of the URI omittable to avoid
+even accidental misrepresentation of a local resource for a remote one
+in Matrix and vice versa.
+
+As mentioned in the authority part section, the MSC intentionally doesn't
+support conveying any kind of user information in URIs.
+
+The MSC strives to not be prescriptive in treating URIs except the `action`
+query parameter. Actions without user confirmation may lead to unintended
+leaks of certain metadata and/or changes in the account state with respect
+to Matrix. To reiterate, clients SHOULD ask for a user consent if/when they
+can unless applying the action doesn't lead to sending persistent (message
+or state) events on user's behalf.
+
+
+## Conclusion
+
+A dedicated URI scheme is well overdue for Matrix. Many other networks
+already have got one for themselves, benefiting both in terms of
+branding (compare `matrix:r/weruletheworld:example.org` vs.
+`#weruletheworld:example.org` from the standpoint of someone who
+hasn't been to Matrix) and interoperability (`matrix.to` requires
+opening a browser while clicking a `tg:` link dumped to the terminal
+application will open the correct application for Telegram without
+user intervention or can even offer to install one, if needed).
+The proposed syntax makes conversion between Matrix URIs
+and Matrix identifiers as easy as a bunch of string comparisons or
+regular expressions; so even though client-side processing of URIs
+might not be optimal longer-term, it's a very simple and quick way
+that allows plenty of experimentation early on.
diff --git a/proposals/2320-identity-versions.md b/proposals/2320-identity-versions.md
new file mode 100644
index 00000000000..ded50b3bc88
--- /dev/null
+++ b/proposals/2320-identity-versions.md
@@ -0,0 +1,35 @@
+# Versions information for identity servers
+
+The client-server API currently specifies a `/versions` endpoint that allows
+clients to know what version of that API are implemented by the server.
+Identity servers could benefit from that endpoint as both homeservers and
+clients interact with them, and therefore could know which features they can
+expect a given identity server to implement by looking at the versions of the
+API it claims to support.
+
+## Proposal
+
+This proposal adds the following endpoint to the identity server API.
+
+### `GET /_matrix/identity/versions`
+
+This endpoint serves information about the versions of the identity server API
+this identity server supports. Its response uses the following format:
+
+```json
+{
+ "versions": [
+ "r0.1.0",
+ "r0.2.0",
+ "r0.2.1",
+ ]
+}
+```
+
+## Alternative solutions
+
+Another solution which was considered was using the status check endpoint ([`GET
+/_matrix/api/v1`](https://matrix.org/docs/spec/identity_service/latest#get-matrix-identity-api-v1))
+to serve this information. This solution was discarded because it's using a
+versioned endpoint, which doesn't make sense to advertise the supported versions
+of the API to use.
diff --git a/proposals/2366-key-verification-accept.md b/proposals/2366-key-verification-accept.md
new file mode 100644
index 00000000000..bc355601ff9
--- /dev/null
+++ b/proposals/2366-key-verification-accept.md
@@ -0,0 +1,80 @@
+# Key verification flow additions: `m.key.verification.ready` and `m.key.verification.done`
+
+The current key verification framework is asymmetrical in that the user who
+requests the verification is unable to select the key verification method.
+This makes it harder for more experienced users who wish to guide less
+experienced users through the verification process, especially if they are not
+verifying in-person, but are using a trusted but remote channel of verification
+(such as telephone or video conference).
+
+As an example, let us say that Alice is an experienced Matrix user and is
+introducing Bob to the wonders of federated communications. Alice wants to
+verify keys with Bob, so she clicks on the "Verify" button in her client on
+Bob's profile (which sends a `m.key.verification.request` message to Bob).
+Bob's device receives the verification request and prompts Bob to accept the
+verification request. At this point, under the current framework, Bob is
+responsible for choosing the verification method to use. However, with this
+proposal, Bob would be able to just accept the verification request without
+choosing a method, and allow Alice to choose the verification method.
+
+In addition, the current key verification framework does not have a method for
+clients to signal to the other side that a key verification was successful.
+Some clients may wish to wait until the other side has either confirmed a
+successful verification or indicated an error before displaying the result of
+the verification, in order to give the two users a consistent view of the
+verification as a whole.
+
+## Proposal
+
+Two new event types are added to the [key verification
+framework](https://matrix.org/docs/spec/client_server/r0.6.1#key-verification-framework)
+when verifying in to-device messages. The new event
+types are already described in [MSC2241 (Key verification in
+DMs)](https://github.com/matrix-org/matrix-doc/pull/2241). This proposal adds
+them to verifications in to-device messages.
+
+The first event type is `m.key.verification.ready`, which must be sent by the
+target of the `m.key.verification.request` message, upon receipt of the
+`m.key.verification.request` event. It has the fields:
+
+- `from_device`: the ID of the device that sent the `m.key.verification.ready`
+ message
+- `methods`: an array of verification methods that the device supports
+
+It also has the usual `transaction_id` or `m.relates_to` fields for key
+verification events, depending on whether it is sent as a to-device event
+or an in-room event.
+
+After the `m.key.verification.ready` event is sent, either party can send an
+`m.key.verification.start` event to begin the verification. If both parties
+send an `m.key.verification.start` event, and they both specify the same
+verification method, then the event sent by the user whose user ID is the
+lexicographically smallest is used, and the other `m.key.verification.start` event is ignored.
+In the case of a single user verifying two of their devices, the device ID is
+compared instead. If both parties send an `m.key.verification.start` event,
+but they specify different verification methods, the verification should be
+cancelled with a `code` of `m.unexpected_message`.
+
+With to-device messages, previously the sender of the
+`m.key.verification.request` message would send an `m.key.verification.cancel`
+message to the recipient's other devices when it received an
+`m.key.verification.start` event. With this new event, the sender of the
+`m.key.verification.request` message should send an `m.key.verification.cancel`
+message when it receives an `m.key.verification.ready` or
+`m.key.verification.start` message, whichever comes first.
+
+The `m.key.verification.ready` event is required for verifications in both DMs
+and in to-device messages to accept verifications requested using an
+`m.key.verification.request` event.
+
+The second event type is `m.key.verification.done`, which has no fields other
+than the usual `transaction_id` or `m.relates_to` field. This indicates that
+the device has successfully completed its side of the verification.
+
+## Potential issues
+
+Clients that follow the Client-Server 0.6.0 spec may not expect an
+`m.key.verification.ready` message in response to `m.key.verification.request`.
+However to our knowledge, no clients implement `m.key.verification.request` in
+this way yet -- to our knowledge, all clients that implement verification
+implement this proposal.
diff --git a/proposals/2403-knock.md b/proposals/2403-knock.md
new file mode 100644
index 00000000000..c0f39254e41
--- /dev/null
+++ b/proposals/2403-knock.md
@@ -0,0 +1,683 @@
+# MSC2403: Add "knock" feature
+Many people are in invite-only rooms. Sometimes, someone wants to join such a
+room and can't, as they aren't invited. This proposal adds a feature for a
+user to indicate that they want to join a room.
+
+# Proposal
+This proposal implements the reserved "knock" membership type for the
+`m.room.member` state event. This state event indicates that when a user
+knocks on a room, they are asking for permission to join. Like all membership
+events, it contains an optional "reason" parameter to specify the reason you
+want to join. Like other membership types, the parameters "displayname" and
+"avatar_url" are optional. This membership can be sent by users who aren't
+currently in said room. An example for the membership would look like the
+following:
+```json
+{
+ "membership": "knock",
+ "displayname": "Alice",
+ "avatar_url": "mxc://example.org/avatar",
+ "reason": "I want to join this room as I really love foxes!"
+}
+```
+
+After a knock in a room, a member of the room can invite the knocker, or they
+can decide to reject it instead.
+
+## Client-Server API
+A new endpoint is introduced in the Client-Server API: `POST
+/_matrix/client/r0/knock/{roomIdOrAlias}`. This allows the client to state
+their intent to knock on a room.
+
+Additionally, extensions to the `GET /_matrix/client/r0/sync` endpoint are
+introduced. These allow a client to receive information about the status of
+their knock attempt.
+
+### `POST /_matrix/client/r0/knock/{roomIdOrAlias}`
+Or the knocking equivalent of
+[`POST
+/_matrix/client/r0/join/{roomIdOrAlias}`](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-join-roomidoralias).
+
+The path parameter (`roomIdOrAlias`) is either the room ID or the alias of
+the room you want to knock on. Additionally, several `server_name` parameters
+can be specified via the query parameters. The post body accepts an optional
+string parameter, `reason`, which is the reason you want to join the room. A
+request could look as follows:
+
+```json
+POST /_matrix/client/r0/knock/%23foxes%3Amatrix.org?server_name=matrix.org&server_name=elsewhere.ca HTTP/1.1
+Content-Type: application/json
+
+{
+ "reason": "I want to join this room as I really love foxes!"
+}
+```
+
+This endpoint requires authentication and can be rate limited.
+
+
+#### Responses:
+##### Status code 200:
+The user knocked successfully. The room ID of the knocked on room is returned. Example
+reply:
+```json
+{
+ "room_id": "!ZclcEpFTORTjmWIrqH:matrix.org"
+}
+```
+
+##### Status code 403:
+The user wasn't allowed to knock (e.g. they are banned). Example error reply:
+```json
+{
+ "errcode": "M_FORBIDDEN",
+ "error": "The user isn't allowed to knock in this room."
+}
+```
+
+##### Status code 404:
+The room was not found. Example error reply:
+```json
+{
+ "errcode": "M_NOT_FOUND",
+ "error": "Unknown room."
+}
+```
+
+### Extensions to `GET /_matrix/client/r0/sync`
+
+In [the response to
+`/sync`](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-sync)
+is a `rooms` field. This is a dictionary which currently contains keys
+`join`, `invite` and `leave`, which each provide information to the client on
+various membership states regarding the user.
+
+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`. `StrippedStateEvent`s are defined as state
+events that only contain the `sender`, `type`, `state_key` and `content`
+keys.
+
+Note that while `join` and `leave` keys in `/sync` use `state`, we use
+`knock_state` here. This mirrors `invite`s use of `invite_state`.
+
+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.
+
+This prevents unneeded state from the room leaking out, and also speeds
+things up (think not sending over hundreds of membership events from big
+rooms).
+
+Also note that like `invite_state`, state events from `knock_state` are
+purely for giving the user some information about the current state of the
+room that they have knocked on. If the user was previously in the room, the
+state events in `knock_state` are not intended to overwrite any historical
+state. This applies storage of state on both the homeserver and the client.
+
+The following is an example of knock state coming down `/sync`.
+
+Request:
+```
+GET /_matrix/client/r0/sync HTTP/1.1
+Content-Type: application/json
+```
+
+Response:
+```json
+{
+ ...
+ "rooms": {
+ "knock": {
+ "!abcdefghijklmo:example.com": {
+ "knock_state": {
+ "events": [
+ {
+ "content": {
+ "join_rule": "knock"
+ },
+ "sender": "@room_admin:example.com",
+ "state_key": "",
+ "type": "m.room.join_rules"
+ },
+ {
+ "content": {
+ "name": "Some cool room"
+ },
+ "sender": "@room_admin:example.com",
+ "state_key": "",
+ "type": "m.room.name"
+ },
+ {
+ "content": {
+ "url": "mxc://example.com/xyz54321"
+ },
+ "sender": "@room_admin:example.com",
+ "state_key": "",
+ "type": "m.room.avatar"
+ },
+ {
+ "content": {
+ "avatar_url": "mxc://example.org/abc1234",
+ "displayname": "Knocking User",
+ "membership": "knock"
+ },
+ "sender": "@knocking_user:example.org",
+ "state_key": "@knocking_user:example.org",
+ "type": "m.room.member",
+ }
+ ]
+ }
+ }
+ }
+ },
+ ...
+}
+```
+
+### Changes regarding the Public Rooms Directory
+
+A problem arises for discovery of knockable rooms. Ideally one wouldn't have
+to send their colleagues a room ID for a room that they need to knock on. One
+of these methods for room discovery is the [public rooms
+directory](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-publicrooms),
+which allows us to explore a list of rooms we may be able to join.
+
+The spec does not prevent us from adding rooms with 'knock' join_rules to the
+public rooms directory. However, a user attempting
+to join a room in the directory will not know whether to directly attempt a
+join, or to knock first. The current content of a `PublicRoomsChunk` does not
+contain this information:
+
+```json
+{
+ "aliases": [
+ "#murrays:cheese.bar"
+ ],
+ "avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
+ "guest_can_join": false,
+ "name": "CHEESE",
+ "num_joined_members": 37,
+ "room_id": "!ol19s:bleecker.street",
+ "topic": "Tasty tasty cheese",
+ "world_readable": true
+}
+```
+
+Therefore this proposal adds `join_rule` as a new, optional field to a
+`PublicRoomsChunk`. The `join_rule` of knockable rooms will be `knock`,
+thus giving clients the information they need to attempt entry of a
+room when a client selects it. It also allows clients to display
+knockable rooms differently than publicly joinable ones.
+
+For backwards compatibility with old servers, the default value of
+`join_rule` is `public`.
+
+### Push Rules
+
+To help knocks be noticed earlier, it would be nice to send a push
+notification to those in the room who can act on a knock when it
+comes in, rather than everyone in the room. This would require a
+push rule to fire only when that user's power level is high enough to
+accept or reject a knock.
+
+With the current push rules implementation it is possible to place a
+condition on the sender's power level, but unfortunately the same does
+not exist for event recipients.
+
+This MSC thus does not propose any changes to push rules at this time,
+but acknowledges that it would be useful for a future MSC to address when
+the underlying push rules architecture can support it.
+
+
+## Server-Server API
+Similarly to [join](https://matrix.org/docs/spec/server_server/r0.1.4#joining-rooms)
+and [leave](https://matrix.org/docs/spec/server_server/r0.1.4#leaving-rooms-rejecting-invites)
+over federation, a ping-pong game with two new endpoints is introduced: `make_knock`
+and `send_knock`. Both endpoints must be protected via server ACLs.
+
+### `GET /_matrix/federation/v1/make_knock/{roomId}/{userId}`
+
+Asks the receiving server to return information that the sending server will
+need to prepare a knock event.
+
+Request format:
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| Path parameters:
+| roomId | string | Required. The room ID that should receive the knock.
+| userId | string | Required. The user ID the knock event will be for.
+| Query Parameters:
+| ver | [string] | Required. The room versions the sending server has support for.
+
+Note that `GET /_matrix/federation/v1/make_join/{roomId}/{userId}` does not make `ver`
+a required query parameter for backwards compatibility reasons. We have no such restrictions.
+
+
+Response Format:
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| room_version | string | The version of the room where the server is trying to knock.
+| event | Event Template | An unsigned template event. May differ between room versions.
+
+#### Responses
+##### Status code 200:
+Returns a template to be used to knock on rooms. May depend on room version.
+```json
+{
+ "room_version": "2",
+ "event": {
+ "type": "m.room.member",
+ "room_id": "!somewhere:example.org",
+ "content": {
+ "membership": "knock"
+ },
+ "state_key": "@someone:example.org",
+ "origin": "example.org",
+ "origin_server_ts": 1549041175876,
+ "sender": "@someone:example.org"
+ }
+}
+```
+
+##### Status code 400:
+This request was invalid, e.g. bad JSON. Example reply:
+```json
+{
+ "errcode": "M_INCOMPATIBLE_ROOM_VERSION",
+ "error": "Your homeserver does not support the features required to join this room",
+ "room_version": "3"
+}
+```
+
+##### Status code 403:
+This request is forbidden, e.g. the user is banned from the room. Example reply:
+```json
+{
+ "errcode": "M_FORBIDDEN",
+ "error": "You are not allowed to knock on this room"
+}
+```
+
+##### Status code 404:
+The room is unknown to the remote server. Example reply:
+```json
+{
+ "errcode": "M_NOT_FOUND",
+ "error": "Unknown room"
+}
+```
+
+### `PUT /_matrix/federation/v1/send_knock/{roomId}/{eventId}`
+Submits a signed knock event to the resident homeserver for it to accept into
+the room's graph. Note that event format may differ between room versions.
+
+Note that in the past all `send_*` federation endpoints were updated to `/v2`
+to remove a redundant HTTP error code from the return body. While we don't
+have the same redundancy here, we start off at `/v1` for this new endpoint
+as per
+[MSC2844](https://github.com/matrix-org/matrix-doc/pull/2844).
+
+Request format:
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| Path parameters:
+| roomId | string | Required. The room ID that should receive the knock.
+| eventId | string | Required. The event ID for the knock event.
+
+The JSON body is expected to be the full event.
+
+Response Format:
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `knock_room_state` | [StrippedStateEvent] | Required. State events providing public room metadata
+
+A request could look as follows:
+```json
+PUT /_matrix/federation/v1/send_knock/%21abc123%3Amatrix.org/%24abc123%3Aexample.org HTTP/1.1
+Content-Type: application/json
+
+{
+ "sender": "@someone:example.org",
+ "origin": "matrix.org",
+ "origin_server_ts": 1234567890,
+ "type": "m.room.member",
+ "state_key": "@someone:example.org",
+ "content": {
+ "membership": "knock",
+ "displayname": "Alice",
+ "avatar_url": "mxc://example.org/avatar",
+ "reason": "I want to join this room as I really love foxes!"
+ }
+}
+```
+
+#### Response:
+##### Status code 200:
+The event was successfully accepted into the graph by the homeserver that
+received the knock. It must then send this knock into the room, before
+responding to the knocking homeserver, indicating the knock succeeded.
+
+The response contains `StrippedStateEvent`s with room metadata (room name,
+avatar ...) that the knocking homeserver can pass to the client. The event
+types that can be sent here should match those of the `/sync` extensions
+mentioned above.
+
+This is loosely based on the
+[federated invite](https://matrix.org/docs/spec/server_server/r0.1.4#put-matrix-federation-v2-invite-roomid-eventid)
+request content.
+```json
+{
+ "knock_room_state": [
+ {
+ "content": {
+ "join_rule": "knock"
+ },
+ "sender": "@room_admin:example.com",
+ "state_key": "",
+ "type": "m.room.join_rules"
+ },
+ {
+ "content": {
+ "name": "Some cool room"
+ },
+ "sender": "@room_admin:example.com",
+ "state_key": "",
+ "type": "m.room.name"
+ },
+ {
+ "content": {
+ "url": "mxc://example.com/xyz54321"
+ },
+ "sender": "@room_admin:example.com",
+ "state_key": "",
+ "type": "m.room.avatar"
+ },
+ {
+ "content": {
+ "avatar_url": "mxc://example.org/abc1234",
+ "displayname": "Knocking User",
+ "membership": "knock"
+ },
+ "sender": "@knocking_user:example.org",
+ "state_key": "@knocking_user:example.org",
+ "type": "m.room.member",
+ }
+ ]
+}
+```
+
+##### Status code 403:
+This request is forbidden, e.g. the user is banned from the room. Example reply:
+```json
+{
+ "errcode": "M_FORBIDDEN",
+ "error": "You are not allowed to knock on this room"
+}
+```
+
+##### Status code 404:
+The room is unknown to the remote server. Example reply:
+```json
+{
+ "errcode": "M_NOT_FOUND",
+ "error": "Unknown room"
+}
+```
+
+## Restrictions
+There are restrictions to being able to set this membership, as well as
+accepting or denying the knock. A formal description of the changes to the auth rules is given below;
+first we summarise the semantics of the proposed changes.
+
+### Current membership
+Only users without a current membership or with their current membership
+set to "knock" or "leave" can knock on a room. This means that a user that
+is banned, is invited or is currently in the room cannot knock on it.
+
+### Join Rules
+This proposal makes use of the existing "knock" join rule. The value of
+`join_rule` in the content of the `m.room.join_rules` state event for a room
+must be set to "knock" for a knock to succeed. This means that existing rooms
+will need to opt into allowing knocks in their rooms. Other than allowing
+knocks, a join rule of "knock" is functionally equivalent to that of
+"invite", except that it additionally allows external users to change their
+membership to "knock" under certain conditions.
+
+### Auth rules
+
+Each room version defines the auth rules which should be applied in that room version.
+This MSC proposes a new room version with the following changes to the [auth
+rules from room version 6](https://matrix.org/docs/spec/rooms/v6#authorization-rules-for-events):
+
+* Under "5. If type is `m.room.member`", insert the following after "e. If membership is `ban`":
+
+ ```
+ f. If `membership` is `knock`:
+ i. If the `join_rule` is anything other than `knock`, reject.
+ ii. If `sender` does not match `state_key`, reject.
+ iii. If the `sender`'s membership is not `ban`, `invite` or `join`, allow.
+ iv. Otherwise, reject.
+ ```
+
+Note that:
+ - Both the `sender` and `state_key` fields are set to the user ID of the knocking
+ user. This is different to an `invite` membership event, where the `sender` is the inviter and
+ the `state_key` is the invitee.
+ - f.ii is justified as one user should not be able to knock on behalf of
+ another user.
+ - f.iii is justified as knocks should not be allowed if the knocking user
+ has been banned from the room, is invited to the room or if they are already
+ in the room.
+ - Knocks are not restricted by power level like invites are. The `join_rules`
+ are already used to enforce whether someone can or cannot knock. However,
+ power level rules do apply when approving or denying the knock, as discussed
+ in the Membership Changes section below.
+
+Additionally, note that redactions of knock events are not a concern, as
+`membership` keys are excluded from being redacted as defined by all current
+room versions.
+
+## Membership changes
+Once someone has sent a `knock` membership into the room, the membership for
+that user can be transitioned to the following possible states:
+ - `invite`: In this case, the knock was accepted by someone inside the room
+ and they are inviting the knocker into the room.
+ - `leave`: In this case, similar to how kicks are handled, the knock has
+ been rejected. Alternatively, the knocking user has rescinded their knock.
+ - `ban`: In this case, the knock was rejected and the user has been prevented
+ from sending further knocks.
+
+Let's talk about each one of these in more detail.
+
+### Membership change to `invite`
+
+The knock has been accepted by someone in the room.
+
+The user who is accepting the knock must have the power level to perform
+invites. The accepting user's homeserver will then send an invite - over federation if
+necessary - to the knocking user. The knocking user may then join the room as
+if they had been invited normally.
+
+To accept a knock, the client should call [`POST
+/_matrix/client/r0/rooms/{roomId}/invite`](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-invite)
+with the user ID of the knocking user in the JSON body.
+
+If the knocking user is on another homeserver, then the homeserver of the
+accepting user will call [`PUT
+/_matrix/federation/v2/invite/{roomId}/{eventId}`](https://matrix.org/docs/spec/server_server/r0.1.4#put-matrix-federation-v2-invite-roomid-eventid)
+on the knocking homeserver to inform it that the knock has been accepted.
+
+The knocking homeserver should assume an invite to a room it has knocked on means
+that its knock has been accepted, even if the invite was not explicitly
+related to the knock attempt.
+
+Note that client or homeserver implementations are free to automatically
+accept this invite given they're aware that it's the result of a previous
+knock. In case of failing to auto-accept an invite on the homeserver, it's
+recommended for homeservers to pass the invite down to the client so that it
+may try at a later point (or reject the potentially broken invite) at the user's
+discretion.
+
+### Membership change to `leave` via rejecting a knock
+
+The knock has been rejected by someone in the room.
+
+To reject a knock, the rejecting user's client must call [`POST
+/_matrix/client/r0/rooms/{roomId}/kick`](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-kick)
+with the user ID of the knocking user in the JSON body. Rejecting a knock
+over federation has a slight catch, though.
+
+When the knocking user is on another homeserver, the homeserver of the
+rejecting user needs to send the `leave` event over federation to the
+knocking homeserver. However, this is a bit tricky as it is currently very
+difficult to have events from a room propagate over federation when the
+receiving homeserver is not in the room. This is due to the remote homeserver
+being unable to verify that the event being sent is actually from a
+homeserver in the room - and that the homeserver in the room had the required
+power level to send it. This is a problem that currently affects other,
+similar operations, such as disinviting or unbanning a federated user. In
+both cases, they won't be notified as their homeserver is not in the room.
+
+While we could easily send the leave event as part of a generic
+transaction to the remote homeserver, that homeserver would have no way to
+validate the `prev_events` and `auth_events` that the event references. We
+could send those events over as well, but those will also reference other
+events that require validation and so on.
+
+A simple thing we could easily do here is to trust the leave event implicitly
+if it is sent by the homeserver we originally knocked through. We know this
+homeserver is (or at least was) in the room, so they're probably telling the
+truth. This is almost an edge case though, as while you'll knock through one
+homeserver in the room, there's no guarantee that the admin that denies your
+knock will be on the same homeserver you knocked through. Perhaps the
+homeserver you knocked through could listen for this and then send the event
+back to you - but what if it goes offline in the meantime?
+
+As such, informing remote homeservers about the rejection of knocks over
+federation is de-scoped for now, and left to a future MSC which can solve
+this class of problem in a suitable way. Rejections should still work for the
+homeservers that are in the room, as they can validate the leave event for
+they have access to the events it references.
+
+### Membership change to `leave` via rescinding a knock
+The knocking user has rescinded their knock.
+
+To rescind a knock, the knocking user's client must call [`POST
+/_matrix/client/r0/rooms/{roomId}/leave`](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-leave).
+To rescind a knock over federation, the knocking homeserver must complete
+a [`make_leave`, `send_leave` dance](
+https://matrix.org/docs/spec/server_server/r0.1.4#leaving-rooms-rejecting-invites)
+with a homeserver in the room.
+
+### Membership change to `ban`
+
+The knock has been rejected by someone in the room and the user has been
+banned, and is unable to send further knocks.
+
+This one is fairly straightforward. Someone with the appropriate power levels
+in the room bans the user. This will have the same effect as rejecting the
+knock, and in addition prevent any further knocks by this user from being
+allowed into the room.
+
+If the user is unbanned, they will be able to send a new knock which could be
+accepted.
+
+To ban the user, the client should call [`POST
+/_matrix/client/r0/rooms/{roomId}/ban`](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-ban) with the user ID of the knocking user in the JSON body.
+
+Informing the knocking user about the update is the same as rejecting the
+knock.
+
+# Potential issues
+This new feature would allow users to send events into rooms that they don't
+partake in. That is why this proposal enables the `knock` join rule, in
+order to allow room admins to opt in to this behaviour.
+
+# Alternatives
+The possibility of initiating a knock by sending EDUs between users instead of sending
+a membership state event into a room has been raised. This is an ongoing discussion
+occurring at https://github.com/matrix-org/matrix-doc/pull/2403/files#r573180627.
+
+# Client UX recommendations
+After a knock is received in a room, it is expected to be displayed in the
+timeline, similar to other membership changes. Clients can optionally add a way
+for users of a room to review all current knocks.
+
+Please also note the recommendations for clients in the "Security considerations"
+section below.
+
+# Security considerations
+Clients must take care when implementing this feature in order to prevent
+simple abuse vectors that can be accomplished by individual users. For
+instance, when a knock occurs, clients are advised to hide the reason until
+the user interacts with the client in some way (e.g. clicking on a "show
+reason" button). The user should reveal the reason only if they choose to.
+
+It is recommended to not display the reason by default as else this would
+essentially allow outsiders to send messages into the room.
+
+It is still theoretically possible for a homeserver admin to create many users
+with different user IDs or display names, all spelling out an abusive
+message, and then having each of them knock in order.
+
+Clients should also do their best to prevent impersonation attacks. Similar to
+joins, users can set any displayname or avatar URL they'd like when knocking on
+a room. Clients SHOULD display further information to help identify the user,
+such as User ID, encryption verification status, rooms you share with the user,
+etc. Care should be taken to balance the importance of preventing attacks while
+avoiding overloading the user with too much information or raising false
+positives.
+
+Another abuse vector is allowed by the ability for users to rescind knocks.
+This is to help users in case they knocked on a room accidentally, or simply
+no longer want to join a room they've knocked on. While this is a useful
+feature, it also allows users to spam a room by knocking and rescinding their
+knocks over and over. Particularly note-worthy is that this will generate
+state events that homeservers in the room will need to process. And while
+join/leave state changes will do the same in a public room, the act of
+knocking is much lighter than the act of joining a room.
+
+In both cases, room admins should employ typical abuse mitigation tools, such
+as user bans and server ACLs. Clients are encouraged to make employing these
+tools easy even if the offensive user or server is not present in the room.
+
+# Unstable prefix
+
+An unstable feature flag is not required due to this proposal's requirement
+of a new room version. Clients can check for a room version that includes
+knocking via the Client-Server API's [capabilities
+endpoint](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-capabilities).
+Experimental implementation should use `xyz.amorgan.knock` as a room version identifier.
+
+The new endpoints should contain an unstable prefix during experimental
+implementation. The unstable counterpart for each endpoint is:
+
+C-S knock:
+
+* `POST /_matrix/client/knock/{roomIdOrAlias}`
+* `POST /_matrix/client/unstable/xyz.amorgan.knock/knock/{roomIdOrAlias}`
+
+S-S make_knock:
+
+* `GET /_matrix/federation/v1/make_knock/{roomId}/{userId}`
+* `GET /_matrix/federation/unstable/xyz.amorgan.knock/make_knock/{roomId}/{userId}`
+
+S-S send_knock:
+
+* `PUT /_matrix/federation/v1/send_knock/{roomId}/{eventId}`
+* `PUT /_matrix/federation/unstable/xyz.amorgan.knock/send_knock/{roomId}/{eventId}`
+
+Finally, an unstable prefix is added to the key that comes down `/sync`,
+the join rule for rooms and the `content.membership` key of the member
+event sent into rooms when a user has knocked successfully. Instead of
+`knock`, experimental implementations should use `xyz.amorgan.knock`.
diff --git a/proposals/2414-optional-content-reporting-reason.md b/proposals/2414-optional-content-reporting-reason.md
new file mode 100644
index 00000000000..43cdf6c72ff
--- /dev/null
+++ b/proposals/2414-optional-content-reporting-reason.md
@@ -0,0 +1,45 @@
+# MSC2414: Make `reason` and `score` optional for reporting content
+
+## Proposal
+
+On the [report content endpoint](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-report-eventid)
+we remove the `required` flag for both the `reason` and `score` parameters, as
+well as the "may be blank" clause in the description of `reason`.
+
+## Rationale
+
+### `reason` Parameter
+
+Currently, the spec says that the `reason` parameter on the content reporting
+endpoint is required, but also says that the string "may be blank." This
+seems to be a contradiction.
+
+Note that the kicking and banning endpoints already have optional `reason`
+parameters. The other membership endpoints mentioned in
+[#2367][membership-endpoints] will also add optional `reason` parameters,
+so it would be more more consistent with the rest of the spec to make this
+optional as well.
+
+### `score` Parameter
+
+The spec also requires the `score` parameter, but its usefulness is limited.
+Offensiveness is difficult to measure and is likely not going to be applied
+consistently across several rooms. Because of this ambiguity, it seems, many
+clients [simply hard-code the integer value][hard-code].
+
+To make this useful, for example, room administrators would need a way to map more
+specific values to the integer range and perhaps even instruct the client to
+display those mappings to the user. That may be possible to do in a closed
+client/homeserver implementation, but not generally across the Matrix protocol.
+
+Making `score` optional would enable this feature to be used in specific contexts
+while not forcing clients to support the ambiguity it brings.
+
+## Backwards Compatibility
+
+Since servers currently expect these fields to be sent by all clients, making
+them optional is a breaking change. Clients should check the spec versions
+the homeserver supports to detect this change.
+
+[membership-endpoints]: https://github.com/matrix-org/matrix-doc/pull/2367
+[hard-code]: https://github.com/matrix-org/matrix-react-sdk/pull/3290/files#diff-551ca16d6a8ffb96888b337b5246402dR66
diff --git a/proposals/2557-spoiler-clarifications.md b/proposals/2557-spoiler-clarifications.md
new file mode 100644
index 00000000000..0391ee97e43
--- /dev/null
+++ b/proposals/2557-spoiler-clarifications.md
@@ -0,0 +1,20 @@
+# MSC2557: Clarifications on spoilers
+
+Spoiler messages are described in [MSC2010](https://github.com/matrix-org/matrix-doc/pull/2010)
+though the MSC is unclear if the fallback is required to be sent by clients.
+
+## Proposal
+
+The fallback for spoiler messages is optional, though recommended to be sent by clients. Clients
+should make reasonable efforts to represent the spoiler in the `body` field of a message.
+
+The recommended fallback format is unchanged.
+
+Additionally, this proposal opens up spoilers to any HTML-supporting message types. Currently
+this includes `m.text` (already included by MSC2010), `m.notice`, and `m.emote`.
+
+## Potential issues
+
+Clients could inadvertently spoil parts of a message by not representing the spoiler correctly
+in the `body` of the message. The author believes this would quickly show up as a bug report
+on the client due to the nature of spoilers.
diff --git a/proposals/2674-event-relationships.md b/proposals/2674-event-relationships.md
new file mode 100644
index 00000000000..95f077bada0
--- /dev/null
+++ b/proposals/2674-event-relationships.md
@@ -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"
+ }
+ }
+}
+```
diff --git a/proposals/2675-aggregations-server.md b/proposals/2675-aggregations-server.md
new file mode 100644
index 00000000000..5808dbec008
--- /dev/null
+++ b/proposals/2675-aggregations-server.md
@@ -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.
\ No newline at end of file
diff --git a/proposals/2713-remove-deprecated-identity-endpoints.md b/proposals/2713-remove-deprecated-identity-endpoints.md
new file mode 100644
index 00000000000..6e49477dfe3
--- /dev/null
+++ b/proposals/2713-remove-deprecated-identity-endpoints.md
@@ -0,0 +1,26 @@
+# MSC2713: Remove deprecated Identity Service endpoints
+
+Implementations will have had plenty of time to adopt the new v2 API for Identity Servers, so
+we should clean out the old endpoints.
+
+All deprecated endpoints in the r0.3.0 Identity Service API specification are to be removed.
+
+For completeness, this includes:
+
+* `GET /_matrix/identity/api/v1`
+* `GET /_matrix/identity/api/v1/pubkey/{keyId}`
+* `GET /_matrix/identity/api/v1/pubkey/isvalid`
+* `GET /_matrix/identity/api/v1/pubkey/ephemeral/isvalid`
+* `GET /_matrix/identity/api/v1/lookup`
+* `POST /_matrix/identity/api/v1/bulk_lookup`
+* `POST /_matrix/identity/api/v1/validate/email/requestToken`
+* `POST /_matrix/identity/api/v1/validate/email/submitToken`
+* `GET /_matrix/identity/api/v1/validate/email/submitToken`
+* `POST /_matrix/identity/api/v1/validate/msisdn/requestToken`
+* `POST /_matrix/identity/api/v1/validate/msisdn/submitToken`
+* `GET /_matrix/identity/api/v1/validate/msisdn/submitToken`
+* `GET /_matrix/identity/api/v1/3pid/getValidated3pid`
+* `POST /_matrix/identity/api/v1/3pid/bind`
+* `POST /_matrix/identity/api/v1/3pid/unbind`
+* `POST /_matrix/identity/api/v1/store-invite`
+* `POST /_matrix/identity/api/v1/sign-ed25519`
diff --git a/proposals/2732-olm-fallback-keys.md b/proposals/2732-olm-fallback-keys.md
new file mode 100644
index 00000000000..5ab90117cf3
--- /dev/null
+++ b/proposals/2732-olm-fallback-keys.md
@@ -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.`.
diff --git a/proposals/2758-textual-id-grammar.md b/proposals/2758-textual-id-grammar.md
new file mode 100644
index 00000000000..626f84aebe7
--- /dev/null
+++ b/proposals/2758-textual-id-grammar.md
@@ -0,0 +1,56 @@
+# MSC2758: Common grammar for textual identifiers
+
+The matrix specification uses textual identifiers for a wide range of
+concepts. Examples include "event types" and "room versions".
+
+In the past, these identifiers have often lacked a formal grammar, leaving
+servers and clients to make assumptions about questions such as which
+characters are permitted, minimum and maximum lengths, etc.
+
+This proposal suggests a common grammar which can be used as a basis for
+*future* identifier types, to reduce the work involved in future specification
+work.
+
+No attempt is made here to bring existing identifiers into line; however
+examples of identifiers which might have benefitted from such a grammar in the
+past include:
+
+ * [`capabilities`](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-capabilities)
+ identifiers.
+ * authentication types for the [User-Interactive Authentication mechanism](https://matrix.org/docs/spec/client_server/r0.6.0#user-interactive-authentication-api).
+ * login types for [`/_matrix/client/r0/login`](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login).
+ * event types
+ * [`m.room.message` `msgtypes`](https://matrix.org/docs/spec/client_server/r0.6.0#m-room-message-msgtypes)
+ * `app_id` for [`POST /_matrix/client/r0/pushers/set`](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-pushers-set).
+ * `rule_ids`, `actions` and `tweaks` for [push rules](https://matrix.org/docs/spec/client_server/r0.6.0#push-rules).
+ * [E2E messaging algorithm names](https://matrix.org/docs/spec/client_server/r0.6.0#messaging-algorithm-names).
+
+## Proposal
+
+We define a "common namespaced identifier grammar". This can then be referenced
+by other parts of the grammar, in much the same way as [Unpadded
+Base64](https://matrix.org/docs/spec/appendices#unpadded-base64) is defined
+today.
+
+The grammar is defined as follows:
+
+ * An identifier may not be less than one character or more than 255 characters
+ in length.
+ * Identifiers must start with one of the characters `[a-z]`, and be entirely
+ composed of the characters `[a-z]`, `[0-9]`, `-`, `_` and `.`.
+ * Identifiers starting with the characters `m.` are reserved for use by the
+ formal matrix specification.
+ * Implementations wishing to implement unspecified identifiers should follow
+ the Java Package Naming convention of starting with a reversed domain
+ name (with a dot after the domain name part). For example, for the
+ organisation `example.com`, a valid identifier would be
+ `com.example.identifier`.
+
+This grammar is intended for use entirely by internal identifiers, and *not*
+for user-visible strings.
+
+### Rationale
+
+ * Avoiding non-ascii characters sidesteps any issues with homoglyphs or
+ altenative encodings of the same characters.
+ * Avoiding upper-case character sidesteps any concerns over case-sensitivity.
diff --git a/proposals/2765-widget-avatars.md b/proposals/2765-widget-avatars.md
new file mode 100644
index 00000000000..a9030d294a5
--- /dev/null
+++ b/proposals/2765-widget-avatars.md
@@ -0,0 +1,51 @@
+# MSC2765: Widget avatars
+
+Currently widgets have a name and title associated with them, though no opportunity for avatars
+for a favicon-like experience. This proposal introduces such a concept.
+
+## Proposal
+
+A new optional paramater named `avatar_url` is added to the widget definition. This parameter is
+an MXC URI to an image clients can use to associate with the widget, likely alongside the `name`
+and/or `title`.
+
+Widget avatars SHOULD be legible at small sizes, such as 20x20. The MXC URI in the `avatar_url`
+should be the source material to allow clients to use the `/thumbnail` API to get a size for their
+use case.
+
+Rendering avatars is optional for clients, much like how clients are not required to use the `name`
+or `title` of a widget.
+
+An example widget would be:
+
+```json
+{
+ "creatorUserId": "@alice:example.org",
+ "data": {
+ "custom_key": "This is a custom key",
+ "title": "This is a witty description for the widget"
+ },
+ "id": "20200827_WidgetExample",
+ "name": "My Cool Widget",
+ "type": "m.custom",
+ "url": "https://example.org/my/widget.html?roomId=$matrix_room_id",
+ "waitForIframeLoad": true,
+ "avatar_url": "mxc://example.org/abc123"
+}
+```
+
+## Alternatives
+
+We could define a whole structured system for different thumbnail sizes, though we have a thumbnail
+API which can be used to request whatever size is needed by the client.
+
+## Security considerations
+
+Widget avatars could be non-images. Clients should use the thumbnail API to encourage error responses
+from the server when a widget avatar is a non-image.
+
+## Unstable prefix
+
+While this MSC is not in a released version of the specification, clients should use an alternative
+event type for widgets or use `org.matrix.msc2765.avatar_url` when using `m.widget` or `m.widgets`
+as an event type.
diff --git a/proposals/2774-widget-id.md b/proposals/2774-widget-id.md
new file mode 100644
index 00000000000..41c67f8f760
--- /dev/null
+++ b/proposals/2774-widget-id.md
@@ -0,0 +1,54 @@
+# MSC2774: Giving widgets their ID so they can communicate
+
+Under the [current specification](https://github.com/matrix-org/matrix-doc/pull/2764), widgets are
+able to communicate with their client host, however doing so is a bit difficult if they don't already
+know their widget ID. Some widgets will be able to get their ID from another source like an
+integration manager, however this is not the case for all widgets.
+
+[MSC2762](https://github.com/matrix-org/matrix-doc/pull/2762) has a fair amount of background
+information on widgets, as does the current specification linked above.
+
+## Proposal
+
+Currently widgets can expect a `?widgetId` query parameter sent to them in clients like Element,
+however this has some issues and as such is not proposed to exist any further. One of the issues
+is that the widget must retain the query string, which isn't entirely possible for some frontends
+(they would instead prefer to use the fragment). It's also a privacy risk in that by being sent
+through the query string, the server can be made aware of the widget ID. The widget ID doesn't
+normally contain any useful information (it's an opaque string), however it's not required for
+the server to function under typical usage.
+
+The proposal is to introduce a `$matrix_widget_id` template variable for the URL, similar to the
+existing `$matrix_room_id` variable. Widgets can then have their widget ID wherever they want in
+the widget URL, making it easier on them to find and use.
+
+This carries the same risks as a room ID being passed to the widget: the client can easily lie about
+it, however there's no real risk involved in doing so because it's used for communication only. So
+long as the client is able to identify which widget is talking to it, it doesn't really matter. It's
+more of a problem if the client uses a widget ID from another widget as that could confuse the
+client, however this is believed to be a bug. Clients should also be performing origin checks to
+ensure the widget is talking from a sane origin and not somewhere else, like another tab or browser.
+
+## Potential issues
+
+This is not backwards compatible with the history of widgets so far, so clients and widgets will
+need to be modified to support it. Clients which currently use the `?widgetId` parameter are
+encouraged to continue supporting the parameter until sufficient adoption is reached.
+
+## Alternatives
+
+As mentioned, a query parameter could be used, though this has the issues previously covered. Another
+solution might be to allow a single widget API action which has no widget ID solely for the purpose
+of finding the widget ID, however clients are unlikely to be able to differentiate between two widgets
+if this were the case.
+
+Another solution would be to let the widget discover its widget ID by harvesting it out of the first
+widget API request it sees. This can't always be relied upon (some flows require the widget to
+speak first), and as the widget API becomes more capable it could become a security risk. A malicious
+browser extension could spam the widget with fake requests to try and convince it to talk to it
+instead of the client, thus redirecting some information to the wrong place.
+
+## Unstable prefix
+
+Implementations should use `$org.matrix.msc2774_widget_id` as a variable until this lands in a
+released version of the specification.
diff --git a/proposals/2778-appservice-login.md b/proposals/2778-appservice-login.md
index 62bf5b04974..fd509ab8941 100644
--- a/proposals/2778-appservice-login.md
+++ b/proposals/2778-appservice-login.md
@@ -2,7 +2,7 @@
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
+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
@@ -47,7 +47,7 @@ If one of the following conditions are true:
- 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"`.
+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"`.
@@ -95,16 +95,8 @@ could store this information rather than calling `POST /login` at all. This does
- 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.
-
-Most appservices
-which do not implement encryption do not store this information as neither the device_id or access_token are needed f However critically
-this means that bridges will need to be designed to store the access_token and device_id from the point of creating the user,
-so older bridges would be unable to get an access token for existing users as `POST /register` would fail.
-It would difficult to log out these tokens if they got exposed additionally, as the AS would not be able to fetch a new access token.
-Furthermore, the ability to generate access tokens for real users who registered elsewhere would not be possible with this mechanism.
+While `POST /register` does work, it is impactical as the sole method of fetching an access token.
## Security considerations
@@ -116,7 +108,7 @@ this is not a problem because:
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
+- 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.
@@ -125,7 +117,7 @@ this is not a problem because:
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
+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
diff --git a/proposals/2788-v6-default-version.md b/proposals/2788-v6-default-version.md
new file mode 100644
index 00000000000..af9672a442b
--- /dev/null
+++ b/proposals/2788-v6-default-version.md
@@ -0,0 +1,29 @@
+# MSC2788: Room version 6 as a default
+
+Enough time has passed to allow the public federation to upgrade their servers to support room
+version 6. This proposal aims to make v6 the default room version.
+
+## Proposal
+
+The specification adopts v6 as the suggested default room version, making no changes to the stability
+of any room versions. As of writing, v5 is currently the suggested room version.
+
+Room version 6 has the following specification: https://matrix.org/docs/spec/rooms/v6
+
+## Potential issues
+
+Servers will be encouraged to update their config/internal defaults to use v6 instead of v5. This
+is considered a good problem to have.
+
+## Alternatives
+
+None relevant.
+
+## Security considerations
+
+None relevant.
+
+## Unstable prefix
+
+None relevant - servers can already choose a different default room version legally. This MSC
+just formalizes v6 as the default.
diff --git a/proposals/2801-untrusted-event-data.md b/proposals/2801-untrusted-event-data.md
new file mode 100644
index 00000000000..2b0d16aa6fc
--- /dev/null
+++ b/proposals/2801-untrusted-event-data.md
@@ -0,0 +1,190 @@
+# MSC2801: Make it explicit that event bodies are untrusted data
+
+As the Matrix Specification stands today, it is easy for client developers to
+assume that all events will be structured as documented. For example, the
+`displayname` key of an [`m.room.member`
+event](https://matrix.org/docs/spec/client_server/r0.6.1#m-room-member) is
+specified to be "`string` or `null`"; and the `info` key of an [`m.image`
+message event](https://matrix.org/docs/spec/client_server/r0.6.1#m-image) must
+be a Javascript object.
+
+In reality, these are not safe assumptions. This MSC proposes that the
+specification be updated to make it clear that developers should treat all
+event data as untrusted.
+
+## Reasons why events may not match the specification
+
+Firstly, let's examine the reasons why such malformed events can exist. Some of
+these reasons may appear to have trivial solutions; these are discussed below.
+
+ 1. Most obviously, Synapse as it currently stands does very little validation
+ of event bodies sent over the Client-Server API. There is nothing stopping
+ any user sending a malformed `m.room.message` event with a simple `curl`
+ command or Element-Web's `/devtools`.
+
+ Any event sent in this way will be returned to the clients of all the other
+ users in the room.
+
+ 2. Events may be encrypted. Any client implementing E2EE must be prepared to
+ deal with any encrypted content, since by definition a server cannot
+ validate it.
+
+ 3. In order to allow the Matrix protocol to be extensible, server
+ implementations must tolerate unknown event types, and allow them to be
+ passed between clients. It is obvious that a pair of custom clients
+ implementing a `com.example.special.event` event type cannot rely on a
+ standard server implementation to do any validation for them.
+
+ However, this problem extends to event types and keys which have been added
+ to the specification. For example, the [`m.room.encrypted` event
+ type](https://matrix.org/docs/spec/client_server/r0.6.1#m-room-encrypted)
+ was added in Client-Server API r0.4.0. It therefore follows that a server
+ implementing CS-API r0.3.0 would have no way to validate an
+ `m.room.encrypted` event, so if a client is connected to such a server, it
+ could receive malformed events.
+
+ 4. To extend from point 3, the problem is not even resolved by upgrading the
+ server. There may now be rooms which contain historical events which would
+ no longer be accepted, but these will still be served by the server.
+
+ This problem also applies to non-room data such as account data. For
+ example, Client-Server API r0.6.0 added the [`m.identity_server` account
+ data event
+ type](https://matrix.org/docs/spec/client_server/r0.6.1#m-identity-server).
+ It is possible, if unlikely, that a client could have uploaded an
+ `m.identity_server` event before the administrator upgraded the server.
+
+ 5. Event redaction removes certain keys from an event. This is a bit of a
+ trivial case, though it is worth noting that the rules for event redaction
+ vary between room versions, so it's possible to see a variety of "partial"
+ events.
+
+ 6. All the cases above can occur without federation. Federation adds
+ additional complexities due to the structure of Matrix rooms. In
+ particular, a server implementation cannot simply ignore any malformed
+ events since such events may either be critical to the structure of the
+ room (for example, they may be `m.room.membership` events), or at the very
+ least ignoring them would leave "holes" in the event graph which would
+ prevent correct back-pagination.
+
+## Ideas for mitigating the problem
+
+The problems above appear to have some easy solutions. Let's describe some of
+those ideas, before considering the fundamental problem that none of them can
+solve.
+
+### Validate all events when they are submitted
+
+In this idea, we would require all server implementations to strictly validate
+any data which is sent over the Client-Server API, to ensure that it complied
+with the specified formats.
+
+This evidently solves problem 1 above, in that it would prevent local users from
+creating malformed events of any event types that the server supports; however,
+it would do nothing to address any of the other problems.
+
+### Validate events at the point they are served to a client
+
+We could require that server implementations validate any data that they are
+about to serve to a client; we might recommend that malformed data be stripped
+out of the response, or redacted, or similar.
+
+It is worth mentioning that this would be tricky to implement efficiently on
+the server side, but it at least helps address most of the problems above, such
+as historical data, or malformed events received over federation.
+
+### Have servers re-validate data on upgrade
+
+Similar to the previous idea, but rather than validating data each time it is
+served to a client, any stored data could be re-validated to check that it
+complies with new validation requirements.
+
+This could be more efficient in the case that changes to validation rules are
+rare, but it could still be a huge amount of data processing on a large server.
+
+### Create new room versions which require events to be well-formed
+
+As outlined above, one of the big problems in this area is how we deal with
+events sent over federation; in particular, if subsets of the servers in a room
+have different ideas as to which events are "valid", then their concepts of the
+room state can begin to drift, and the room can eventually become
+"split-brained". This makes it hard to simply say, for example,
+"`m.room.member` events with a non-string `displayname` are invalid and should
+not form part of the room state": we have a risk that some servers will accept
+the event, and some will not.
+
+One approach to solving this is via [room
+versions](https://matrix.org/docs/spec/index#room-versions). By specifying that
+a change of rules only applies for a future room version, we can eliminate this
+potential disagreement.
+
+The process of changing a room from one version to another is intrusive, not
+least because it requires that all servers in a room support the new room
+version (or risk being locked out). For that reason, it is extremely
+undesirable that any new feature require a new room version: whenever possible,
+it should be possible to use new features in existing rooms. It therefore
+follows that we cannot rely on room versions to provide validation of event
+data.
+
+### Create a single new room version which applies all event validation rules
+
+This idea is included for completeness, though it is unclear how it would work
+in practice.
+
+It has been suggested that we create a new room version which explicitly states
+that events which fail the current event schema, whatever that is at that
+moment in time, should be rejected.
+
+Let's imagine that in future, the `m.room.member` event schema is extended to
+include an optional `location` key, which, if given, must be a string. The
+implication of this idea is that servers should reject any `m.room.member`
+event whose `location` is not a string. We now have a problem: any servers in
+the room which are updated to the latest spec will reject such malformed
+events, but any other servers yet to be upgraded will allow it. So is that user
+in the room or not?
+
+Even if all the servers in the room can be upgraded at once, what about any
+`m.room.member` events which were sent before the rule change?
+
+## The fundamental problem
+
+The ideas above all mitigate the problems discussed earlier to a greater or
+lesser extent, and may indeed be worth doing on their own merits. However, none
+of them can address the problem of outdated server implementations.
+
+For example, consider the case of a new key being added to an event body, say
+`m.relates_to`. Now, we may have decided as above that all new specced keys
+must be validated by the server, so `vN+1` of Synapse dutifully implements such
+validation and refuses to accept events with a malformed `m.relates_to`.
+
+The problem comes for users whose server is still Synapse `vN`. It knows
+nothing of `m.relates_to`, so accepts and passes it through even if
+malformed. The only potential solution is for clients seeking to implement
+`m.relates_to` to refuse to talk to servers which do not declare support for
+it.
+
+However, this is an entirely client-side feature: it is illogical to require
+that servers must be upgraded before it can be used. Consider that the hosted
+element-web at `https://app.element.io` is upgraded to support the new feature;
+in this scenario, that would lock out any user whose homeserver had not yet
+been upgraded. This is not an acceptable user experience.
+
+In short, we are left with the reality that clients must still handle the
+unvalidated data.
+
+## Conclusions
+
+Short of closely coupling server and client versions - which violates the
+fundamental ethos of the Matrix project - there is nothing that can completely
+prevent clients from having to handle untrusted data. In addition, encrypted
+events eliminate any possibility of server-side validation.
+
+With that in mind, the advantages of the ideas above are diminished. If clients
+must handle untrusted data in some circumstances, why not in all? "You can
+trust the content of this data structure, provided you have checked that the
+server knows how to validate it, in which case you need to treat it as
+untrusted" is not a useful message for a client developer.
+
+It may be possible to assert that specific, known cases can be treated as
+trusted data, but these should be called out as specific cases. The default
+message should be that clients must treat all event data as untrusted.
diff --git a/proposals/2844-global-versioning.md b/proposals/2844-global-versioning.md
new file mode 100644
index 00000000000..b6db20c190a
--- /dev/null
+++ b/proposals/2844-global-versioning.md
@@ -0,0 +1,211 @@
+# MSC2844: Using a global version number for the entire specification
+
+Currently we have 4 kinds of versions, all of which have slightly different use cases and semantics
+which apply:
+
+1. The individual API spec document versions, tracked as revisions (`r0.6.1`, for example).
+2. Individual endpoint versioning underneath an API spec document version (`/v1/`, `/v2/`, etc). Note
+ that the client-server API currently ties the major version of its spec document version to the
+ endpoint, thus making most endpoints under it `/r0/` (currently).
+3. Room versions which define a set of behaviour and algorithms on a per-room basis. These are well
+ defined in the spec and are not covered here: https://matrix.org/docs/spec/#room-versions
+4. An overarching "Matrix" version, largely for marketing purposes. So far we've only cut Matrix 1.0
+ back when we finalized the initial versions of the spec documents, but have not cut another one
+ since.
+
+This current system is slightly confusing, and has some drawbacks for being able to compile builds of
+the spec documents (published on matrix.org) and generally try and communicate what supported versions
+an implementation might have. For example, Synapse currently supports 4 different APIs, all of which
+have their own versions, and all of which would need to be considered and compared when validating
+another implementation of Matrix such as a client or push gateway. Instead, Synapse could say it
+supports "Matrix 1.1", making compatibility much easier to determine - this is what this proposal aims
+to define.
+
+## Proposal
+
+Instead of having per-API versions (`r0.6.1`, etc), we have a version that spans the entire specification.
+This version represents versioning for the index (which has quite a bit of unversioned specification on
+it currently), the APIs, room versions, and the appendices (which are also currently unversioned but
+contain specification). Room versions are a bit more nuanced though, and are covered later in this MSC.
+
+The version which covers the entire specification and all its parts is called the "Matrix version", and
+is a promotion of the previously marketing-only version number assigned to the spec. The first version
+after this proposal is expected to be Matrix 1.1, though the spec core team will make that decision.
+v1.0 would be left in the marketing era and recorded for posterity (though still retains no significant
+meaning).
+
+Doing this has the benefits previously alluded to:
+
+* Implementations of Matrix can now easily compare their supported versions using a single identifier
+ without having to (potentially) indicate which API they built support for.
+* Publishing the specification is less likely to contain broken or outdated links due to API versions
+ not matching up properly. This is currently an issue where if we want to release a new version of
+ the server-server specification then we must also either rebuild or manually fix the blob of HTML
+ known as the client-server API to account for the new version - we often forget this step, sometimes
+ because it's just too difficult.
+* Explaining to people what version Matrix or any of the documents is at becomes incredibly simplified.
+ No longer will we have to explain most of what the introduction to this proposal covers to every new
+ person who asks.
+
+### Full Matrix version grammar
+
+The Matrix versioning scheme takes heavy inspiration from semantic versioning, though intentionally does
+not follow it for reasons described throughout this proposal. Primarily, the argument against semantic
+versioning is held in the alternatives section below.
+
+Given a version number `MAJOR.MINOR`, incremement the:
+
+* `MAJOR` version when a substantial change is made to the core of the protocol. This is reserved for
+ interpretation by the Spec Core Team, though is intended to be for extremely invasive changes such
+ as switching away from JSON, introducing a number of features where a `MINOR` version increase just
+ doesn't feel good enough, or changes to the signing algorithms.
+* `MINOR` version when a feature is introduced, or a backwards incompatible change has been managed
+ through the specification. Later on, this proposal explains what it means to manage a breaking change.
+
+When present in the protocol itself, the Matrix version will always be prefixed with `v`. For example,
+`v1.1`.
+
+Additional information can be supplied in the version number by appending a dash (`-`) to the end of the
+version and including any relevant information. This is typically used to denote alpha, beta, unstable,
+or other similar off-cycle release builds. This MSC does not propose a scheme for RCs or pre-releases,
+though the Spec Core Team may wish to do so. This can also be used to represent patch builds for the
+documentation itself, such as correcting spelling mistakes. An example would be `v1.1-patch.20210109`.
+
+See the section on brewing Matrix versions for information on how the unstable version is decided.
+
+`MINOR` versions have a backwards compatibility scheme described later in this proposal. `MAJOR`
+versions are expected to have zero backwards compatibility guarantees to them.
+
+For clarity, `v1.2` will probably work with `v1.1`, though implementations should be wary if they
+depend on a version. As mentioned, the backwards compatibility scheme section goes into more detail on
+this.
+
+Most notably, this MSC does not propose including a patch version at all. The specifics of what would
+be included in a patch version (spelling changes, release process bug fixes, etc) do not impact any
+implementations of Matrix and thus are not needing of a patch version.
+
+### Structure changes and changelogs
+
+The API documents remain mostly unchanged. We'll still have a client-server API, server-server API, etc,
+but won't have versions associated with those particular documents. This also means they would lose their
+individual changelogs in favour of a more general changelog. An exception to this rule is room versions,
+which are covered later in this proposal.
+
+### Endpoint versioning
+
+Under this MSC, all HTTP endpoints in the specification are to be per-endpoint versioned. This is already
+the case for all APIs except the Client-Server API, and so this section deals specifically with that API.
+The deprecation of endpoints is handled later in this proposal.
+
+Under this proposal, all endpoints in the client-server API get assigned `v3` as their per-endpoint
+version as a starting point. This is primarily done to avoid confusion with the ancient client-server API
+versions which had `v1` and called the `rN` system "v2". Though many of the endpoints available today
+are not present in those older API editions, it is still proposed that they start at `v3` to avoid
+confusion with long-standing implementations.
+
+Servers would advertise support for the new Matrix version by appending it to the array in `/versions`.
+If the sever also supports an older `rN` version, it would include those too.
+For example: `["v1.1", "r0.6.1"]`.
+
+### Room versions
+
+*Author's note*: Having many things with the root word "version" can be confusing, so for this section
+"room versions" are called "room editions" and the Matrix version refers to what this proposal is
+introducing. This MSC does not propose renaming "room versions" - that is another MSC's problem.
+
+Room editions are a bit special in that they have their own versioning scheme as servers and, potentially,
+clients need to be aware of how to process the room. As such, a room edition's versioning scheme is not
+altered by this proposal, however the publishing of the (in)stability of a given edition is now covered by the
+newly proposed Matrix version.
+
+Whenever a room edition transitions from stable to unstable, or unstable to stable, or is introduced
+then it would get counted as a feature for a `MINOR` release of Matrix. We don't currently have a plan
+to remove any room editions, so they are not covered as a potential process for this MSC.
+
+### Deprecation approach
+
+Previous to this proposal the deprecation approach was largely undocumented - this MSC aims to codify
+a standardized approach.
+
+An MSC is required to transition something from stable (the default) to deprecated. Once something has
+been deprecated for suitably long enough (usually 1 version), it is eligible for removal from the
+specification with another MSC. Today's process is the same, though not defined explicitly.
+
+The present system for deprecation also allows implementations to skip implementation of deprecated
+endpoints. This proposal does not permit such behaviour: for an implementation to remain compliant
+with the specification, it must implement all endpoints (including deprecated ones) in the version(s)
+it wishes to target.
+
+As an example, if `/test` were introduced in v1.1, deprecated in v1.2, and removed in v1.3 then an
+implementation can support v1.1, v1.2, and v1.3 by implementing `/test` as it was defined in v1.2 (minus
+the deprecation flag). If the implementation wanted to support just v1.2 and v1.3, then it still must
+implement `/test`. If the implementation only wanted to support v1.3, then it *should not* implement
+`/test` at all because it was removed.
+
+Generally deprecation is paired with replacement or breaking changes. For example, if `/v3/sync` were
+to be modified such that it needed to be bumped to `v4`, the MSC which does so would deprecate `/v3/sync`
+in favour of its proposed `/v4/sync`. Because endpoints are versioned on a per-endpoint basis, `/v4/sync`
+will still work with a server that supports `/v3/profile` (for example) - the version number doesn't mean
+an implementation can only use v4 endpoints.
+
+## Potential issues
+
+None appear to be relevant to be discussed on their own - they are discussed in their respective
+sections above when raised.
+
+## Alternatives
+
+There are some strong opinions that we should use proper semantic versioning for the specification
+instead of the inspired system proposed here. So, why shouldn't we use semantic versioning?
+
+1. It's meant for software and library compatibility, not specifications. Though it could theoretically
+ be used as a specification version, the benefits of doing so are not immediately clear. The scheme
+ proposed here is simple enough where rudimentary comparisons are still possible between versions,
+ and existing semantic versioning libraries can still be made to work. Further, the specification's
+ version number should not be relied upon by a library for its versioning scheme - libraries,
+ applications, etc should have their own versioning scheme so they may work independently of the
+ spec's release schedule.
+
+2. It has potential for causing very high major version numbers. Though largely an aesthetic concern,
+ it can be hard to market Matrix v45 (or even Matrix v4) to potential ecosystem adopters due to
+ the apparant unstable-ness of the specification. Similarly, the major version is used for advertising
+ purposes which could be confusing or overly noisy to say there's a major version every few
+ releases. By instead staying in the 1.x series for a long period of time, the specification appears
+ stable and easy to work with, attracting potential adopters and making that 2.0 release feel all
+ that more special.
+
+3. The semantic versioning spec is not followed in practice. Most uses of semantic versioning are
+ actually off-spec adaptations which are largely compatible with the ideals of the system. This, however,
+ puts Matrix in a difficult spot as it would want to say we follow semantic versioning, but can't
+ because there's no relevant specification document to link to. Even if there was, it would appear
+ as though we were encouraging the idea of forking a specification as a specification ourselves,
+ which may be confusing if not sending the wrong message entirely. Though the system proposed here
+ is a reinvention of semantic versioning to a degree, this proposed system is different from how
+ semantic versioning works in so many ways it is not entirely comparable.
+
+4. The benefit of saying we use a well-popularized versioning system is not a strong enough argument
+ to be considered here.
+
+This MSC is also inherently incompatible with semantic versioning due to its approach to deprecation.
+Instead of encouraging breaking changes (removal of endpoints) be major version changes, this MSC
+says that happens at the minor version change level. As mentioned in the relevant section, this is
+not foreseen to be an issue for Matrix given its a system already used by the protocol and is common
+enough to at least be moderately familiar - the arguments for using semantic versioning in this respect
+do not hold up, per above.
+
+## Security considerations
+
+None relevant - if we need to make a security release for Matrix then we simply make a release and
+advertise accordingly.
+
+## Unstable prefix
+
+The author does not recommend that this MSC be implemented prior to it landing due to the complexity
+involved as well as the behavioural changes not being possible to implement. However, if an implementation
+wishes to try anyways, it should use `org.matrix.msc2844` in the `unstable_features` of `/versions`
+and use `/_matrix/client/unstable/org.matrix.msc2844` in place of `/_matrix/client/r0`.
+
+This MSC is largely proven as possible through an in-development build of the specification which uses
+an alternative toolchain for rendering the specification: https://adoring-einstein-5ea514.netlify.app/
+(see the 'releases' dropdown in the top right; link may not be available or even the same as described
+here due to development changes - sorry).
diff --git a/proposals/2858-Multiple-SSO-Identity-Providers.md b/proposals/2858-Multiple-SSO-Identity-Providers.md
new file mode 100644
index 00000000000..1e49d070cf0
--- /dev/null
+++ b/proposals/2858-Multiple-SSO-Identity-Providers.md
@@ -0,0 +1,247 @@
+# MSC2858: Multiple SSO Identity Providers
+
+Matrix already has generic SSO support, but it does not yield the best user experience especially for
+instances which wish to offer multiple identity providers (IdPs). This MSC provides a simple and fully
+backwards compatible way to extend the current spec which would allow clients to give users options
+like `Continue with Google` and `Continue with Github` side-by-side.
+
+Currently, Matrix supports `m.login.sso`, `m.login.token` and `/login/sso/redirect` for clients to
+pass their user to the configured Identity provider and for them to come back with something which
+is exchangeable for a Matrix access token. This flow offers no insight to the user as to what
+Identity providers are available: clients can offer only a very generic `Sign in with SSO`
+button. With the currently possible solutions and workarounds the experience is far from great
+and users have to blindly click `Sign in with SSO` without any clue as to what's hiding on the other
+side of the door. Some users will definitely not be familiar with `SSO` but will be with the concept of
+"Continue with Google" or similar.
+
+## Proposal
+
+We extend the [login
+flow](https://matrix.org/docs/spec/client_server/r0.6.1#login) to allow clients
+to choose an SSO Identity provider before control is handed over to the
+server. The following sequence diagram illustrates the proposed, updated, login flow:
+
+
+
+
+
+### Extensions to login flow discovery
+
+The response to [`GET /_matrix/client/r0/login`](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-login)
+is extended to **optionally** include an `identity_providers` property for
+flows whose type `m.login.sso`. This would look like this:
+
+```json
+{
+ "flows": [
+ {
+ "type": "m.login.sso",
+ "identity_providers": [
+ {
+ "id": "google",
+ "name": "Google",
+ "icon": "mxc://...",
+ "brand": "google"
+ },
+ {
+ "id": "github",
+ "name": "Github",
+ "icon": "mxc://...",
+ "brand": "github"
+ }
+ ]
+ },
+ {
+ "type": "m.login.token"
+ }
+ ]
+}
+```
+
+The value of the `identity_providers` property is a list, each entry consisting
+of an object with the following fields:
+
+ * The `id` field is **required**. It is an opaque string chosen by the
+ homeserver implementation, and uniquely identifies the identity provider on
+ that server. Clients should not infer any semantic meaning from the
+ identifier. The identifier should be between 1 and 255 characters in length,
+ and should consist of the characters matching unreserved URI characters as
+ defined in [RFC3986](http://www.ietf.org/rfc/rfc3986.txt):
+
+ ```
+ ALPHA DIGIT "-" / "." / "_" / "~"
+ ```
+
+ * The `name` field is **required**. It should be a human readable string
+ intended for printing by the client. No explicit length limit or grammar is
+ specified.
+
+ * The `icon` field is **optional**. It should point to an icon representing
+ the IdP. If present then it must be an MXC URI to an image resource.
+
+ * The `brand` field is **optional**. It allows the client to style the login
+ button to suit a particular brand. It should be a string using the following
+ grammar:
+
+ * Must be at least one character and no more than 255 characters in length.
+ * Must start with one of the characters `[a-z]`, and be entirely composed
+ of the characters `[a-z]`, `[0-9]`, `-`, `_` and `.`.
+
+ To reduce confusion over which identifier should be used for each brand
+ (for example: should "Sign in with Microsoft" be `microsoft` or
+ `azure`?), it is proposed to maintain a registry of identifiers outside
+ the core specification document, avoiding the need for a full MSC to add
+ entries to the list. An initial list of proposed identifiers is given below.
+
+ [Rationale: this grammar is based on the
+ [MSC2758](https://github.com/matrix-org/matrix-doc/pull/2758), removing the
+ requirements for a namespaced heirarchy. In
+ [discussion](https://github.com/matrix-org/matrix-doc/pull/2858#discussion_r565506802),
+ it was agreed that a separate registry was seen as important for a
+ lightweight process by which implementations can agree on identifiers. The
+ registry makes the namespacing of MSC2758 redundant; the namespacing system
+ was also somewhat confusing.]
+
+ Server implementations are free to add additional brands, though they should
+ be mindful of clients which do not recognise any given brand.
+
+ Clients are free to implement any set of brands they wish, including all or
+ any of the brands listed in the registry, but are expected to apply a
+ sensible unbranded fallback for any brand they do not recognise/support.
+
+ Where `icon` and `brand` are both present, it is recommended that clients
+ which support the `brand` give precedence to `brand` over `icon`.
+
+### Extend the `/login/sso/redirect` endpoint
+
+A new endpoint is added to support redirecting directly to one of the IdPs:
+
+`GET /_matrix/client/r0/login/sso/redirect/{idp_id}`
+
+This would behave identically to the existing endpoint without the last argument
+except would allow the server to forward the user directly to the correct IdP.
+
+For the case of backwards compatibility the existing endpoint is to remain,
+and if the server supports multiple SSO IdPs it should offer the user a page
+which lets them choose between the available IdP options as a fallback.
+
+If the `idp_id` is unrecognised, the server should display some sort of error
+page to the user. (A protocol whereby an error can be returned to the original
+client could be a matter for a future improvement, but is out of scope for now.)
+
+### Notes on user-interactive auth
+
+No change is proposed to the SSO flow for User-Interactive Authentication.
+
+For a reauthentication operation, the server implementation is free to choose
+any suitable IdP to authenticate the user. (Often, this will simply be
+the IdP that the user logged in with.)
+
+### Proposed initial identifiers for the `brand` indentifier
+
+The following identifiers are proposed for the initial content of the `brand`
+identifier registry. The descriptions are guidelines to help server
+administrators pick a suitable brand identifier, and to help client authors
+style buttons in their clients.
+
+ * Identifier: `apple`
+
+ Description: Suitable for "Sign in with Apple": see
+ https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/.
+
+ * Identifier: `facebook`
+
+ Description: "Continue with Facebook": see
+ https://developers.facebook.com/docs/facebook-login/web/login-button/.
+
+ * Identifier: `github`
+
+ Description: Logos available at https://github.com/logos.
+
+ * Identifier: `gitlab`
+
+ Description: Logos available at https://about.gitlab.com/press/press-kit/.
+
+ * Identifier: `google`
+
+ Description: Suitable for "Google Sign-In": see
+ https://developers.google.com/identity/branding-guidelines.
+
+ * Identifier: `twitter`
+
+ Description: Suitable for "Log in with Twitter": see
+ https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab1.
+
+When considering a new identifier for private use, administrators should pick
+some sensible name following the advice of [RFC6648 sec
+3](https://tools.ietf.org/html/rfc6648#section-3).
+
+## Alternatives
+
+An alternative to the whole approach would be to allow `m.login.sso.$idp` but this forces
+treating an opaque identifier as hierarchical and offers worse backwards compatibility.
+
+An alternative to the proposed backwards compatibility plan where the server offers a
+fallback page which fills the gap and lets the user choose which SSO IdP they need is
+for the server to deterministically always pick one, maybe the first option and let
+old clients only auth via that one but that means potentially locking users out of their
+accounts.
+
+[MSC2964](https://github.com/matrix-org/matrix-doc/pull/2964) proposes
+replacing much of Matrix's authentication mechanism with OAuth2.0. If that is
+adopted, then the Matrix client would not be able to specify an authentication
+mechanism; rather it is left up to the server to host pages allowing the user
+to choose their authentication mechanism.
+
+### Styling information as an alternative to `brand`
+
+The `brand` field is intended to allow clients to style "login" buttons according
+to the identity provider in question. For example, a mobile application might
+show:
+
+
+
+Some identity providers have very specific rules about how such buttons should
+be presented, so a fine level of control is important.
+
+An alternative way to achieve this would be for the server to give full details
+about the styling: icon, font colour, border colour, background colour,
+etc. However, this soon becomes unscalable. For example, it might be desirable
+to offer each logo at a range of resolutions to suit different screen sizes.
+Likewise, some brands need different styling depending on the background
+colour, so a complete second set of colours must be specified to account for
+dark or light themes.
+
+## Potential issues
+
+ * New Identity Providers added by server administators will be unbranded until
+ clients adopt support for the new brand.
+
+## Security considerations
+
+This could potentially aid phishing attacks by bad homeservers, where if the app says
+`Continue with Google` and then they are taken to a page which is styled to look like
+the Google login page they might be a tiny bit more susceptible to being phished as opposed
+as to when they click a more generic `Sign in with SSO` button, but this attack was possible
+anyhow using a different vector of a controlled Element/client instance which modifies
+the text.
+
+
+## Unstable prefix
+
+Whilst in development use `org.matrix.msc2858.identity_providers` for the flow
+discovery and
+`/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect/{idp_id}` for
+the new endpoints.
+
+When identity providers are listed under the experimental
+`org.matrix.msc2858.identity_providers` field of the response to `/login`,
+(instead of `identity_providers`), different values for the `brand` field are
+used. In particular the following were defined:
+
+ * `org.matrix.gitlab` (now `gitlab`).
+ * `org.matrix.github` (now `github`).
+ * `org.matrix.apple` (now `apple`).
+ * `org.matrix.google` (now `google`).
+ * `org.matrix.facebook` (now `facebook`).
+ * `org.matrix.twitter` (now `twitter`).
diff --git a/proposals/2874-single-ssss.md b/proposals/2874-single-ssss.md
new file mode 100644
index 00000000000..d7d312886f2
--- /dev/null
+++ b/proposals/2874-single-ssss.md
@@ -0,0 +1,62 @@
+# MSC2874: Single SSSS
+
+[Secure Secret Storage and
+Sharing](https://github.com/matrix-org/matrix-doc/pull/1946) (SSSS) was
+designed to allow the user to create multiple keys that would be able to
+decrypt different subsets of the secrets. However, the vast majority of users
+do not need this feature.
+
+This proposal defines how clients should behave if they only wish to support a
+single key, by defining which key clients should use if multiple keys are
+present. It also makes the `name` field in the `m.secret_storage.key.*` events
+optional, as this field was mainly added to allow a user to select between
+different keys.
+
+## Proposal
+
+If a client wants to present a simplified interface to users by not supporting
+multiple SSSS keys, then the client should use the default key (the key listed
+in the `m.secret_storage.default_key` account data event.) If there is no
+default key the client may behave as if there is no SSSS key at all. When such
+a client creates an SSSS key, it must mark that key as being the default key.
+
+The `name` field in the `m.secret_storage.key.*` account data events is
+optional, rather than required. If a client wishes to display multiple keys to
+a user and a given key does not have a `name` field, the client may use a
+default name as the key's name, such as "Unnamed key", or "Default key" if the
+key is marked as default.
+
+For example, when a client creates a key with ID `abcdefg`, it will create an
+`m.secret_storage.key.abcdefg` account data event to store information about
+the key. It will then mark it as the default key by setting the
+`m.secret_storage.default_key` account data to `{"key": "abcdefg"}`. When
+another client logs in after this, it will see that the default key has been
+set, and will know to use that key as the SSSS key.
+
+## Potential issues
+
+If secrets are encrypted using a key that is not marked as default, a client
+might not decrypt the secrets, even if it would otherwise be able to.
+
+## Alternatives
+
+Rather than solely relying on the key marked as default, a client could guess
+at what key to use. For example, it could look at the secrets that it needs,
+see what keys they are encrypted with, and if there is only one common key,
+then it could use that. (This is what Element currently does.) Or if there
+are multiple keys, it could use some sort of heuristic to pick a key. However,
+this approach can be error-prone, and it is better to rely on an explicit
+marking.
+
+## Security considerations
+
+None
+
+## Unstable prefix
+
+An unstable prefix is not needed for a behaviour change in choosing the key to
+use as there are no event/endpoint changes.
+
+Some clients already omit the `name` field (notably, matrix-js-sdk
+unintentionally does this -- mea culpa), and this does not seem to be causing
+issues, so an unstable prefix seems unnecessary for this.
diff --git a/proposals/2918-refreshtokens.md b/proposals/2918-refreshtokens.md
new file mode 100644
index 00000000000..8caaef8fa6d
--- /dev/null
+++ b/proposals/2918-refreshtokens.md
@@ -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.
diff --git a/proposals/2946-spaces-summary.md b/proposals/2946-spaces-summary.md
new file mode 100644
index 00000000000..fc0c136b8cc
--- /dev/null
+++ b/proposals/2946-spaces-summary.md
@@ -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).[1](#f1)
+
+#### 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
+
+[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)
diff --git a/proposals/2998-rooms-v7.md b/proposals/2998-rooms-v7.md
new file mode 100644
index 00000000000..ec83793649e
--- /dev/null
+++ b/proposals/2998-rooms-v7.md
@@ -0,0 +1,26 @@
+# MSC2998: Room Version 7
+
+A new room version, `7`, is proposed using [room version 6](https://matrix.org/docs/spec/rooms/v6.html) as a base
+and incorporating the following MSCs:
+
+* [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403) - Add "knock" feature.
+
+Though other MSCs are capable of being included in this version, they do not have sufficient implementation to be
+considered for v7 rooms. A future room version may still include them.
+
+Room version 7 upon being added to the specification shall be considered stable. No other room versions are affected
+by this MSC. Before v7 can enter the specification, MSC2403 needs sufficient review to be eligible to enter the spec
+itself. This MSC is reserving the room version for use in broader testing of MSC2403 - this does not make MSC2403
+stable for use in most implementations.
+
+## A note on spec process
+
+The spec core team has accepted "knocking" as a concept, and is generally aligned on the ideas proposed by MSC2403. As
+such, we're going ahead with reserving a room version number early for some broader testing given MSC2403 is near to the
+point of being stable itself. Typically the team would declare a room version number once all the included MSCs are
+eligible for becoming stable, however in this case it's ideal to push ahead and reserve the version number.
+
+If MSC2403 were to be replaced or otherwise be rejected for some reason, we'd ultimately have a gap in room versions
+which might look weird but does not necessarily have an impact on the specification: room versions have no associative
+ordering, so skipping a perceived sequential version is valid. The sequential versioning is a human ideal, not one of
+the spec.
diff --git a/proposals/3069-guests-whoami.md b/proposals/3069-guests-whoami.md
new file mode 100644
index 00000000000..7666dfb2346
--- /dev/null
+++ b/proposals/3069-guests-whoami.md
@@ -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.
diff --git a/proposals/3083-restricted-rooms.md b/proposals/3083-restricted-rooms.md
new file mode 100644
index 00000000000..1caa9f97096
--- /dev/null
+++ b/proposals/3083-restricted-rooms.md
@@ -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[1](#f1)
+* 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[2](#f2).
+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.[3](#f3)
+
+ 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).[4](#f4)
+
+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
+
+[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)
+
+[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)
+
+[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)
+
+[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)
diff --git a/proposals/3122-deprecate-starting-verifications-without-request.md b/proposals/3122-deprecate-starting-verifications-without-request.md
new file mode 100644
index 00000000000..9a00699bd58
--- /dev/null
+++ b/proposals/3122-deprecate-starting-verifications-without-request.md
@@ -0,0 +1,40 @@
+# MSC3122: Deprecate starting key verifications without requesting first
+
+Currently, the [Key verification
+framework](https://spec.matrix.org/unstable/client-server-api/#key-verification-framework)
+allows a device to begin a verification via to-device messages by sending an
+`m.key.verification.start` event without first sending or receiving an
+`m.key.verification.request` message. (The last sentence of the 5th paragraph
+of the Key verification framework in the unstable spec, as of the time of
+writing.) However, doing so does not provide a good user experience, and
+allowing this adds unnecessary complexity to implementations.
+
+We propose to deprecate allowing this behaviour.
+
+Note that verifications in DMs do not allow this behaviour. Currently, Element
+Web is the only client known to do this.
+
+## Proposal
+
+The ability to begin a key verification by sending an
+`m.key.verification.start` event as a to-device event without a prior
+`m.key.verification.request` is deprecated. New clients should not begin
+verifications in this way, but will still need to accept verifications begun in
+this way, until it is removed from the spec.
+
+## Potential issues
+
+None.
+
+## Alternatives
+
+We could do nothing and leave it in the spec. But we should clean up cruft when
+possible.
+
+## Security considerations
+
+None.
+
+## Unstable prefix
+
+No unstable prefix is required since we are simply deprecating behaviour.
diff --git a/proposals/3173-expose-stripped-state-events.md b/proposals/3173-expose-stripped-state-events.md
new file mode 100644
index 00000000000..830a9388a6c
--- /dev/null
+++ b/proposals/3173-expose-stripped-state-events.md
@@ -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[1](#f1):
+
+> 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[2](#f2)) that
+homeserver implementations include the following events as part of the stripped
+state of a room:
+
+* Create event (`m.room.create`)[3](#f3)
+* 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`)[4](#f4)
+* Room topic (`m.room.topic`)[5](#f5)
+
+## 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
+
+[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)
+
+[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)
+
+[3]: As updated in [MSC1772](https://github.com/matrix-org/matrix-doc/pull/1772). [↩](#a3)
+
+[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)
+
+[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)
diff --git a/proposals/3231-token-authenticated-registration.md b/proposals/3231-token-authenticated-registration.md
new file mode 100644
index 00000000000..a86b9fbc53f
--- /dev/null
+++ b/proposals/3231-token-authenticated-registration.md
@@ -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.
diff --git a/proposals/3283-enable_set_displayname-capabilities.md b/proposals/3283-enable_set_displayname-capabilities.md
new file mode 100644
index 00000000000..e59746d8b5f
--- /dev/null
+++ b/proposals/3283-enable_set_displayname-capabilities.md
@@ -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.
diff --git a/proposals/3288-pass_room_type_in_3pid_invite.md b/proposals/3288-pass_room_type_in_3pid_invite.md
new file mode 100644
index 00000000000..408573c08b1
--- /dev/null
+++ b/proposals/3288-pass_room_type_in_3pid_invite.md
@@ -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`
diff --git a/proposals/3289-rooms-v8.md b/proposals/3289-rooms-v8.md
new file mode 100644
index 00000000000..7212e0a8cc3
--- /dev/null
+++ b/proposals/3289-rooms-v8.md
@@ -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.
diff --git a/proposals/3316-appservice-timestamp-massaging.md b/proposals/3316-appservice-timestamp-massaging.md
new file mode 100644
index 00000000000..b954e59a12e
--- /dev/null
+++ b/proposals/3316-appservice-timestamp-massaging.md
@@ -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.
diff --git a/proposals/images/2858-login.png b/proposals/images/2858-login.png
new file mode 100644
index 00000000000..0d0a06f618c
Binary files /dev/null and b/proposals/images/2858-login.png differ
diff --git a/pyproject.toml b/pyproject.toml
index 060a44fc9db..19a6ee8b5ae 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[ tool.gilesbot ]
[ tool.gilesbot.circleci_artifacts.docs ]
- url = "gen/index.html"
+ url = "public/index.html"
message = "Click details to preview the HTML documentation."
[ tool.gilesbot.circleci_artifacts.swagger ]
diff --git a/event-schemas/check_examples.py b/scripts/check-event-schema-examples.py
similarity index 86%
rename from event-schemas/check_examples.py
rename to scripts/check-event-schema-examples.py
index 31daa478315..0eca7abf99d 100755
--- a/event-schemas/check_examples.py
+++ b/scripts/check-event-schema-examples.py
@@ -117,6 +117,11 @@ def check_example_dir(exampledir, schemadir):
schemapath = examplepath.replace(exampledir, schemadir)
if schemapath.find("$") >= 0:
schemapath = schemapath[:schemapath.find("$")]
+ # Automatically correct for file extension being stripped off
+ if not schemapath.endswith(".yaml"):
+ schemapath += ".yaml"
+ if not examplepath.endswith(".yaml"):
+ examplepath += ".yaml"
try:
check_example_file(examplepath, schemapath)
except Exception as e:
@@ -128,7 +133,14 @@ def check_example_dir(exampledir, schemadir):
if __name__ == '__main__':
+ # Get the directory that this script is residing in
+ script_directory = os.path.dirname(os.path.realpath(__file__))
+
+ # Resolve the directories to check, relative to the script path
+ examples_directory = os.path.join(script_directory, "../data/event-schemas/examples")
+ schema_directory = os.path.join(script_directory, "../data/event-schemas/schema")
+
try:
- check_example_dir("examples", "schema")
+ check_example_dir(examples_directory, schema_directory)
except:
sys.exit(1)
diff --git a/api/check_examples.py b/scripts/check-swagger-sources.py
similarity index 69%
rename from api/check_examples.py
rename to scripts/check-swagger-sources.py
index 94f3495e3c6..df99b7c004d 100755
--- a/api/check_examples.py
+++ b/scripts/check-swagger-sources.py
@@ -108,13 +108,36 @@ def check_swagger_file(filepath):
def resolve_references(path, schema):
+ """Recurse through a given schema until we find a $ref key. Upon doing so,
+ check that the referenced file exists, then load it up and check all of the
+ references in that file. Continue on until we've hit all dead ends.
+
+ $ref values are deleted from schemas as they are validated, to prevent
+ duplicate work.
+ """
if isinstance(schema, dict):
# do $ref first
if '$ref' in schema:
- value = schema['$ref']
- path = os.path.abspath(os.path.join(os.path.dirname(path), value))
- ref = load_file("file://" + path)
- result = resolve_references(path, ref)
+ # Pull the referenced filepath from the schema
+ referenced_file = schema['$ref']
+
+ # Referenced filepaths are relative, so take the current path's
+ # directory and append the relative, referenced path to it.
+ inner_path = os.path.join(os.path.dirname(path), referenced_file)
+
+ # Then convert the path (which may contiain '../') into a
+ # normalised, absolute path
+ inner_path = os.path.abspath(inner_path)
+
+ # Load the referenced file
+ ref = load_file("file://" + inner_path)
+
+ # Check that the references in *this* file are valid
+ result = resolve_references(inner_path, ref)
+
+ # They were valid, and so were the sub-references. Delete
+ # the reference here to ensure we don't pass over it again
+ # when checking other files
del schema['$ref']
else:
result = {}
@@ -143,15 +166,22 @@ def load_file(path):
if __name__ == '__main__':
- paths = sys.argv[1:]
- if not paths:
- paths = []
- for (root, dirs, files) in os.walk(os.curdir):
- for filename in files:
- if filename.endswith(".yaml"):
- paths.append(os.path.join(root, filename))
- for path in paths:
- try:
- check_swagger_file(path)
- except Exception as e:
- raise ValueError("Error checking file %r" % (path,), e)
+ # Get the directory that this script is residing in
+ script_directory = os.path.dirname(os.path.realpath(__file__))
+
+ # Resolve the directory containing the swagger sources,
+ # relative to the script path
+ source_files_directory = os.path.realpath(os.path.join(script_directory, "../data"))
+
+ # Walk the source path directory, looking for YAML files to check
+ for (root, dirs, files) in os.walk(source_files_directory):
+ for filename in files:
+ if not filename.endswith(".yaml"):
+ continue
+
+ path = os.path.join(root, filename)
+
+ try:
+ check_swagger_file(path)
+ except Exception as e:
+ raise ValueError("Error checking file %s" % (path,), e)
diff --git a/scripts/continuserv/README.md b/scripts/continuserv/README.md
deleted file mode 100644
index 40321bb6216..00000000000
--- a/scripts/continuserv/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-continuserv proactively re-generates the spec on filesystem changes, and serves
-it over HTTP. For notes on using it, see [the main
-readme](../../README.rst#continuserv).
diff --git a/scripts/continuserv/index.html b/scripts/continuserv/index.html
deleted file mode 100644
index 24ed7ecbb0e..00000000000
--- a/scripts/continuserv/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
diff --git a/scripts/continuserv/main.go b/scripts/continuserv/main.go
deleted file mode 100644
index 1bd07e6ef2f..00000000000
--- a/scripts/continuserv/main.go
+++ /dev/null
@@ -1,274 +0,0 @@
-// continuserv proactively re-generates the spec on filesystem changes, and serves it over HTTP.
-// It will always serve the most recent version of the spec, and may block an HTTP request until regeneration is finished.
-// It does not currently pre-empt stale generations, but will block until they are complete.
-package main
-
-import (
- "bytes"
- "flag"
- "fmt"
- "io/ioutil"
- "log"
- "net/http"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "strings"
- "sync"
- "sync/atomic"
- "time"
-
- fsnotify "gopkg.in/fsnotify/fsnotify.v1"
-)
-
-var (
- port = flag.Int("port", 8000, "Port on which to serve HTTP")
-
- mu sync.Mutex // Prevent multiple updates in parallel.
- toServe atomic.Value // Always contains a bytesOrErr. May be stale unless wg is zero.
-
- wgMu sync.Mutex // Prevent multiple calls to wg.Wait() or wg.Add(positive number) in parallel.
- wg sync.WaitGroup // Indicates how many updates are pending.
-)
-
-func main() {
- flag.Parse()
-
- w, err := fsnotify.NewWatcher()
- if err != nil {
- log.Fatalf("Error making watcher: %v", err)
- }
-
- dir, err := os.Getwd()
- if err != nil {
- log.Fatalf("Error getting wd: %v", err)
- }
- for ; !exists(path.Join(dir, ".git")); dir = path.Dir(dir) {
- if dir == "/" {
- log.Fatalf("Could not find git root")
- }
- }
-
- walker := makeWalker(dir, w)
- paths := []string{"api", "changelogs", "event-schemas", "scripts",
- "specification", "schemas", "data-definitions"}
-
- for _, p := range paths {
- filepath.Walk(path.Join(dir, p), walker)
- }
-
- wg.Add(1)
- populateOnce(dir)
-
- ch := make(chan struct{}, 100) // Buffered to ensure we can multiple-increment wg for pending writes
- go doPopulate(ch, dir)
-
- go watchFS(ch, w)
- fmt.Printf("Listening on port %d\n", *port)
- http.HandleFunc("/", serve)
- log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
-
-}
-
-func watchFS(ch chan struct{}, w *fsnotify.Watcher) {
- for {
- select {
- case e := <-w.Events:
- if filter(e) {
- fmt.Printf("Noticed change to %s, re-generating spec\n", e.Name)
- ch <- struct{}{}
- }
- }
- }
-}
-
-func makeWalker(base string, w *fsnotify.Watcher) filepath.WalkFunc {
- return func(path string, i os.FileInfo, err error) error {
- if err != nil {
- log.Fatalf("Error walking: %v", err)
- }
- if !i.IsDir() {
- // we set watches on directories, not files
- return nil
- }
-
- rel, err := filepath.Rel(base, path)
- if err != nil {
- log.Fatalf("Failed to get relative path of %s: %v", path, err)
- }
-
- // Normalize slashes
- rel = filepath.ToSlash(rel)
-
- // skip a few things that we know don't form part of the spec
- if rel == "api/node_modules" ||
- rel == "scripts/gen" ||
- rel == "scripts/tmp" {
- return filepath.SkipDir
- }
-
- // log.Printf("Adding watch on %s", path)
- if err := w.Add(path); err != nil {
- log.Fatalf("Failed to add watch on %s: %v", path, err)
- }
- return nil
- }
-}
-
-// Return true if event should trigger re-population
-func filter(e fsnotify.Event) bool {
- // vim is *really* noisy about how it writes files
- if e.Op != fsnotify.Write {
- return false
- }
-
- _, fname := filepath.Split(e.Name)
-
- // Avoid some temp files that vim or emacs writes
- if strings.HasSuffix(e.Name, "~") || strings.HasSuffix(e.Name, ".swp") || strings.HasPrefix(fname, ".") ||
- (strings.HasPrefix(fname, "#") && strings.HasSuffix(fname, "#")) {
- return false
- }
-
- // Forcefully ignore directories we don't care about (Windows, at least, tries to notify about some directories)
- filePath := filepath.ToSlash(e.Name) // normalize slashes
- if strings.Contains(filePath, "/scripts/tmp") ||
- strings.Contains(filePath, "/scripts/gen") ||
- strings.Contains(filePath, "/api/node_modules") {
- return false
- }
-
- return true
-}
-
-func serve(w http.ResponseWriter, req *http.Request) {
- wgMu.Lock()
- wg.Wait()
- wgMu.Unlock()
-
- m := toServe.Load().(bytesOrErr)
- if m.err != nil {
- w.Header().Set("Content-Type", "text/plain")
- w.Write([]byte(m.err.Error()))
- return
- }
-
- ok := true
- var b []byte
-
- file := req.URL.Path
- if file[0] == '/' {
- file = file[1:]
- }
- b, ok = m.bytes[filepath.FromSlash(file)] // de-normalize slashes
-
- if ok && file == "api-docs.json" {
- w.Header().Set("Access-Control-Allow-Origin", "*")
- }
-
- if ok {
- w.Header().Set("Content-Type", "text/html")
- w.Write([]byte(b))
- return
- }
- w.Header().Set("Content-Type", "text/plain")
- w.WriteHeader(404)
- w.Write([]byte("Not found"))
-}
-
-func generate(dir string) (map[string][]byte, error) {
- cmd := exec.Command("python", "gendoc.py")
- cmd.Dir = path.Join(dir, "scripts")
- var b bytes.Buffer
- cmd.Stderr = &b
- err := cmd.Run()
- if err != nil {
- return nil, fmt.Errorf("error generating spec: %v\nOutput from gendoc:\n%v", err, b.String())
- }
-
- // cheekily dump the swagger docs into the gen directory so that it is
- // easy to serve
- cmd = exec.Command("python", "dump-swagger.py", "-o", "gen/api-docs.json")
- cmd.Dir = path.Join(dir, "scripts")
- cmd.Stderr = &b
- if err := cmd.Run(); err != nil {
- return nil, fmt.Errorf("error generating api docs: %v\nOutput from dump-swagger:\n%v", err, b.String())
- }
-
- files := make(map[string][]byte)
- base := path.Join(dir, "scripts", "gen")
- walker := func(path string, info os.FileInfo, err error) error {
- if err != nil {
- return err
- }
- if info.IsDir() {
- return nil
- }
-
- rel, err := filepath.Rel(base, path)
- if err != nil {
- return fmt.Errorf("Failed to get relative path of %s: %v", path, err)
- }
-
- bytes, err := ioutil.ReadFile(path)
- if err != nil {
- return err
- }
- files[rel] = bytes
- return nil
- }
-
- if err := filepath.Walk(base, walker); err != nil {
- return nil, fmt.Errorf("error reading spec: %v", err)
- }
-
- // load the special index
- indexpath := path.Join(dir, "scripts", "continuserv", "index.html")
- bytes, err := ioutil.ReadFile(indexpath)
- if err != nil {
- return nil, fmt.Errorf("error reading index: %v", err)
- }
- files[""] = bytes
-
- return files, nil
-}
-
-func populateOnce(dir string) {
- defer wg.Done()
- mu.Lock()
- defer mu.Unlock()
-
- files, err := generate(dir)
- toServe.Store(bytesOrErr{files, err})
-}
-
-func doPopulate(ch chan struct{}, dir string) {
- var pending int
- for {
- select {
- case <-ch:
- if pending == 0 {
- wgMu.Lock()
- wg.Add(1)
- wgMu.Unlock()
- }
- pending++
- case <-time.After(10 * time.Millisecond):
- if pending > 0 {
- pending = 0
- populateOnce(dir)
- }
- }
- }
-}
-
-func exists(path string) bool {
- _, err := os.Stat(path)
- return !os.IsNotExist(err)
-}
-
-type bytesOrErr struct {
- bytes map[string][]byte // filename -> contents
- err error
-}
diff --git a/scripts/contrib/shell.nix b/scripts/contrib/shell.nix
deleted file mode 100644
index 7bdcdffabc3..00000000000
--- a/scripts/contrib/shell.nix
+++ /dev/null
@@ -1,6 +0,0 @@
-with import {};
-
-(python.buildEnv.override {
- extraLibs = with pythonPackages;
- [ docutils pyyaml jinja2 pygments ];
-}).env
diff --git a/scripts/css/nature.css b/scripts/css/nature.css
index 771e40b221d..69169129566 100644
--- a/scripts/css/nature.css
+++ b/scripts/css/nature.css
@@ -290,3 +290,6 @@ div.admonition-example {
border: 1px solid #ccc;
}
+div#table-of-contents ul {
+ list-style-type: none;
+}
diff --git a/scripts/dump-swagger.py b/scripts/dump-swagger.py
index 232ca8adcce..68a9356aa32 100755
--- a/scripts/dump-swagger.py
+++ b/scripts/dump-swagger.py
@@ -31,7 +31,7 @@
scripts_dir = os.path.dirname(os.path.abspath(__file__))
templating_dir = os.path.join(scripts_dir, "templating")
-api_dir = os.path.join(os.path.dirname(scripts_dir), "api")
+api_dir = os.path.join(os.path.dirname(scripts_dir), "data", "api")
sys.path.insert(0, templating_dir)
@@ -98,10 +98,9 @@
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)
diff --git a/scripts/gendoc.py b/scripts/gendoc.py
deleted file mode 100755
index 7e68ccd7348..00000000000
--- a/scripts/gendoc.py
+++ /dev/null
@@ -1,561 +0,0 @@
-#! /usr/bin/env python
-
-# Copyright 2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from argparse import ArgumentParser
-from docutils.core import publish_file
-import copy
-import fileinput
-import glob
-import os
-import os.path
-import re
-import shutil
-import subprocess
-import sys
-import yaml
-
-script_dir = os.path.dirname(os.path.abspath(__file__))
-docs_dir = os.path.dirname(script_dir)
-spec_dir = os.path.join(docs_dir, "specification")
-tmp_dir = os.path.join(script_dir, "tmp")
-changelog_dir = os.path.join(docs_dir, "changelogs")
-
-VERBOSE = False
-
-"""
-Read a RST file and replace titles with a different title level if required.
-Args:
- filename: The name of the file being read (for debugging)
- file_stream: The open file stream to read from.
- title_level: The integer which determines the offset to *start* from.
- title_styles: An array of characters detailing the right title styles to use
- e.g. ["=", "-", "~", "+"]
-Returns:
- string: The file contents with titles adjusted.
-Example:
- Assume title_styles = ["=", "-", "~", "+"], title_level = 1, and the file
- when read line-by-line encounters the titles "===", "---", "---", "===", "---".
- This function will bump every title encountered down a sub-heading e.g.
- "=" to "-" and "-" to "~" because title_level = 1, so the output would be
- "---", "~~~", "~~~", "---", "~~~". There is no bumping "up" a title level.
-"""
-def load_with_adjusted_titles(filename, file_stream, title_level, title_styles):
- rst_lines = []
-
- prev_line_title_level = 0 # We expect the file to start with '=' titles
- file_offset = None
- prev_non_title_line = None
- for i, line in enumerate(file_stream):
- if (prev_non_title_line is None
- or not is_title_line(prev_non_title_line, line, title_styles)
- ):
- rst_lines.append(line)
- prev_non_title_line = line
- continue
-
- line_title_style = line[0]
- line_title_level = title_styles.index(line_title_style)
-
- # Not all files will start with "===" and we should be flexible enough
- # to allow that. The first title we encounter sets the "file offset"
- # which is added to the title_level desired.
- if file_offset is None:
- file_offset = line_title_level
- if file_offset != 0:
- logv((" WARNING: %s starts with a title style of '%s' but '%s' " +
- "is preferable.") % (filename, line_title_style, title_styles[0]))
-
- # Sanity checks: Make sure that this file is obeying the title levels
- # specified and bail if it isn't.
- # The file is allowed to go 1 deeper or any number shallower
- if prev_line_title_level - line_title_level < -1:
- raise Exception(
- ("File '%s' line '%s' has a title " +
- "style '%s' which doesn't match one of the " +
- "allowed title styles of %s because the " +
- "title level before this line was '%s'") %
- (filename, (i + 1), line_title_style, title_styles,
- title_styles[prev_line_title_level])
- )
- prev_line_title_level = line_title_level
-
- adjusted_level = (
- title_level + line_title_level - file_offset
- )
-
- # Sanity check: Make sure we can bump down the title and we aren't at the
- # lowest level already
- if adjusted_level >= len(title_styles):
- raise Exception(
- ("Files '%s' line '%s' has a sub-title level too low and it " +
- "cannot be adjusted to fit. You can add another level to the " +
- "'title_styles' key in targets.yaml to fix this.") %
- (filename, (i + 1))
- )
-
- if adjusted_level == line_title_level:
- # no changes required
- rst_lines.append(line)
- continue
-
- # Adjusting line levels
- logv(
- "File: %s Adjusting %s to %s because file_offset=%s title_offset=%s" %
- (filename, line_title_style, title_styles[adjusted_level],
- file_offset, title_level)
- )
- rst_lines.append(line.replace(
- line_title_style,
- title_styles[adjusted_level]
- ))
-
- return "".join(rst_lines)
-
-
-def is_title_line(prev_line, line, title_styles):
- # The title underline must match at a minimum the length of the title
- if len(prev_line) > len(line):
- return False
-
- line = line.rstrip()
-
- # must be at least 3 chars long
- if len(line) < 3:
- return False
-
- # must start with a title char
- title_char = line[0]
- if title_char not in title_styles:
- return False
-
- # all characters must be the same
- for char in line[1:]:
- if char != title_char:
- return False
-
- # looks like a title line
- return True
-
-
-def get_rst(file_info, title_level, title_styles, spec_dir, adjust_titles):
- # string are file paths to RST blobs
- if isinstance(file_info, str):
- log("%s %s" % (">" * (1 + title_level), file_info))
- with open(os.path.join(spec_dir, file_info), "r", encoding="utf-8") as f:
- rst = None
- if adjust_titles:
- rst = load_with_adjusted_titles(
- file_info, f, title_level, title_styles
- )
- else:
- rst = f.read()
-
- rst += "\n\n"
- return rst
- # dicts look like {0: filepath, 1: filepath} where the key is the title level
- elif isinstance(file_info, dict):
- levels = sorted(file_info.keys())
- rst = []
- for l in levels:
- rst.append(get_rst(file_info[l], l, title_styles, spec_dir, adjust_titles))
- return "".join(rst)
- # lists are multiple file paths e.g. [filepath, filepath]
- elif isinstance(file_info, list):
- rst = []
- for f in file_info:
- rst.append(get_rst(f, title_level, title_styles, spec_dir, adjust_titles))
- return "".join(rst)
- raise Exception(
- "The following 'file' entry in this target isn't a string, list or dict. " +
- "It really really should be. Entry: %s" % (file_info,)
- )
-
-
-def build_spec(target, out_filename):
- log("Building templated file %s" % out_filename)
- with open(out_filename, "w", encoding="utf-8") as outfile:
- for file_info in target["files"]:
- section = get_rst(
- file_info=file_info,
- title_level=0,
- title_styles=target["title_styles"],
- spec_dir=spec_dir,
- adjust_titles=True
- )
- outfile.write(section)
-
-
-"""
-Replaces relative title styles with actual title styles.
-
-The templating system has no idea what the right title style is when it produces
-RST because it depends on the build target. As a result, it uses relative title
-styles defined in targets.yaml to say "down a level, up a level, same level".
-
-This function replaces these relative titles with actual title styles from the
-array in targets.yaml.
-"""
-def fix_relative_titles(target, filename, out_filename):
- log("Fix relative titles, %s -> %s" % (filename, out_filename))
- title_styles = target["title_styles"]
- relative_title_chars = [
- target["relative_title_styles"]["subtitle"],
- target["relative_title_styles"]["sametitle"],
- target["relative_title_styles"]["supertitle"]
- ]
- relative_title_matcher = re.compile(
- "^[" + re.escape("".join(relative_title_chars)) + "]{3,}$"
- )
- title_matcher = re.compile(
- "^[" + re.escape("".join(title_styles)) + "]{3,}$"
- )
- current_title_style = None
- with open(filename, "r", encoding="utf-8") as infile:
- with open(out_filename, "w", encoding="utf-8") as outfile:
- for line in infile.readlines():
- if not relative_title_matcher.match(line):
- if title_matcher.match(line):
- current_title_style = line[0]
- outfile.write(line)
- continue
- line_char = line[0]
- replacement_char = None
- current_title_level = title_styles.index(current_title_style)
- if line_char == target["relative_title_styles"]["subtitle"]:
- if (current_title_level + 1) == len(title_styles):
- raise Exception(
- "Encountered sub-title line style but we can't go " +
- "any lower."
- )
- replacement_char = title_styles[current_title_level + 1]
- elif line_char == target["relative_title_styles"]["sametitle"]:
- replacement_char = title_styles[current_title_level]
- elif line_char == target["relative_title_styles"]["supertitle"]:
- if (current_title_level - 1) < 0:
- raise Exception(
- "Encountered super-title line style but we can't go " +
- "any higher."
- )
- replacement_char = title_styles[current_title_level - 1]
- else:
- raise Exception(
- "Unknown relative line char %s" % (line_char,)
- )
-
- outfile.write(
- line.replace(line_char, replacement_char)
- )
-
-
-
-def rst2html(i, o, stylesheets):
- log("rst2html %s -> %s" % (i, o))
- with open(i, "r", encoding="utf-8") as in_file:
- with open(o, "w", encoding="utf-8") as out_file:
- publish_file(
- source=in_file,
- destination=out_file,
- reader_name="standalone",
- parser_name="restructuredtext",
- writer_name="html",
- settings_overrides={
- "stylesheet_path": stylesheets,
- "syntax_highlight": "short",
- },
- )
-
-
-def addAnchors(path):
- log("add anchors %s" % path)
-
- with open(path, "r", encoding="utf-8") as f:
- lines = f.readlines()
-
- replacement = r'
\n\1'
- with open(path, "w", encoding="utf-8") as f:
- for line in lines:
- line = re.sub(r'()', replacement, line.rstrip())
- line = re.sub(r'(
)', replacement, line.rstrip())
- f.write(line + "\n")
-
-
-def run_through_template(input_files, set_verbose, substitutions):
- args = [
- 'python', script_dir+'/templating/build.py',
- "-o", tmp_dir,
- "-i", "matrix_templates",
- ]
-
- for k, v in substitutions.items():
- args.append("--substitution=%s=%s" % (k, v))
-
- if set_verbose:
- args.insert(2, "-v")
-
- args.extend(input_files)
-
- log("EXEC: %s" % " ".join(args))
- log(" ==== build.py output ==== ")
- subprocess.check_call(args)
-
-"""
-Extract and resolve groups for the given target in the given targets listing.
-Args:
- all_targets (dict): The parsed YAML file containing a list of targets
- target_name (str): The name of the target to extract from the listings.
-Returns:
- dict: Containing "filees" (a list of file paths), "relative_title_styles"
- (a dict of relative style keyword to title character) and "title_styles"
- (a list of characters which represent the global title style to follow,
- with the top section title first, the second section second, and so on.)
-"""
-def get_build_target(all_targets, target_name):
- build_target = {
- "title_styles": [],
- "relative_title_styles": {},
- "files": []
- }
-
- build_target["title_styles"] = all_targets["title_styles"]
- build_target["relative_title_styles"] = all_targets["relative_title_styles"]
- target = all_targets["targets"].get(target_name)
- if not target:
- raise Exception(
- "No target by the name '" + target_name + "' exists in '" +
- targets_listing + "'."
- )
- if not isinstance(target.get("files"), list):
- raise Exception(
- "Found target but 'files' key is not a list."
- )
-
- def get_group(group_id, depth):
- group_name = group_id[len("group:"):]
- group = all_targets.get("groups", {}).get(group_name)
- if not group:
- raise Exception(
- "Tried to find group '%s' but it doesn't exist." % group_name
- )
- if not isinstance(group, list):
- raise Exception(
- "Expected group '%s' to be a list but it isn't." % group_name
- )
- # deep copy so changes to depths don't contaminate multiple uses of this group
- group = copy.deepcopy(group)
- # swap relative depths for absolute ones
- for i, entry in enumerate(group):
- if isinstance(entry, dict):
- group[i] = {
- (rel_depth + depth): v for (rel_depth, v) in entry.items()
- }
- return group
-
- resolved_files = []
- for file_entry in target["files"]:
- # file_entry is a group id
- if isinstance(file_entry, str) and file_entry.startswith("group:"):
- group = get_group(file_entry, 0)
- # The group may be resolved to a list of file entries, in which case
- # we want to extend the array to insert each of them rather than
- # insert the entire list as a single element (which is what append does)
- if isinstance(group, list):
- resolved_files.extend(group)
- else:
- resolved_files.append(group)
- # file_entry is a dict which has more file entries as values
- elif isinstance(file_entry, dict):
- resolved_entry = {}
- for (depth, entry) in file_entry.items():
- if not isinstance(entry, str):
- raise Exception(
- "Double-nested depths are not supported. Entry: %s" % (file_entry,)
- )
- if entry.startswith("group:"):
- resolved_entry[depth] = get_group(entry, depth)
- else:
- # map across without editing (e.g. normal file path)
- resolved_entry[depth] = entry
- resolved_files.append(resolved_entry)
- continue
- # file_entry is just a plain ol' file path
- else:
- resolved_files.append(file_entry)
- build_target["files"] = resolved_files
- return build_target
-
-def log(line):
- print("gendoc: %s" % line)
-
-def logv(line):
- if VERBOSE:
- print("gendoc:V: %s" % line)
-
-
-def cleanup_env():
- shutil.rmtree(tmp_dir)
-
-
-def mkdirp(d) :
- if not os.path.exists(d):
- os.makedirs(d)
-
-
-def main(targets, dest_dir, keep_intermediates, substitutions):
- try:
- mkdirp(dest_dir)
- except Exception as e:
- log("Error creating destination directory '%s': %s" % (dest_dir, str(e)))
- return 1
- try:
- mkdirp(tmp_dir)
- except Exception as e:
- log("Error creating temporary directory '%s': %s" % (tmp_dir, str(e)))
- return 1
-
- with open(os.path.join(spec_dir, "targets.yaml"), "r") as targ_file:
- target_defs = yaml.load(targ_file.read())
-
- if targets == ["all"]:
- targets = target_defs["targets"].keys()
-
- log("Building spec [targets=%s]" % targets)
-
- templated_files = {} # map from target name to templated file
-
- for target_name in targets:
- templated_file = os.path.join(tmp_dir, "templated_%s.rst" % (target_name,))
-
- target = get_build_target(target_defs, target_name)
- build_spec(target=target, out_filename=templated_file)
- templated_files[target_name] = templated_file
-
- # we do all the templating at once, because it's slow
- run_through_template(templated_files.values(), VERBOSE, substitutions)
-
- stylesheets = glob.glob(os.path.join(script_dir, "css", "*.css"))
-
- for target_name, templated_file in templated_files.items():
- target = target_defs["targets"].get(target_name)
- version_label = None
- if target:
- version_label = target.get("version_label")
- if version_label:
- for old, new in substitutions.items():
- version_label = version_label.replace(old, new)
-
- rst_file = os.path.join(tmp_dir, "spec_%s.rst" % (target_name,))
- if version_label:
- d = os.path.join(dest_dir, target_name.split('@')[0])
- if not os.path.exists(d):
- os.mkdir(d)
- html_file = os.path.join(d, "%s.html" % version_label)
- else:
- html_file = os.path.join(dest_dir, "%s.html" % (target_name, ))
-
- fix_relative_titles(
- target=target_defs, filename=templated_file,
- out_filename=rst_file,
- )
- rst2html(rst_file, html_file, stylesheets=stylesheets)
- addAnchors(html_file)
-
- if not keep_intermediates:
- cleanup_env()
-
- return 0
-
-
-def list_targets():
- with open(os.path.join(spec_dir, "targets.yaml"), "r") as targ_file:
- target_defs = yaml.load(targ_file.read())
- targets = target_defs["targets"].keys()
- print("\n".join(targets))
-
-
-def extract_major(s):
- major_version = s
- match = re.match("^(r\d+)(\.\d+)*$", s)
- if match:
- major_version = match.group(1)
- return major_version
-
-
-if __name__ == '__main__':
- parser = ArgumentParser(
- "gendoc.py - Generate the Matrix specification as HTML."
- )
- parser.add_argument(
- "--nodelete", "-n", action="store_true",
- help="Do not delete intermediate files. They will be found in scripts/tmp/"
- )
- parser.add_argument(
- "--target", "-t", action="append",
- help="Specify the build target to build from specification/targets.yaml. " +
- "The value 'all' will build all of the targets therein."
- )
- parser.add_argument(
- "--verbose", "-v", action="store_true",
- help="Turn on verbose mode."
- )
- parser.add_argument(
- "--client_release", "-c", action="store", default="unstable",
- help="The client-server release tag to generate, e.g. r1.2"
- )
- parser.add_argument(
- "--server_release", "-s", action="store", default="unstable",
- help="The server-server release tag to generate, e.g. r1.2"
- )
- parser.add_argument(
- "--appservice_release", "-a", action="store", default="unstable",
- help="The appservice release tag to generate, e.g. r1.2"
- )
- parser.add_argument(
- "--push_gateway_release", "-p", action="store", default="unstable",
- help="The push gateway release tag to generate, e.g. r1.2"
- )
- parser.add_argument(
- "--identity_release", "-i", action="store", default="unstable",
- help="The identity service release tag to generate, e.g. r1.2"
- )
- parser.add_argument(
- "--list_targets", action="store_true",
- help="Do not update the specification. Instead print a list of targets.",
- )
- parser.add_argument(
- "--dest", "-d", default=os.path.join(script_dir, "gen"),
- help="Set destination directory (default: scripts/gen)",
- )
-
- args = parser.parse_args()
- VERBOSE = args.verbose
-
- if args.list_targets:
- list_targets()
- exit(0)
-
- substitutions = {
- "%CLIENT_RELEASE_LABEL%": args.client_release,
- # we hardcode the major versions. This ends up in the example
- # API URLs. When we have released a new major version, we'll
- # have to bump them.
- "%CLIENT_MAJOR_VERSION%": "r0",
- "%SERVER_RELEASE_LABEL%": args.server_release,
- "%APPSERVICE_RELEASE_LABEL%": args.appservice_release,
- "%IDENTITY_RELEASE_LABEL%": args.identity_release,
- "%PUSH_GATEWAY_RELEASE_LABEL%": args.push_gateway_release,
- }
-
- exit (main(args.target or ["all"], args.dest, args.nodelete, substitutions))
diff --git a/scripts/generate-matrix-org-assets b/scripts/generate-matrix-org-assets
index 76032850ab3..6b9881d1d36 100755
--- a/scripts/generate-matrix-org-assets
+++ b/scripts/generate-matrix-org-assets
@@ -8,19 +8,8 @@ cd `dirname $0`/..
mkdir -p assets
-if [ "$CIRCLECI" != "true" ]
-then
- # generate specification/proposals.rst
- ./scripts/proposals.py
-fi
-
-# generate the spec docs
-./scripts/gendoc.py -d assets/spec
-
# and the swagger
./scripts/dump-swagger.py -o assets/spec/client_server/unstable.json
-# create a tarball of the assets. Exclude the spec index for now, as
-# we want to leave it pointing at the release versions of the specs.
-# (XXX: how to maintain this?)
-tar -czf assets.tar.gz --exclude="assets/spec/index.html" assets
+# create a tarball of the assets.
+tar -czf assets.tar.gz assets
diff --git a/api/package.json b/scripts/package.json
similarity index 100%
rename from api/package.json
rename to scripts/package.json
diff --git a/scripts/proposals.js b/scripts/proposals.js
new file mode 100644
index 00000000000..af78ac91e43
--- /dev/null
+++ b/scripts/proposals.js
@@ -0,0 +1,219 @@
+"use strict";
+
+/**
+ * This Node script fetches MSC proposals and stores them in /data/msc,
+ * so they can be used by a Hugo template to render summary tables of them
+ * in the specification.
+ *
+ * In detail, it:
+ * - fetches all GitHub issues from matrix-doc that have the `proposal` label
+ * - groups them by their state in the MSC process
+ * - does some light massaging of them so it's easier for the Hugo template to work with them
+ * - store them at /data/msc
+ */
+
+// built-in modules
+const path = require('path');
+const fs = require('fs');
+
+// third-party modules
+const fetch = require('node-fetch');
+
+// We will write proposals into the /data/msc directory
+const outputDir = path.join(__dirname, "../data/msc");
+
+/**
+ * This defines the different proposal lifecycle states.
+ * Each state has:
+ * - `label`: a GitHub label used to identify issues in this state
+ * - `title`: used for things like headings in renderings of the proposals
+ */
+const states = [
+ {
+ label: "proposal-in-review",
+ title: "Proposal In Review"
+ },
+ {
+ label: "proposed-final-comment-period",
+ title: "Proposed Final Comment Period"
+ },
+ {
+ label: "final-comment-period",
+ title: "Final Comment Period"
+ },
+ {
+ label: "finished-final-comment-period",
+ title: "Finished Final Comment Period"
+ },
+ {
+ label: "spec-pr-missing",
+ title: "Spec PR Missing"
+ },
+ {
+ label: "spec-pr-in-review",
+ title: "Spec PR In Review"
+ },
+ {
+ label: "merged",
+ title: "Merged"
+ },
+ {
+ label: "proposal-postoned",
+ title: "Postponed"
+ },
+ {
+ label: "abandoned",
+ title: "Abandoned"
+ },
+ {
+ label: "obsolete",
+ title: "Obsolete"
+ }
+];
+
+let issues = [];
+
+/**
+ * Fetch all the MSC proposals from GitHub.
+ *
+ * GitHub only lets us fetch 100 items at a time, and it gives us a `link`
+ * response header containing the URL for the next batch.
+ * So we will keep fetching until the response doesn't contain the "next" link.
+ */
+async function getIssues() {
+
+ /**
+ * A pretty ugly function to get us the "next" link in the header if there
+ * was one, or `null` otherwise.
+ */
+ function getNextLink(header) {
+ const links = header.split(",");
+ for (const link of links) {
+ const linkPieces = link.split(";");
+ if (linkPieces[1] == ` rel=\"next\"`) {
+ const next = linkPieces[0].trim();
+ return next.substring(1, next.length-1);
+ }
+ }
+ return null;
+ }
+
+ let pageLink = "https://api.github.com/repos/matrix-org/matrix-doc/issues?state=all&labels=proposal&per_page=100";
+ while (pageLink) {
+ const response = await fetch(pageLink);
+ const issuesForPage = await response.json();
+ issues = issues.concat(issuesForPage);
+ const linkHeader = response.headers.get("link");
+ pageLink = getNextLink(linkHeader);
+ }
+}
+
+getIssues().then(processIssues);
+
+/**
+ * Rather than just store the complete issue, we'll extract
+ * only the pieces we need.
+ * We'll also do some transformation of the issues, jsut because
+ * it's easier to do in JS than in the template.
+ */
+function getProposalFromIssue(issue) {
+
+ /**
+ * A helper function to fetch the contents of special
+ * directives in the issue body.
+ * Looks for a directive in the format:
+ * `^${directiveName}: (.+?)$`, returning the matched
+ * part or null if the directive wasn't found.
+ */
+ function getDirective(directiveName, issue) {
+ const re = new RegExp(`^${directiveName}: (.+?)$`, "m");
+ const found = issue.body.match(re);
+ return found? found[1]: null;
+ }
+
+ function getDocumentation(issue) {
+ const found = getDirective("Documentation", issue);
+ if (found) {
+ return found.split(",").map(a => a.trim());
+ } else {
+ return [];
+ }
+ }
+
+ function getAuthors(issue) {
+ const found = getDirective("Author", issue);
+ if (found) {
+ return found.split(",").map(a => a.trim().substr(1));
+ } else {
+ return [`${issue.user.login}`];
+ }
+ }
+
+ function getShepherd(issue) {
+ const found = getDirective("Shepherd", issue);
+ if (found) {
+ return found.substr(1);
+ } else {
+ return null;
+ }
+ }
+
+ return {
+ number: issue.number,
+ url: issue.html_url,
+ title: issue.title,
+ created_at: issue.created_at.substr(0, 10),
+ updated_at: issue.updated_at.substr(0, 10),
+ authors: getAuthors(issue),
+ shepherd: getShepherd(issue),
+ documentation: getDocumentation(issue)
+ }
+}
+
+/**
+ * Returns the intersection of two arrays.
+ */
+function intersection(array1, array2) {
+ return array1.filter(i => array2.includes(i));
+}
+
+/**
+ * Given all the GitHub issues with a "proposal" label:
+ * - group issues by the state they are in, and for each group:
+ * - extract the bits we need from each issue
+ * - write the result under /data/msc
+ */
+function processIssues() {
+ if (!fs.existsSync(outputDir)){
+ fs.mkdirSync(outputDir);
+ }
+ const output = [];
+ // make a group of "work in progress" proposals,
+ // which are identified by not having any of the state labels
+ const stateLabels = states.map(s => s.label);
+ const worksInProgress = issues.filter(issue => {
+ const labelsForIssue = issue.labels.map(l => l.name);
+ return intersection(labelsForIssue, stateLabels).length === 0;
+ });
+ output.push({
+ title: "Work In Progress",
+ label: "work-in-progress",
+ proposals: worksInProgress.map(issue => getProposalFromIssue(issue))
+ });
+ // for each defined state
+ for (const state of states) {
+ // get the set of issues for that state
+ const issuesForState = issues.filter(msc => {
+ return msc.labels.some(l => l.name === state.label);
+ });
+ // store it in /data
+ output.push({
+ title: state.title,
+ label: state.label,
+ proposals: issuesForState.map(issue => getProposalFromIssue(issue))
+ });
+ }
+ const outputData = JSON.stringify(output, null, '\t');
+ const outputFile = path.join(outputDir, `proposals.json`);
+ fs.writeFileSync(outputFile, outputData);
+}
diff --git a/scripts/proposals.py b/scripts/proposals.py
deleted file mode 100755
index faa10a838a7..00000000000
--- a/scripts/proposals.py
+++ /dev/null
@@ -1,218 +0,0 @@
-#!/usr/bin/env python
-#
-# proposals.py: generate an RST file (proposals.rst) from queries to github.com/matrix.org/matrix-doc/issues.
-
-import requests
-import re
-from datetime import datetime
-
-# a list of the labels we care about
-LABELS_LIST=[
- 'proposal-in-review',
- 'proposed-final-comment-period',
- 'final-comment-period',
- 'finished-final-comment-period',
- 'spec-pr-missing',
- 'spec-pr-in-review',
- 'merged',
- 'proposal-postponed',
- 'abandoned',
- 'obsolete',
-]
-
-
-authors = set()
-prs = set()
-
-def getpage(url):
- """Request the given URL, and extract the pagecount from the response headers
-
- Args:
- url (str): URL to fetch
-
- Returns:
- Tuple[int, list]: number of pages, and the list of items on this page
- """
- resp = requests.get(url)
-
- pagecount = 1
- for link in resp.links.values():
- if link['rel'] == 'last':
- # we extract the pagecount from the `page` param of the last url
- # in the response, eg
- # 'https://api.github.com/repositories/24998719/issues?state=all&labels=proposal&page=10'
- pagecount = int(re.search('page=(\d+)', link['url']).group(1))
-
- val = resp.json()
- if not isinstance(val, list):
- print(val) # Just dump the raw (likely error) response to the log
- raise Exception("Error calling %s" % url)
- return (pagecount, val)
-
-def getbylabel(label):
- """Fetch all the issues with a given label
-
- Args:
- label (str): label to fetch
-
- Returns:
- Iterator[dict]: an iterator over the issue list.
- """
- urlbase = 'https://api.github.com/repos/matrix-org/matrix-doc/issues?state=all&labels=' + label + '&page='
- page = 1
- while True:
- (pagecount, results) = getpage(urlbase + str(page))
- for i in results:
- yield i
- page += 1
- if page > pagecount:
- return
-
-def print_issue_list(text_file, label, issues):
- text_file.write(label + "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n")
-
- if (len(issues) == 0):
- text_file.write("No proposals.\n\n")
- return
-
- text_file.write(".. list-table::\n :header-rows: 1\n :widths: auto\n :stub-columns: 1\n\n")
- text_file.write(" * - MSC\n")
- text_file.write(" - Proposal Title\n")
- text_file.write(" - Creation Date\n")
- text_file.write(" - Update Date\n")
- text_file.write(" - Documentation\n")
- text_file.write(" - Author\n")
- text_file.write(" - Shepherd\n")
- text_file.write(" - PRs\n")
-
- for item in issues:
- # set the created date, find local field, otherwise Github
- body = str(item['body'])
- created = re.search('^Date: (.+?)\n', body, flags=re.MULTILINE)
- if created is not None:
- created = created.group(1).strip()
- try:
- created = datetime.strptime(created, "%d/%m/%Y")
- created = created.strftime('%Y-%m-%d')
- except:
- pass
- try:
- created = datetime.strptime(created, "%Y-%m-%d")
- created = created.strftime('%Y-%m-%d')
- except:
- pass
- else :
- created = datetime.strptime(item['created_at'], "%Y-%m-%dT%XZ")
- created = created.strftime('%Y-%m-%d')
- item['created'] = created
-
- issues_to_print = sorted(issues, key=lambda issue_sort: issue_sort["created"])
-
- for item in issues_to_print:
- # MSC number
- text_file.write(" * - `MSC" + str(item['number']) + " <" + item['html_url'] + ">`_\n")
-
- # title from Github issue
- text_file.write(" - " + item['title'] + "\n")
-
- # created date
- text_file.write(" - " + item['created'] + "\n")
-
- # last updated, purely Github
- updated = datetime.strptime(item['updated_at'], "%Y-%m-%dT%XZ")
- text_file.write(" - " + updated.strftime('%Y-%m-%d') + "\n")
-
- # list of document links (urls comma-separated)
- maindoc = re.search('^Documentation: (.+?)$', str(item['body']), flags=re.MULTILINE)
- if maindoc is not None:
- maindoc = maindoc.group(1)
- doc_list_formatted = ["`" + str(item['number']) + "-" + str(i) + " <" + x.strip() + ">`_" for i, x in enumerate(maindoc.split(','),1)]
- text_file.write(" - " + ', '.join(doc_list_formatted))
- else:
- text_file.write(" - ")
- text_file.write("\n")
-
- # author list, if missing just use Github issue creator
- author = re.search('^Author: (.+?)$', str(item['body']), flags=re.MULTILINE)
- if author is not None:
- author_list_formatted = set()
- author_list = author.group(1)
- for a in author_list.split(","):
- authors.add(a.strip())
- author_list_formatted.add("`" + str(a.strip()) + "`_")
- text_file.write(" - " + ', '.join(author_list_formatted))
- else:
- author = "@" + item['user']['login']
- authors.add(author)
- text_file.write(" - `" + str(author) + "`_")
- text_file.write("\n")
-
- # shepherd (currently only one)
- shepherd = re.search('Shepherd: (.+?)\n', str(item['body']))
- if shepherd is not None:
- authors.add(shepherd.group(1).strip())
- shepherd = "`" + shepherd.group(1).strip() + "`_"
- text_file.write(" - " + str(shepherd) + "\n")
-
- # PRs
- try:
- pr_list = re.search('PRs: (.+?)$', str(item['body']))
- if pr_list is not None:
- pr_list_formatted = set()
- pr_list = pr_list.group(1)
- for p in pr_list.split(","):
- if re.match(r"#\d", p.strip()):
- prs.add(p.strip())
- pr_list_formatted.add("`PR" + str(p.strip()) + "`_")
- elif re.match(r"https://github.com/matrix-org/matrix-doc/pulls/\d", p.strip()):
- pr = "#" + p.strip().replace('https://github.com/matrix-org/matrix-doc/pulls/', '')
- prs.add(pr)
- pr_list_formatted.add("`PR" + str(pr) + "`_")
- else:
- raise RuntimeWarning
- text_file.write(" - " + ', '.join(pr_list_formatted))
- text_file.write("\n")
- else:
- text_file.write(" - \n")
- except:
- print("exception parsing PRs for MSC" + str(item['number']))
- text_file.write(" - \n")
-
- text_file.write("\n\n\n")
-
-
-# first get all of the issues, filtering by label
-issues = {n: [] for n in LABELS_LIST}
-# use the magic 'None' key for a proposal in progress
-issues[None] = []
-
-for prop in getbylabel('proposal'):
- print("%s: %s" % (prop['number'], [l['name'] for l in prop['labels']]))
- found_label = False
- for label in prop['labels']:
- label_name = label['name']
- if label_name in issues:
- issues[label_name].append(prop)
- found_label = True
-
- # if it doesn't have any other label, assume it's work-in-progress
- if not found_label:
- issues[None].append(prop)
-
-text_file = open("specification/proposals.rst", "w")
-
-text_file.write("Tables of Tracked Proposals\n---------------------------\n\n")
-
-print_issue_list(text_file, "", issues[None])
-for label in LABELS_LIST:
- print_issue_list(text_file, label, issues[label])
-
-text_file.write("\n")
-
-for author in authors:
- text_file.write("\n.. _" + author + ": https://github.com/" + author[1:])
-
-for pr in prs:
- text_file.write("\n.. _PR" + pr + ": https://github.com/matrix-org/matrix-doc/pull/" + pr.replace('#', ''))
-
-text_file.close()
diff --git a/scripts/speculator/main.go b/scripts/speculator/main.go
index 12ec2aec2ba..e09846a855c 100644
--- a/scripts/speculator/main.go
+++ b/scripts/speculator/main.go
@@ -337,7 +337,7 @@ func (s *server) serveSpec(w http.ResponseWriter, req *http.Request) {
log.Printf("Serving pr %s (%s)\n", branchName, sha)
} else if strings.ToLower(branchName) == "head" ||
branchName == "master" ||
- strings.HasPrefix(branchName, "drafts/") {
+ strings.HasPrefix(branchName, "attic/drafts/") {
branchSHA, err := s.lookupBranch(branchName)
if err != nil {
writeError(w, 400, err)
diff --git a/scripts/templating/build.py b/scripts/templating/build.py
old mode 100755
new mode 100644
diff --git a/scripts/test-and-build.sh b/scripts/test-and-build.sh
deleted file mode 100755
index f45e2da612e..00000000000
--- a/scripts/test-and-build.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#! /bin/bash
-
-set -ex
-
-cd `dirname $0`/..
-
-virtualenv -p python3 env
-. env/bin/activate
-
-# Print out the python versions for debugging purposes
-python --version
-pip --version
-
-pip install -r scripts/requirements.txt
-
-# do sanity checks on the examples and swagger
-(cd event-schemas/ && ./check_examples.py)
-(cd api && ./check_examples.py)
-(cd api && npm install && node validator.js -s "client-server")
-
-: ${GOPATH:=${WORKSPACE}/.gopath}
-mkdir -p "${GOPATH}"
-export GOPATH
-go get github.com/hashicorp/golang-lru
-go get gopkg.in/fsnotify/fsnotify.v1
-
-# make sure that the scripts build
-(cd scripts/continuserv && go build)
-(cd scripts/speculator && go build)
-
-# build the spec for matrix.org.
-# (we don't actually use it on travis, but it's still useful to check we
-# can build it. On Buildkite, this is then used to deploy to matrix.org).
-./scripts/generate-matrix-org-assets
diff --git a/api/validator.js b/scripts/validator.js
similarity index 100%
rename from api/validator.js
rename to scripts/validator.js
diff --git a/specification/appendices.rst b/specification/appendices.rst
deleted file mode 100644
index 4a106a3a5e3..00000000000
--- a/specification/appendices.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-.. Copyright 2015 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Appendices
-==========
-
-.. contents:: Table of Contents
-.. sectnum::
diff --git a/specification/appendices/base64.rst b/specification/appendices/base64.rst
deleted file mode 100644
index a918c2f9c29..00000000000
--- a/specification/appendices/base64.rst
+++ /dev/null
@@ -1,57 +0,0 @@
-.. Copyright 2017 Vector Creations Limited
-..
-.. 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.
-
-Unpadded Base64
----------------
-
-*Unpadded* Base64 refers to 'standard' Base64 encoding as defined in `RFC
-4648`_, without "=" padding. Specifically, where RFC 4648 requires that encoded
-data be padded to a multiple of four characters using ``=`` characters,
-unpadded Base64 omits this padding.
-
-For reference, RFC 4648 uses the following alphabet for Base 64::
-
- Value Encoding Value Encoding Value Encoding Value Encoding
- 0 A 17 R 34 i 51 z
- 1 B 18 S 35 j 52 0
- 2 C 19 T 36 k 53 1
- 3 D 20 U 37 l 54 2
- 4 E 21 V 38 m 55 3
- 5 F 22 W 39 n 56 4
- 6 G 23 X 40 o 57 5
- 7 H 24 Y 41 p 58 6
- 8 I 25 Z 42 q 59 7
- 9 J 26 a 43 r 60 8
- 10 K 27 b 44 s 61 9
- 11 L 28 c 45 t 62 +
- 12 M 29 d 46 u 63 /
- 13 N 30 e 47 v
- 14 O 31 f 48 w
- 15 P 32 g 49 x
- 16 Q 33 h 50 y
-
-Examples of strings encoded using unpadded Base64::
-
- UNPADDED_BASE64("") = ""
- UNPADDED_BASE64("f") = "Zg"
- UNPADDED_BASE64("fo") = "Zm8"
- UNPADDED_BASE64("foo") = "Zm9v"
- UNPADDED_BASE64("foob") = "Zm9vYg"
- UNPADDED_BASE64("fooba") = "Zm9vYmE"
- UNPADDED_BASE64("foobar") = "Zm9vYmFy"
-
-When decoding Base64, implementations SHOULD accept input with or without
-padding characters wherever possible, to ensure maximum interoperability.
-
-.. _`RFC 4648`: https://tools.ietf.org/html/rfc4648
diff --git a/specification/appendices/identifier_grammar.rst b/specification/appendices/identifier_grammar.rst
deleted file mode 100644
index 95c22fb1934..00000000000
--- a/specification/appendices/identifier_grammar.rst
+++ /dev/null
@@ -1,408 +0,0 @@
-.. Copyright 2016 Openmarket Ltd.
-.. Copyright 2017, 2018 New Vector Ltd.
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. 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.
-
-Identifier Grammar
-------------------
-
-Some identifiers are specific to given room versions, please refer to the
-`room versions specification`_ for more information.
-
-.. _`room versions specification`: index.html#room-versions
-
-
-Server Name
-~~~~~~~~~~~
-
-A homeserver is uniquely identified by its server name. This value is used in a
-number of identifiers, as described below.
-
-The server name represents the address at which the homeserver in question can
-be reached by other homeservers. All valid server names are included by the
-following grammar::
-
- server_name = hostname [ ":" port ]
-
- port = 1*5DIGIT
-
- hostname = IPv4address / "[" IPv6address "]" / dns-name
-
- IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
-
- IPv6address = 2*45IPv6char
-
- IPv6char = DIGIT / %x41-46 / %x61-66 / ":" / "."
- ; 0-9, A-F, a-f, :, .
-
- dns-name = 1*255dns-char
-
- dns-char = DIGIT / ALPHA / "-" / "."
-
-— in other words, the server name is the hostname, followed by an optional
-numeric port specifier. The hostname may be a dotted-quad IPv4 address literal,
-an IPv6 address literal surrounded with square brackets, or a DNS name.
-
-IPv4 literals must be a sequence of four decimal numbers in the
-range 0 to 255, separated by ``.``. IPv6 literals must be as specified by
-`RFC3513, section 2.2 `_.
-
-DNS names for use with Matrix should follow the conventional restrictions for
-internet hostnames: they should consist of a series of labels separated by
-``.``, where each label consists of the alphanumeric characters or hyphens.
-
-Examples of valid server names are:
-
-* ``matrix.org``
-* ``matrix.org:8888``
-* ``1.2.3.4`` (IPv4 literal)
-* ``1.2.3.4:1234`` (IPv4 literal with explicit port)
-* ``[1234:5678::abcd]`` (IPv6 literal)
-* ``[1234:5678::abcd]:5678`` (IPv6 literal with explicit port)
-
-.. Note::
-
- This grammar is based on the standard for internet host names, as specified
- by `RFC1123, section 2.1 `_,
- with an extension for IPv6 literals.
-
-Server names must be treated case-sensitively: in other words,
-``@user:matrix.org`` is a different person from ``@user:MATRIX.ORG``.
-
-Some recommendations for a choice of server name follow:
-
-* The length of the complete server name should not exceed 230 characters.
-* Server names should not use upper-case characters.
-
-Common Identifier Format
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-The Matrix protocol uses a common format to assign unique identifiers to a
-number of entities, including users, events and rooms. Each identifier takes
-the form::
-
- &string
-
-where ``&`` represents a 'sigil' character; ``string`` is the string which makes
-up the identifier.
-
-The sigil characters are as follows:
-
-* ``@``: User ID
-* ``!``: Room ID
-* ``$``: Event ID
-* ``+``: Group ID
-* ``#``: Room alias
-
-User IDs, group IDs, room IDs, room aliases, and sometimes event IDs take the form::
-
- &localpart:domain
-
-where ``domain`` is the `server name`_ of the homeserver which allocated the
-identifier, and ``localpart`` is an identifier allocated by that homeserver.
-
-The precise grammar defining the allowable format of an identifier depends on
-the type of identifier. For example, event IDs can sometimes be represented with
-a ``domain`` component under some conditions - see the `Event IDs <#room-ids-and-event-ids>`_
-section below for more information.
-
-User Identifiers
-++++++++++++++++
-
-Users within Matrix are uniquely identified by their Matrix user ID. The user
-ID is namespaced to the homeserver which allocated the account and has the
-form::
-
- @localpart:domain
-
-The ``localpart`` of a user ID is an opaque identifier for that user. It MUST
-NOT be empty, and MUST contain only the characters ``a-z``, ``0-9``, ``.``,
-``_``, ``=``, ``-``, and ``/``.
-
-The ``domain`` of a user ID is the `server name`_ of the homeserver which
-allocated the account.
-
-The length of a user ID, including the ``@`` sigil and the domain, MUST NOT
-exceed 255 characters.
-
-The complete grammar for a legal user ID is::
-
- user_id = "@" user_id_localpart ":" server_name
- user_id_localpart = 1*user_id_char
- user_id_char = DIGIT
- / %x61-7A ; a-z
- / "-" / "." / "=" / "_" / "/"
-
-.. admonition:: Rationale
-
- A number of factors were considered when defining the allowable characters
- for a user ID.
-
- Firstly, we chose to exclude characters outside the basic US-ASCII character
- set. User IDs are primarily intended for use as an identifier at the protocol
- level, and their use as a human-readable handle is of secondary
- benefit. Furthermore, they are useful as a last-resort differentiator between
- users with similar display names. Allowing the full unicode character set
- would make very difficult for a human to distinguish two similar user IDs. The
- limited character set used has the advantage that even a user unfamiliar with
- the Latin alphabet should be able to distinguish similar user IDs manually, if
- somewhat laboriously.
-
- We chose to disallow upper-case characters because we do not consider it
- valid to have two user IDs which differ only in case: indeed it should be
- possible to reach ``@user:matrix.org`` as ``@USER:matrix.org``. However,
- user IDs are necessarily used in a number of situations which are inherently
- case-sensitive (notably in the ``state_key`` of ``m.room.member``
- events). Forbidding upper-case characters (and requiring homeservers to
- downcase usernames when creating user IDs for new users) is a relatively simple
- way to ensure that ``@USER:matrix.org`` cannot refer to a different user to
- ``@user:matrix.org``.
-
- Finally, we decided to restrict the allowable punctuation to a very basic set
- to reduce the possibility of conflicts with special characters in various
- situations. For example, "*" is used as a wildcard in some APIs (notably the
- filter API), so it cannot be a legal user ID character.
-
- The length restriction is derived from the limit on the length of the
- ``sender`` key on events; since the user ID appears in every event sent by the
- user, it is limited to ensure that the user ID does not dominate over the actual
- content of the events.
-
-Matrix user IDs are sometimes informally referred to as MXIDs.
-
-Historical User IDs
-<<<<<<<<<<<<<<<<<<<
-
-Older versions of this specification were more tolerant of the characters
-permitted in user ID localparts. There are currently active users whose user
-IDs do not conform to the permitted character set, and a number of rooms whose
-history includes events with a ``sender`` which does not conform. In order to
-handle these rooms successfully, clients and servers MUST accept user IDs with
-localparts from the expanded character set::
-
- extended_user_id_char = %x21-39 / %x3B-7E ; all ascii printing chars except :
-
-Mapping from other character sets
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-In certain circumstances it will be desirable to map from a wider character set
-onto the limited character set allowed in a user ID localpart. Examples include
-a homeserver creating a user ID for a new user based on the username passed to
-``/register``, or a bridge mapping user ids from another protocol.
-
-.. TODO-spec
-
- We need to better define the mechanism by which homeservers can allow users
- to have non-Latin login credentials. The general idea is for clients to pass
- the non-Latin in the ``username`` field to ``/register`` and ``/login``, and
- the HS then maps it onto the MXID space when turning it into the
- fully-qualified ``user_id`` which is returned to the client and used in
- events.
-
-Implementations are free to do this mapping however they choose. Since the user
-ID is opaque except to the implementation which created it, the only
-requirement is that the implementation can perform the mapping
-consistently. However, we suggest the following algorithm:
-
-1. Encode character strings as UTF-8.
-
-2. Convert the bytes ``A-Z`` to lower-case.
-
- * In the case where a bridge must be able to distinguish two different users
- with ids which differ only by case, escape upper-case characters by
- prefixing with ``_`` before downcasing. For example, ``A`` becomes
- ``_a``. Escape a real ``_`` with a second ``_``.
-
-3. Encode any remaining bytes outside the allowed character set, as well as
- ``=``, as their hexadecimal value, prefixed with ``=``. For example, ``#``
- becomes ``=23``; ``á`` becomes ``=c3=a1``.
-
-.. admonition:: Rationale
-
- The suggested mapping is an attempt to preserve human-readability of simple
- ASCII identifiers (unlike, for example, base-32), whilst still allowing
- representation of *any* character (unlike punycode, which provides no way to
- encode ASCII punctuation).
-
-
-Room IDs and Event IDs
-++++++++++++++++++++++
-
-A room has exactly one room ID. A room ID has the format::
-
- !opaque_id:domain
-
-An event has exactly one event ID. The format of an event ID depends upon the
-`room version specification `_.
-
-The ``domain`` of a room ID is the `server name`_ of the homeserver which
-created the room/event. The domain is used only for namespacing to avoid the
-risk of clashes of identifiers between different homeservers. There is no
-implication that the room or event in question is still available at the
-corresponding homeserver.
-
-Event IDs and Room IDs are case-sensitive. They are not meant to be human
-readable. They are intended to be treated as fully opaque strings by clients.
-
-.. TODO-spec
- What is the grammar for the opaque part? https://matrix.org/jira/browse/SPEC-389
-
-
-Group Identifiers
-+++++++++++++++++
-
-Groups within Matrix are uniquely identified by their group ID. The group
-ID is namespaced to the group server which hosts this group and has the
-form::
-
- +localpart:domain
-
-The ``localpart`` of a group ID is an opaque identifier for that group. It MUST
-NOT be empty, and MUST contain only the characters ``a-z``, ``0-9``, ``.``,
-``_``, ``=``, ``-``, and ``/``.
-
-The ``domain`` of a group ID is the `server name`_ of the group server which
-hosts this group.
-
-The length of a group ID, including the ``+`` sigil and the domain, MUST NOT
-exceed 255 characters.
-
-The complete grammar for a legal group ID is::
-
- group_id = "+" group_id_localpart ":" server_name
- group_id_localpart = 1*group_id_char
- group_id_char = DIGIT
- / %x61-7A ; a-z
- / "-" / "." / "=" / "_" / "/"
-
-
-Room Aliases
-++++++++++++
-
-A room may have zero or more aliases. A room alias has the format::
-
- #room_alias:domain
-
-The ``domain`` of a room alias is the `server name`_ of the homeserver which
-created the alias. Other servers may contact this homeserver to look up the
-alias.
-
-Room aliases MUST NOT exceed 255 bytes (including the ``#`` sigil and the
-domain).
-
-.. TODO-spec
- - Need to specify precise grammar for Room Aliases. https://matrix.org/jira/browse/SPEC-391
-
-matrix.to navigation
-++++++++++++++++++++
-
-.. NOTE::
- This namespacing is in place pending a ``matrix://`` (or similar) URI scheme.
- This is **not** meant to be interpreted as an available web service - see
- below for more details.
-
-Rooms, users, aliases, and groups may be represented as a "matrix.to" URI.
-This URI can be used to reference particular objects in a given context, such
-as mentioning a user in a message or linking someone to a particular point
-in the room's history (a permalink).
-
-A matrix.to URI has the following format, based upon the specification defined
-in RFC 3986:
-
- https://matrix.to/#//?
-
-The identifier may be a room ID, room alias, user ID, or group ID. The extra
-parameter is only used in the case of permalinks where an event ID is referenced.
-The matrix.to URI, when referenced, must always start with ``https://matrix.to/#/``
-followed by the identifier.
-
-The ```` and the preceeding question mark are optional and
-only apply in certain circumstances, documented below.
-
-Clients should not rely on matrix.to URIs falling back to a web server if accessed
-and instead should perform some sort of action within the client. For example, if
-the user were to click on a matrix.to URI for a room alias, the client may open
-a view for the user to participate in the room.
-
-The components of the matrix.to URI (```` and ````)
-are to be percent-encoded as per RFC 3986.
-
-Examples of matrix.to URIs are:
-
-* Room alias: ``https://matrix.to/#/%23somewhere%3Aexample.org``
-* Room: ``https://matrix.to/#/!somewhere%3Aexample.org``
-* Permalink by room: ``https://matrix.to/#/!somewhere%3Aexample.org/%24event%3Aexample.org``
-* Permalink by room alias: ``https://matrix.to/#/%23somewhere:example.org/%24event%3Aexample.org``
-* User: ``https://matrix.to/#/%40alice%3Aexample.org``
-* Group: ``https://matrix.to/#/%2Bexample%3Aexample.org``
-
-.. Note::
- Historically, clients have not produced URIs which are fully encoded. Clients should
- try to interpret these cases to the best of their ability. For example, an unencoded
- room alias should still work within the client if possible.
-
-.. Note::
- Clients should be aware that decoding a matrix.to URI may result in extra slashes
- appearing due to some `room versions `_. These slashes
- should normally be encoded when producing matrix.to URIs, however.
-
-Routing
-<<<<<<<
-
-Room IDs are not routable on their own as there is no reliable domain to send requests
-to. This is partially mitigated with the addition of a ``via`` argument on a matrix.to
-URI, however the problem of routability is still present. Clients should do their best
-to route Room IDs to where they need to go, however they should also be aware of
-`issue #1579 `_.
-
-A room (or room permalink) which isn't using a room alias should supply at least one
-server using ``via`` in the ````, like so:
-``https://matrix.to/!somewhere%3Aexample.org?via=example.org&via=alt.example.org``. The
-parameter can be supplied multiple times to specify multiple servers to try.
-
-The values of ``via`` are intended to be passed along as the ``server_name`` parameters
-on the Client Server ``/join`` API.
-
-When generating room links and permalinks, the application should pick servers which
-have a high probability of being in the room in the distant future. How these servers
-are picked is left as an implementation detail, however the current recommendation is
-to pick 3 unique servers based on the following criteria:
-
-* The first server should be the server of the highest power level user in the room,
- provided they are at least power level 50. If no user meets this criteria, pick the
- most popular server in the room (most joined users). The rationale for not picking
- users with power levels under 50 is that they are unlikely to be around into the
- distant future while higher ranking users (and therefore servers) are less likely
- to give up their power and move somewhere else. Most rooms in the public federation
- have a power level 100 user and have not deviated from the default structure where
- power level 50 users have moderator-style privileges.
-
-* The second server should be the next highest server by population, or the first
- highest by population if the first server was based on a user's power level. The
- rationale for picking popular servers is that the server is unlikely to be removed
- as the room naturally grows in membership due to that server joining users. The
- server could be refused participation in the future due to server ACLs or similar,
- however the chance of that happening to a server which is organically joining the
- room is unlikely.
-
-* The third server should be the next highest server by population.
-
-* Servers which are blocked due to server ACLs should never be chosen.
-
-* Servers which are IP addresses should never be chosen. Servers which use a domain
- name are less likely to be unroutable in the future whereas IP addresses cannot be
- pointed to a different location and therefore higher risk options.
-
-* All 3 servers should be unique from each other. If the room does not have enough users
- to supply 3 servers, the application should only specify the servers it can. For example,
- a room with only 2 users in it would result in maximum 2 ``via`` parameters.
diff --git a/specification/appendices/signing_json.rst b/specification/appendices/signing_json.rst
deleted file mode 100644
index fbeb0010179..00000000000
--- a/specification/appendices/signing_json.rst
+++ /dev/null
@@ -1,327 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Signing JSON
-------------
-
-Various points in the Matrix specification require JSON objects to be
-cryptographically signed. This requires us to encode the JSON as a binary
-string. Unfortunately the same JSON can be encoded in different ways by
-changing how much white space is used or by changing the order of keys within
-objects.
-
-Signing an object therefore requires it to be encoded as a sequence of bytes
-using `Canonical JSON`_, computing the signature for that sequence and then
-adding the signature to the original JSON object.
-
-Canonical JSON
-~~~~~~~~~~~~~~
-
-We define the canonical JSON encoding for a value to be the shortest UTF-8 JSON
-encoding with dictionary keys lexicographically sorted by unicode codepoint.
-Numbers in the JSON must be integers in the range ``[-(2**53)+1, (2**53)-1]``.
-
-We pick UTF-8 as the encoding as it should be available to all platforms and
-JSON received from the network is likely to be already encoded using UTF-8.
-We sort the keys to give a consistent ordering. We force integers to be in the
-range where they can be accurately represented using IEEE double precision
-floating point numbers since a number of JSON libraries represent all numbers
-using this representation.
-
-.. WARNING::
- Events in room versions 1, 2, 3, 4, and 5 might not be fully compliant with
- these restrictions. Servers SHOULD be capable of handling JSON which is considered
- invalid by these restrictions where possible.
-
- The most notable consideration is that integers might not be in the range
- specified above.
-
-.. Note::
- Float values are not permitted by this encoding.
-
-.. code:: python
-
- import json
-
- def canonical_json(value):
- return json.dumps(
- value,
- # Encode code-points outside of ASCII as UTF-8 rather than \u escapes
- ensure_ascii=False,
- # Remove unnecessary white space.
- separators=(',',':'),
- # Sort the keys of dictionaries.
- sort_keys=True,
- # Encode the resulting unicode as UTF-8 bytes.
- ).encode("UTF-8")
-
-Grammar
-+++++++
-
-Adapted from the grammar in http://tools.ietf.org/html/rfc7159 removing
-insignificant whitespace, fractions, exponents and redundant character escapes.
-
-.. code::
-
- value = false / null / true / object / array / number / string
- false = %x66.61.6c.73.65
- null = %x6e.75.6c.6c
- true = %x74.72.75.65
- object = %x7B [ member *( %x2C member ) ] %7D
- member = string %x3A value
- array = %x5B [ value *( %x2C value ) ] %5B
- number = [ %x2D ] int
- int = %x30 / ( %x31-39 *digit )
- digit = %x30-39
- string = %x22 *char %x22
- char = unescaped / %x5C escaped
- unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
- escaped = %x22 ; " quotation mark U+0022
- / %x5C ; \ reverse solidus U+005C
- / %x62 ; b backspace U+0008
- / %x66 ; f form feed U+000C
- / %x6E ; n line feed U+000A
- / %x72 ; r carriage return U+000D
- / %x74 ; t tab U+0009
- / %x75.30.30.30 (%x30-37 / %x62 / %x65-66) ; u000X
- / %x75.30.30.31 (%x30-39 / %x61-66) ; u001X
-
-Examples
-++++++++
-
-To assist in the development of compatible implementations, the following test
-values may be useful for verifying the canonical transformation code.
-
-Given the following JSON object:
-
-.. code:: json
-
- {}
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {}
-
-Given the following JSON object:
-
-.. code:: json
-
- {
- "one": 1,
- "two": "Two"
- }
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"one":1,"two":"Two"}
-
-Given the following JSON object:
-
-.. code:: json
-
- {
- "b": "2",
- "a": "1"
- }
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"a":"1","b":"2"}
-
-Given the following JSON object:
-
-.. code:: json
-
- {"b":"2","a":"1"}
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"a":"1","b":"2"}
-
-Given the following JSON object:
-
-.. code:: json
-
- {
- "auth": {
- "success": true,
- "mxid": "@john.doe:example.com",
- "profile": {
- "display_name": "John Doe",
- "three_pids": [
- {
- "medium": "email",
- "address": "john.doe@example.org"
- },
- {
- "medium": "msisdn",
- "address": "123456789"
- }
- ]
- }
- }
- }
-
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}
-
-
-Given the following JSON object:
-
-.. code:: json
-
- {
- "a": "日本語"
- }
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"a":"日本語"}
-
-Given the following JSON object:
-
-.. code:: json
-
- {
- "本": 2,
- "日": 1
- }
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"日":1,"本":2}
-
-Given the following JSON object:
-
-.. code:: json
-
- {
- "a": "\u65E5"
- }
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"a":"日"}
-
-Given the following JSON object:
-
-.. code:: json
-
- {
- "a": null
- }
-
-The following canonical JSON should be produced:
-
-.. code:: json
-
- {"a":null}
-
-Signing Details
-~~~~~~~~~~~~~~~
-
-JSON is signed by encoding the JSON object without ``signatures`` or keys grouped
-as ``unsigned``, using the canonical encoding described above. The JSON bytes are then signed using the
-signature algorithm and the signature is encoded using `unpadded Base64`_.
-The resulting base64 signature is added to an object under the
-*signing key identifier* which is added to the ``signatures`` object under the
-name of the entity signing it which is added back to the original JSON object
-along with the ``unsigned`` object.
-
-The *signing key identifier* is the concatenation of the *signing algorithm*
-and a *key identifier*. The *signing algorithm* identifies the algorithm used
-to sign the JSON. The currently supported value for *signing algorithm* is
-``ed25519`` as implemented by NACL (http://nacl.cr.yp.to/). The *key identifier*
-is used to distinguish between different signing keys used by the same entity.
-
-The ``unsigned`` object and the ``signatures`` object are not covered by the
-signature. Therefore intermediate entities can add unsigned data such as
-timestamps and additional signatures.
-
-
-.. code:: json
-
- {
- "name": "example.org",
- "signing_keys": {
- "ed25519:1": "XSl0kuyvrXNj6A+7/tkrB9sxSbRi08Of5uRhxOqZtEQ"
- },
- "unsigned": {
- "age_ts": 922834800000
- },
- "signatures": {
- "example.org": {
- "ed25519:1": "s76RUgajp8w172am0zQb/iPTHsRnb4SkrzGoeCOSFfcBY2V/1c8QfrmdXHpvnc2jK5BD1WiJIxiMW95fMjK7Bw"
- }
- }
- }
-
-.. code:: python
-
- def sign_json(json_object, signing_key, signing_name):
- signatures = json_object.pop("signatures", {})
- unsigned = json_object.pop("unsigned", None)
-
- signed = signing_key.sign(encode_canonical_json(json_object))
- signature_base64 = encode_base64(signed.signature)
-
- key_id = "%s:%s" % (signing_key.alg, signing_key.version)
- signatures.setdefault(signing_name, {})[key_id] = signature_base64
-
- json_object["signatures"] = signatures
- if unsigned is not None:
- json_object["unsigned"] = unsigned
-
- return json_object
-
-Checking for a Signature
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-To check if an entity has signed a JSON object an implementation does the
-following:
-
-1. Checks if the ``signatures`` member of the object contains an entry with
- the name of the entity. If the entry is missing then the check fails.
-2. Removes any *signing key identifiers* from the entry with algorithms it
- doesn't understand. If there are no *signing key identifiers* left then the
- check fails.
-3. Looks up *verification keys* for the remaining *signing key identifiers*
- either from a local cache or by consulting a trusted key server. If it
- cannot find a *verification key* then the check fails.
-4. Decodes the base64 encoded signature bytes. If base64 decoding fails then
- the check fails.
-5. Removes the ``signatures`` and ``unsigned`` members of the object.
-6. Encodes the remainder of the JSON object using the `Canonical JSON`_
- encoding.
-7. Checks the signature bytes against the encoded object using the
- *verification key*. If this fails then the check fails. Otherwise the check
- succeeds.
diff --git a/specification/appendices/test_vectors.rst b/specification/appendices/test_vectors.rst
deleted file mode 100644
index 05b115db8cb..00000000000
--- a/specification/appendices/test_vectors.rst
+++ /dev/null
@@ -1,182 +0,0 @@
-.. Copyright 2015 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-
-Cryptographic Test Vectors
---------------------------
-
-To assist in the development of compatible implementations, the following test
-values may be useful for verifying the cryptographic event signing code.
-
-Signing Key
-~~~~~~~~~~~
-
-The following test vectors all use the 32-byte value given by the following
-Base64-encoded string as the seed for generating the ``ed25519`` signing key:
-
-.. code::
-
- SIGNING_KEY_SEED = decode_base64(
- "YJDBA9Xnr2sVqXD9Vj7XVUnmFZcZrlw8Md7kMW+3XA1"
- )
-
-In each case, the server name and key ID are as follows:
-
-.. code::
-
- SERVER_NAME = "domain"
-
- KEY_ID = "ed25519:1"
-
-JSON Signing
-~~~~~~~~~~~~
-
-Given an empty JSON object:
-
-.. code:: json
-
- {}
-
-The JSON signing algorithm should emit the following signed data:
-
-.. code:: json
-
- {
- "signatures": {
- "domain": {
- "ed25519:1": "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"
- }
- }
- }
-
-Given the following JSON object with data values in it:
-
-.. code:: json
-
- {
- "one": 1,
- "two": "Two"
- }
-
-The JSON signing algorithm should emit the following signed JSON:
-
-.. code:: json
-
- {
- "one": 1,
- "signatures": {
- "domain": {
- "ed25519:1": "KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"
- }
- },
- "two": "Two"
- }
-
-Event Signing
-~~~~~~~~~~~~~
-
-Given the following minimally-sized event:
-
-.. code:: json
-
- {
- "room_id": "!x:domain",
- "sender": "@a:domain",
- "origin": "domain",
- "origin_server_ts": 1000000,
- "signatures": {},
- "hashes": {},
- "type": "X",
- "content": {},
- "prev_events": [],
- "auth_events": [],
- "depth": 3,
- "unsigned": {
- "age_ts": 1000000
- }
- }
-
-The event signing algorithm should emit the following signed event:
-
-.. code:: json
-
- {
- "auth_events": [],
- "content": {},
- "depth": 3,
- "hashes": {
- "sha256": "5jM4wQpv6lnBo7CLIghJuHdW+s2CMBJPUOGOC89ncos"
- },
- "origin": "domain",
- "origin_server_ts": 1000000,
- "prev_events": [],
- "room_id": "!x:domain",
- "sender": "@a:domain",
- "signatures": {
- "domain": {
- "ed25519:1": "KxwGjPSDEtvnFgU00fwFz+l6d2pJM6XBIaMEn81SXPTRl16AqLAYqfIReFGZlHi5KLjAWbOoMszkwsQma+lYAg"
- }
- },
- "type": "X",
- "unsigned": {
- "age_ts": 1000000
- }
- }
-
-Given the following event containing redactable content:
-
-.. code:: json
-
- {
- "content": {
- "body": "Here is the message content"
- },
- "event_id": "$0:domain",
- "origin": "domain",
- "origin_server_ts": 1000000,
- "type": "m.room.message",
- "room_id": "!r:domain",
- "sender": "@u:domain",
- "signatures": {},
- "unsigned": {
- "age_ts": 1000000
- }
- }
-
-The event signing algorithm should emit the following signed event:
-
-.. code:: json
-
- {
- "content": {
- "body": "Here is the message content"
- },
- "event_id": "$0:domain",
- "hashes": {
- "sha256": "onLKD1bGljeBWQhWZ1kaP9SorVmRQNdN5aM2JYU2n/g"
- },
- "origin": "domain",
- "origin_server_ts": 1000000,
- "type": "m.room.message",
- "room_id": "!r:domain",
- "sender": "@u:domain",
- "signatures": {
- "domain": {
- "ed25519:1": "Wm+VzmOUOz08Ds+0NTWb1d4CZrVsJSikkeRxh6aCcUwu6pNC78FunoD7KNWzqFn241eYHYMGCA5McEiVPdhzBA"
- }
- },
- "unsigned": {
- "age_ts": 1000000
- }
- }
diff --git a/specification/appendices/threat_model.rst b/specification/appendices/threat_model.rst
deleted file mode 100644
index 9ad5fef80b7..00000000000
--- a/specification/appendices/threat_model.rst
+++ /dev/null
@@ -1,140 +0,0 @@
-.. Copyright 2015 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Security Threat Model
-----------------------
-
-Denial of Service
-~~~~~~~~~~~~~~~~~
-
-The attacker could attempt to prevent delivery of messages to or from the
-victim in order to:
-
-* Disrupt service or marketing campaign of a commercial competitor.
-* Censor a discussion or censor a participant in a discussion.
-* Perform general vandalism.
-
-Threat: Resource Exhaustion
-+++++++++++++++++++++++++++
-
-An attacker could cause the victims server to exhaust a particular resource
-(e.g. open TCP connections, CPU, memory, disk storage)
-
-Threat: Unrecoverable Consistency Violations
-++++++++++++++++++++++++++++++++++++++++++++
-
-An attacker could send messages which created an unrecoverable "split-brain"
-state in the cluster such that the victim's servers could no longer derive a
-consistent view of the chatroom state.
-
-Threat: Bad History
-+++++++++++++++++++
-
-An attacker could convince the victim to accept invalid messages which the
-victim would then include in their view of the chatroom history. Other servers
-in the chatroom would reject the invalid messages and potentially reject the
-victims messages as well since they depended on the invalid messages.
-
-.. TODO-spec
- Track trustworthiness of HS or users based on if they try to pretend they
- haven't seen recent events, and fake a splitbrain... --M
-
-Threat: Block Network Traffic
-+++++++++++++++++++++++++++++
-
-An attacker could try to firewall traffic between the victim's server and some
-or all of the other servers in the chatroom.
-
-Threat: High Volume of Messages
-+++++++++++++++++++++++++++++++
-
-An attacker could send large volumes of messages to a chatroom with the victim
-making the chatroom unusable.
-
-Threat: Banning users without necessary authorisation
-+++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-An attacker could attempt to ban a user from a chatroom without the necessary
-authorisation.
-
-Spoofing
-~~~~~~~~
-
-An attacker could try to send a message claiming to be from the victim without
-the victim having sent the message in order to:
-
-* Impersonate the victim while performing illicit activity.
-* Obtain privileges of the victim.
-
-Threat: Altering Message Contents
-+++++++++++++++++++++++++++++++++
-
-An attacker could try to alter the contents of an existing message from the
-victim.
-
-Threat: Fake Message "origin" Field
-+++++++++++++++++++++++++++++++++++
-
-An attacker could try to send a new message purporting to be from the victim
-with a phony "origin" field.
-
-Spamming
-~~~~~~~~
-
-The attacker could try to send a high volume of solicited or unsolicited
-messages to the victim in order to:
-
-* Find victims for scams.
-* Market unwanted products.
-
-Threat: Unsolicited Messages
-++++++++++++++++++++++++++++
-
-An attacker could try to send messages to victims who do not wish to receive
-them.
-
-Threat: Abusive Messages
-++++++++++++++++++++++++
-
-An attacker could send abusive or threatening messages to the victim
-
-Spying
-~~~~~~
-
-The attacker could try to access message contents or metadata for messages sent
-by the victim or to the victim that were not intended to reach the attacker in
-order to:
-
-* Gain sensitive personal or commercial information.
-* Impersonate the victim using credentials contained in the messages.
- (e.g. password reset messages)
-* Discover who the victim was talking to and when.
-
-Threat: Disclosure during Transmission
-++++++++++++++++++++++++++++++++++++++
-
-An attacker could try to expose the message contents or metadata during
-transmission between the servers.
-
-Threat: Disclosure to Servers Outside Chatroom
-++++++++++++++++++++++++++++++++++++++++++++++
-
-An attacker could try to convince servers within a chatroom to send messages to
-a server it controls that was not authorised to be within the chatroom.
-
-Threat: Disclosure to Servers Within Chatroom
-+++++++++++++++++++++++++++++++++++++++++++++
-
-An attacker could take control of a server within a chatroom to expose message
-contents or metadata for messages in that room.
diff --git a/specification/appendices/threepids.rst b/specification/appendices/threepids.rst
deleted file mode 100644
index 84860740c30..00000000000
--- a/specification/appendices/threepids.rst
+++ /dev/null
@@ -1,48 +0,0 @@
-.. Copyright 2017 Kamax.io
-..
-.. 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.
-
-3PID Types
-----------
-Third Party Identifiers (3PIDs) represent identifiers on other namespaces that
-might be associated with a particular person. They comprise a tuple of ``medium``
-which is a string that identifies the namespace in which the identifier exists,
-and an ``address``: a string representing the identifier in that namespace. This
-must be a canonical form of the identifier, *i.e.* if multiple strings could
-represent the same identifier, only one of these strings must be used in a 3PID
-address, in a well-defined manner.
-
-For example, for e-mail, the ``medium`` is 'email' and the ``address`` would be the
-email address, *e.g.* the string ``bob@example.com``. Since domain resolution is
-case-insensitive, the email address ``bob@Example.com`` is also has the 3PID address
-of ``bob@example.com`` (without the capital 'e') rather than ``bob@Example.com``.
-
-The namespaces defined by this specification are listed below. More namespaces
-may be defined in future versions of this specification.
-
-E-Mail
-~~~~~~
-Medium: ``email``
-
-Represents E-Mail addresses. The ``address`` is the raw email address in
-``user@domain`` form with the domain in lowercase. It must not contain other text
-such as real name, angle brackets or a mailto: prefix.
-
-PSTN Phone numbers
-~~~~~~~~~~~~~~~~~~
-Medium: ``msisdn``
-
-Represents telephone numbers on the public switched telephone network. The
-``address`` is the telephone number represented as a MSISDN (Mobile Station
-International Subscriber Directory Number) as defined by the E.164 numbering
-plan. Note that MSISDNs do not include a leading '+'.
diff --git a/specification/application_service_api.rst b/specification/application_service_api.rst
deleted file mode 100644
index 302f0980ffc..00000000000
--- a/specification/application_service_api.rst
+++ /dev/null
@@ -1,447 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2018 New Vector Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Application Service API
-=======================
-
-{{unstable_warning_block_APPSERVICE_RELEASE_LABEL}}
-
-The Matrix client-server API and server-server APIs provide the means to
-implement a consistent self-contained federated messaging fabric. However, they
-provide limited means of implementing custom server-side behaviour in Matrix
-(e.g. gateways, filters, extensible hooks etc). The Application Service API (AS API)
-defines a standard API to allow such extensible functionality to be implemented
-irrespective of the underlying homeserver implementation.
-
-.. TODO-spec
- Add in Client-Server services? Overview of bots? Seems weird to be in the spec
- given it is VERY implementation specific.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Changelog
----------
-
-
-.. topic:: Version: %APPSERVICE_RELEASE_LABEL%
-{{application_service_changelog}}
-
-This version of the specification is generated from
-`matrix-doc `_ as of Git commit
-`{{git_version}} `_.
-
-For the full historical changelog, see
-https://github.com/matrix-org/matrix-doc/blob/master/changelogs/application_service.rst
-
-Other versions of this specification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following other versions are also available, in reverse chronological order:
-
-- `HEAD `_: Includes all changes since the latest versioned release.
-- `r0.1.1 `_
-- `r0.1.0 `_
-
-
-Application Services
---------------------
-Application services are passive and can only observe events from homeserver.
-They can inject events into rooms they are participating in.
-They cannot prevent events from being sent, nor can they modify the content of
-the event being sent. In order to observe events from a homeserver, the
-homeserver needs to be configured to pass certain types of traffic to the
-application service. This is achieved by manually configuring the homeserver
-with information about the application service.
-
-Registration
-~~~~~~~~~~~~
-
-.. NOTE::
- Previously, application services could register with a homeserver via HTTP
- APIs. This was removed as it was seen as a security risk. A compromised
- application service could re-register for a global ``*`` regex and sniff
- *all* traffic on the homeserver. To protect against this, application
- services now have to register via configuration files which are linked to
- the homeserver configuration file. The addition of configuration files
- allows homeserver admins to sanity check the registration for suspicious
- regex strings.
-
-.. TODO
- Removing the API entirely is probably a mistake - having a standard cross-HS
- way of doing this stops ASes being coupled to particular HS implementations.
- A better solution would be to somehow mandate that the API done to avoid
- abuse.
-
-Application services register "namespaces" of user IDs, room aliases and room IDs.
-These namespaces are represented as regular expressions. An application service
-is said to be "interested" in a given event if one of the IDs in the event match
-the regular expression provided by the application service, such as the room having
-an alias or ID in the relevant namespaces. Similarly, the application service is
-said to be interested in a given event if one of the application service's namespaced
-users is the target of the event, or is a joined member of the room where the event
-occurred.
-
-An application service can also state whether they should be the only ones who
-can manage a specified namespace. This is referred to as an "exclusive"
-namespace. An exclusive namespace prevents humans and other application
-services from creating/deleting entities in that namespace. Typically,
-exclusive namespaces are used when the rooms represent real rooms on
-another service (e.g. IRC). Non-exclusive namespaces are used when the
-application service is merely augmenting the room itself (e.g. providing
-logging or searching facilities). Namespaces are represented by POSIX extended
-regular expressions and look like:
-
-.. code-block:: yaml
-
- users:
- - exclusive: true
- regex: "@_irc_bridge_.*"
-
-Application services may define the following namespaces (with none being explicitly required):
-
-+------------------+-----------------------------------------------------------+
-| Name | Description |
-+==================+===========================================================+
-| users | Events which are sent from certain users. |
-+------------------+-----------------------------------------------------------+
-| aliases | Events which are sent in rooms with certain room aliases. |
-+------------------+-----------------------------------------------------------+
-| rooms | Events which are sent in rooms with certain room IDs. |
-+------------------+-----------------------------------------------------------+
-
-Each individual namespace MUST declare the following fields:
-
-+------------------+-----------------------------------------------------------------------------------------------------------------------------------+
-| Name | Description |
-+==================+===================================================================================================================================+
-| exclusive | **Required** A true or false value stating whether this application service has exclusive access to events within this namespace. |
-+------------------+-----------------------------------------------------------------------------------------------------------------------------------+
-| regex | **Required** A regular expression defining which values this namespace includes. |
-+------------------+-----------------------------------------------------------------------------------------------------------------------------------+
-
-Exclusive user and alias namespaces should begin with an underscore after the
-sigil to avoid collisions with other users on the homeserver. Application
-services should additionally attempt to identify the service they represent
-in the reserved namespace. For example, ``@_irc_.*`` would be a good namespace
-to register for an application service which deals with IRC.
-
-The registration is represented by a series of key-value pairs, which this
-specification will present as YAML. See below for the possible options along
-with their explanation:
-
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| Name | Description |
-+==================+====================================================================================================================================================+
-| id | **Required.** A unique, user-defined ID of the application service which will never change. |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| url | **Required.** The URL for the application service. May include a path after the domain name. Optionally set to ``null`` if no traffic is required. |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| as_token | **Required.** A unique token for application services to use to authenticate requests to Homeservers. |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| hs_token | **Required.** A unique token for Homeservers to use to authenticate requests to application services. |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| sender_localpart | **Required.** The localpart of the user associated with the application service. |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| namespaces | **Required.** A list of ``users``, ``aliases`` and ``rooms`` namespaces that the application service controls. |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| rate_limited | Whether requests from masqueraded users are rate-limited. The sender is excluded. |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-| protocols | The external protocols which the application service provides (e.g. IRC). |
-+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------+
-
-An example registration file for an IRC-bridging application service is below:
-
-.. code-block:: yaml
-
- id: "IRC Bridge"
- url: "http://127.0.0.1:1234"
- as_token: "30c05ae90a248a4188e620216fa72e349803310ec83e2a77b34fe90be6081f46"
- hs_token: "312df522183efd404ec1cd22d2ffa4bbc76a8c1ccf541dd692eef281356bb74e"
- sender_localpart: "_irc_bot" # Will result in @_irc_bot:example.org
- namespaces:
- users:
- - exclusive: true
- regex: "@_irc_bridge_.*"
- aliases:
- - exclusive: false
- regex: "#_irc_bridge_.*"
- rooms: []
-
-.. WARNING::
- If the homeserver in question has multiple application services, each
- ``as_token`` and ``id`` MUST be unique per application service as these are
- used to identify the application service. The homeserver MUST enforce this.
-
-Homeserver -> Application Service API
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Authorization
-+++++++++++++
-
-Homeservers MUST include a query parameter named ``access_token`` containing the
-``hs_token`` from the application service's registration when making requests to
-the application service. Application services MUST verify the provided ``access_token``
-matches their known ``hs_token``, failing the request with a ``M_FORBIDDEN`` error
-if it does not match.
-
-Legacy routes
-+++++++++++++
-
-Previous drafts of the application service specification had a mix of endpoints
-that have been used in the wild for a significant amount of time. The application
-service specification now defines a version on all endpoints to be more compatible
-with the rest of the Matrix specification and the future.
-
-Homeservers should attempt to use the specified endpoints first when communicating
-with application services. However, if the application service receives an http status
-code that does not indicate success (ie: 404, 500, 501, etc) then the homeserver
-should fall back to the older endpoints for the application service.
-
-The older endpoints have the exact same request body and response format, they
-just belong at a different path. The equivalent path for each is as follows:
-
-* ``/_matrix/app/v1/transactions/{txnId}`` should fall back to ``/transactions/{txnId}``
-* ``/_matrix/app/v1/users/{userId}`` should fall back to ``/users/{userId}``
-* ``/_matrix/app/v1/rooms/{roomAlias}`` should fall back to ``/rooms/{roomAlias}``
-* ``/_matrix/app/v1/thirdparty/protocol/{protocol}`` should fall back to ``/_matrix/app/unstable/thirdparty/protocol/{protocol}``
-* ``/_matrix/app/v1/thirdparty/user/{user}`` should fall back to ``/_matrix/app/unstable/thirdparty/user/{user}``
-* ``/_matrix/app/v1/thirdparty/location/{location}`` should fall back to ``/_matrix/app/unstable/thirdparty/location/{location}``
-* ``/_matrix/app/v1/thirdparty/user`` should fall back to ``/_matrix/app/unstable/thirdparty/user``
-* ``/_matrix/app/v1/thirdparty/location`` should fall back to ``/_matrix/app/unstable/thirdparty/location``
-
-Homeservers should periodically try again for the newer endpoints because the
-application service may have been updated.
-
-Pushing events
-++++++++++++++
-
-The application service API provides a transaction API for sending a list of
-events. Each list of events includes a transaction ID, which works as follows:
-
-::
-
- Typical
- HS ---> AS : Homeserver sends events with transaction ID T.
- <--- : Application Service sends back 200 OK.
-
- AS ACK Lost
- HS ---> AS : Homeserver sends events with transaction ID T.
- <-/- : AS 200 OK is lost.
- HS ---> AS : Homeserver retries with the same transaction ID of T.
- <--- : Application Service sends back 200 OK. If the AS had processed these
- events already, it can NO-OP this request (and it knows if it is the
- same events based on the transaction ID).
-
-The events sent to the application service should be linearised, as if they were
-from the event stream. The homeserver MUST maintain a queue of transactions to
-send to the application service. If the application service cannot be reached, the
-homeserver SHOULD backoff exponentially until the application service is reachable again.
-As application services cannot *modify* the events in any way, these requests can
-be made without blocking other aspects of the homeserver. Homeservers MUST NOT
-alter (e.g. add more) events they were going to send within that transaction ID
-on retries, as the application service may have already processed the events.
-
-{{transactions_as_http_api}}
-
-Querying
-++++++++
-
-The application service API includes two querying APIs: for room aliases and for
-user IDs. The application service SHOULD create the queried entity if it desires.
-During this process, the application service is blocking the homeserver until the
-entity is created and configured. If the homeserver does not receive a response
-to this request, the homeserver should retry several times before timing out. This
-should result in an HTTP status 408 "Request Timeout" on the client which initiated
-this request (e.g. to join a room alias).
-
-.. admonition:: Rationale
-
- Blocking the homeserver and expecting the application service to create the entity
- using the client-server API is simpler and more flexible than alternative methods
- such as returning an initial sync style JSON blob and get the HS to provision
- the room/user. This also meant that there didn't need to be a "backchannel" to inform
- the application service about information about the entity such as room ID to
- room alias mappings.
-
-{{query_user_as_http_api}}
-
-{{query_room_as_http_api}}
-
-
-Third party networks
-++++++++++++++++++++
-
-Application services may declare which protocols they support via their registration
-configuration for the homeserver. These networks are generally for third party services
-such as IRC that the application service is managing. Application services may populate
-a Matrix room directory for their registered protocols, as defined in the Client-Server
-API Extensions.
-
-Each protocol may have several "locations" (also known as "third party locations" or "3PLs").
-A location within a protocol is a place in the third party network, such as an IRC channel.
-Users of the third party network may also be represented by the application service.
-
-Locations and users can be searched by fields defined by the application service, such
-as by display name or other attribute. When clients request the homeserver to search
-in a particular "network" (protocol), the search fields will be passed along to the
-application service for filtering.
-
-{{protocols_as_http_api}}
-
-
-.. _create the user: `sect:asapi-permissions`_
-
-Client-Server API Extensions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Application services can use a more powerful version of the
-client-server API by identifying itself as an application service to the
-homeserver.
-
-Endpoints defined in this section MUST be supported by homeservers in the
-client-server API as accessible only by application services.
-
-Identity assertion
-++++++++++++++++++
-The client-server API infers the user ID from the ``access_token`` provided in
-every request. To avoid the application service from having to keep track of each
-user's access token, the application service should identify itself to the Client-Server
-API by providing its ``as_token`` for the ``access_token`` alongside the user the
-application service would like to masquerade as.
-
-Inputs:
- - Application service token (``as_token``)
- - User ID in the AS namespace to act as.
-
-Notes:
- - This applies to all aspects of the Client-Server API, except for Account Management.
- - The ``as_token`` is inserted into ``access_token`` which is usually where the
- client token is, such as via the query string or ``Authorization`` header. This
- is done on purpose to allow application services to reuse client SDKs.
- - The ``access_token`` should be supplied through the ``Authorization`` header where
- possible to prevent the token appearing in HTTP request logs by accident.
-
-The application service may specify the virtual user to act as through use of a
-``user_id`` query string parameter on the request. The user specified in the query
-string must be covered by one of the application service's ``user`` namespaces. If
-the parameter is missing, the homeserver is to assume the application service intends
-to act as the user implied by the ``sender_localpart`` property of the registration.
-
-An example request would be::
-
- GET /_matrix/client/%CLIENT_MAJOR_VERSION%/account/whoami?user_id=@_irc_user:example.org
- Authorization: Bearer YourApplicationServiceTokenHere
-
-.. TODO-TravisR: Temporarily take out timestamp massaging while we're releasing r0.
- See https://github.com/matrix-org/matrix-doc/issues/1585
-.. Timestamp massaging
- +++++++++++++++++++
- The application service may want to inject events at a certain time (reflecting
- the time on the network they are tracking e.g. irc, xmpp). Application services
- need to be able to adjust the ``origin_server_ts`` value to do this.
-
- Inputs:
- - Application service token (``as_token``)
- - Desired timestamp (in milliseconds since the unix epoch)
-
- Notes:
- - This will only apply when sending events.
-
- ::
-
- PUT /_matrix/client/r0/rooms/!somewhere:example.org/send/m.room.message/txnId?ts=1534535223283
- Authorization: Bearer YourApplicationServiceTokenHere
-
- Content: The event to send, as per the Client-Server API.
-
-Timestamp massaging
-+++++++++++++++++++
-
-Previous drafts of the Application Service API permitted application services
-to alter the timestamp of their sent events by providing a ``ts`` query parameter
-when sending an event. This API has been excluded from the first release due to
-design concerns, however some servers may still support the feature. Please visit
-`issue #1585 `_ for more
-information.
-
-Server admin style permissions
-++++++++++++++++++++++++++++++
-
-.. _sect:asapi-permissions:
-
-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 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:
-
-- Work around captchas.
-- Have a 'passwordless' user.
-
-This involves bypassing the registration flows entirely. This is achieved by
-including the ``as_token`` on a ``/register`` request, along with a login type of
-``m.login.application_service`` to set the desired user ID without a password.
-
-::
-
- POST /_matrix/client/%CLIENT_MAJOR_VERSION%/register
- Authorization: Bearer YourApplicationServiceTokenHere
-
- Content:
- {
- type: "m.login.application_service",
- username: "_irc_example"
- }
-
-Application services which attempt to create users or aliases *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 defined the namespace as ``exclusive``.
-
-Using ``/sync`` and ``/events``
-+++++++++++++++++++++++++++++++
-
-Application services wishing to use ``/sync`` or ``/events`` from the Client-Server
-API MUST do so with a virtual user (provide a ``user_id`` via the query string). It
-is expected that the application service use the transactions pushed to it to
-handle events rather than syncing with the user implied by ``sender_localpart``.
-
-Application service room directories
-++++++++++++++++++++++++++++++++++++
-
-Application services can maintain their own room directories for their defined
-third party protocols. These room directories may be accessed by clients through
-additional parameters on the ``/publicRooms`` client-server endpoint.
-
-{{appservice_room_directory_cs_http_api}}
-
-Referencing messages from a third party network
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Application services should include an ``external_url`` in the ``content`` of
-events it emits to indicate where the message came from. This typically applies
-to application services that bridge other networks into Matrix, such as IRC,
-where an HTTP URL may be available to reference.
-
-Clients should provide users with a way to access the ``external_url`` if it
-is present. Clients should additionally ensure the URL has a scheme of ``https``
-or ``http`` before making use of it.
-
-The presence of an ``external_url`` on an event does not necessarily mean the
-event was sent from an application service. Clients should be wary of the URL
-contained within, as it may not be a legitimate reference to the event's source.
diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst
deleted file mode 100644
index 4847d837117..00000000000
--- a/specification/client_server_api.rst
+++ /dev/null
@@ -1,2085 +0,0 @@
-.. Copyright 2016-2020 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-Client-Server API
-=================
-
-{{unstable_warning_block_CLIENT_RELEASE_LABEL}}
-
-The client-server API provides a simple lightweight API to let clients send
-messages, control rooms and synchronise conversation history. It is designed to
-support both lightweight clients which store no state and lazy-load data from
-the server as required - as well as heavyweight clients which maintain a full
-local persistent copy of server state.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Changelog
----------
-
-.. topic:: Version: %CLIENT_RELEASE_LABEL%
-{{client_server_changelog}}
-
-This version of the specification is generated from
-`matrix-doc `_ as of Git commit
-`{{git_version}} `_.
-
-For the full historical changelog, see
-https://github.com/matrix-org/matrix-doc/blob/master/changelogs/client_server.rst
-
-Other versions of this specification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following other versions are also available, in reverse chronological order:
-
-- `HEAD `_: Includes all changes since the latest versioned release.
-- `r0.6.1 `_
-- `r0.6.0 `_
-- `r0.5.0 `_
-- `r0.4.0 `_
-- `r0.3.0 `_
-- `r0.2.0 `_
-- `r0.1.0 `_
-- `r0.0.1 `_
-- `r0.0.0 `_
-- `Legacy `_: The last draft before the spec was formally released in version r0.0.0.
-
-
-API Standards
--------------
-
-.. TODO: Move a lot of this to a common area for all specs.
-
-.. TODO
- Need to specify any HMAC or access_token lifetime/ratcheting tricks
- We need to specify capability negotiation for extensible transports
-
-The mandatory baseline for client-server communication in Matrix is exchanging
-JSON objects over HTTP APIs. HTTPS is recommended for communication, although
-HTTP may be supported as a fallback to support basic
-HTTP clients. More efficient optional transports
-will in future be 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`_ for details), passed as a
-query string parameter on all requests.
-
-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.
-
-.. NOTE::
- There are a few historical exceptions to this rule, such as
- ``/createRoom``. A future version of this specification will address the
- inconsistency.
-
-Any errors which occur at the Matrix API level MUST return a "standard error
-response". This is a JSON object which looks like:
-
-.. code:: json
-
- {
- "errcode": "",
- "error": ""
- }
-
-The ``error`` string will be a human-readable error message, usually a sentence
-explaining what went wrong. The ``errcode`` string will be a unique string
-which can be used to handle an error message e.g. ``M_FORBIDDEN``. 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.
-
-Errors are generally best expressed by their error code rather than the HTTP
-status code returned. When encountering the error code ``M_UNKNOWN``, clients
-should prefer the HTTP status code as a more reliable reference for what the
-issue was. For example, if the client receives an error code of ``M_NOT_FOUND``
-but the request gave a 400 Bad Request status code, the client should treat
-the error as if the resource was not 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:
-
-:``M_FORBIDDEN``:
- Forbidden access, e.g. joining a room without permission, failed login.
-
-:``M_UNKNOWN_TOKEN``:
- The access token specified was not recognised.
-
- An additional response parameter, ``soft_logout``, might be present on the response
- for 401 HTTP status codes. See `the soft logout section <#soft-logout>`_ for more
- information.
-
-:``M_MISSING_TOKEN``:
- No access token was specified for the request.
-
-:``M_BAD_JSON``:
- Request contained valid JSON, but it was malformed in some way, e.g. missing
- required keys, invalid values for keys.
-
-:``M_NOT_JSON``:
- Request did not contain valid JSON.
-
-:``M_NOT_FOUND``:
- No resource was found for this request.
-
-:``M_LIMIT_EXCEEDED``:
- Too many requests have been sent in a short period of time. Wait a while then
- try again.
-
-:``M_UNKNOWN``:
- An unknown error has occurred.
-
-Other error codes the client might encounter are:
-
-:``M_UNRECOGNIZED``:
- The server did not understand the request.
-
-:``M_UNAUTHORIZED``:
- The request was not correctly authorized. Usually due to login failures.
-
-:``M_USER_DEACTIVATED``:
- The user ID associated with the request has been deactivated. Typically for
- endpoints that prove authentication, such as ``/login``.
-
-:``M_USER_IN_USE``:
- Encountered when trying to register a user ID which has been taken.
-
-:``M_INVALID_USERNAME``:
- Encountered when trying to register a user ID which is not valid.
-
-:``M_ROOM_IN_USE``:
- Sent when the room alias given to the ``createRoom`` API is already in use.
-
-:``M_INVALID_ROOM_STATE``:
- Sent when the initial state given to the ``createRoom`` API is invalid.
-
-:``M_THREEPID_IN_USE``:
- Sent when a threepid given to an API cannot be used because the same threepid is already in use.
-
-:``M_THREEPID_NOT_FOUND``:
- Sent when a threepid given to an API cannot be used because no record matching the threepid was found.
-
-:``M_THREEPID_AUTH_FAILED``:
- Authentication could not be performed on the third party identifier.
-
-:``M_THREEPID_DENIED``:
- The server does not permit this third party identifier. This may happen if the server only
- permits, for example, email addresses from a particular domain.
-
-:``M_SERVER_NOT_TRUSTED``:
- The client's request used a third party server, eg. identity server, that this server does not trust.
-
-:``M_UNSUPPORTED_ROOM_VERSION``:
- The client's request to create a room used a room version that the server does not support.
-
-:``M_INCOMPATIBLE_ROOM_VERSION``:
- The client attempted to join a room that has a version the server does not support. Inspect the
- ``room_version`` property of the error response for the room's version.
-
-:``M_BAD_STATE``:
- The state change requested cannot be performed, such as attempting to unban
- a user who is not banned.
-
-:``M_GUEST_ACCESS_FORBIDDEN``:
- The room or resource does not permit guests to access it.
-
-:``M_CAPTCHA_NEEDED``:
- A Captcha is required to complete the request.
-
-:``M_CAPTCHA_INVALID``:
- The Captcha provided did not match what was expected.
-
-:``M_MISSING_PARAM``:
- A required parameter was missing from the request.
-
-:``M_INVALID_PARAM``:
- A parameter that was specified has the wrong value. For example, the server
- expected an integer and instead received a string.
-
-:``M_TOO_LARGE``:
- The request or entity was too large.
-
-:``M_EXCLUSIVE``:
- The resource being requested is reserved by an application service, or the
- application service making the request has not created the resource.
-
-:``M_RESOURCE_LIMIT_EXCEEDED``:
- The request cannot be completed because the homeserver has reached a resource
- limit imposed on it. For example, a homeserver held in a shared hosting environment
- may reach a resource limit if it starts using too much memory or disk space. The
- error MUST have an ``admin_contact`` field to provide the user receiving the error
- a place to reach out to. Typically, this error will appear on routes which attempt
- to modify state (eg: sending messages, account data, etc) and not routes which only
- read state (eg: ``/sync``, get account data, etc).
-
-:``M_CANNOT_LEAVE_SERVER_NOTICE_ROOM``:
- The user is unable to reject an invite to join the server notices room. See the
- `Server Notices <#server-notices>`_ module for more information.
-
-.. TODO: More error codes (covered by other issues)
-.. * M_CONSENT_NOT_GIVEN - GDPR: https://github.com/matrix-org/matrix-doc/issues/1512
-
-.. _sect:txn_ids:
-
-The client-server API typically uses ``HTTP PUT`` to submit requests with a
-client-generated transaction identifier. This means that these requests are
-idempotent. The scope of a transaction identifier is a particular access token.
-It **only** serves to identify new
-requests from retransmits. After the request has finished, the ``{txnId}``
-value should be changed (how is not specified; a monotonically increasing
-integer is recommended).
-
-Some API endpoints may allow or require the use of ``POST`` requests without a
-transaction ID. Where this is optional, the use of a ``PUT`` request is strongly
-recommended.
-
-{{versions_cs_http_api}}
-
-
-.. _`CORS`:
-
-Web Browser Clients
--------------------
-
-It is realistic to expect that some clients will be written to be run within a
-web browser or similar environment. In these cases, the homeserver should respond
-to pre-flight requests and supply Cross-Origin Resource Sharing (CORS) headers on
-all requests.
-
-Servers MUST expect that clients will approach them with ``OPTIONS`` requests,
-allowing clients to discover the CORS headers. All endpoints in this specification s
-upport the ``OPTIONS`` method, however the server MUST NOT perform any logic defined
-for the endpoints when approached with an ``OPTIONS`` request.
-
-When a client approaches the server with a request, the server should respond with
-the CORS headers for that route. The recommended CORS headers to be returned by
-servers on all requests are:
-
-.. code::
-
- 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
-
-Server Discovery
-----------------
-
-In order to allow users to connect to a Matrix server without needing to
-explicitly specify the homeserver's URL or other parameters, clients SHOULD use
-an auto-discovery mechanism to determine the server's URL based on a user's
-Matrix ID. Auto-discovery should only be done at login time.
-
-In this section, the following terms are used with specific meanings:
-
-``PROMPT``
- Retrieve the specific piece of information from the user in a way which
- fits within the existing client user experience, if the client is inclined to
- do so. Failure can take place instead if no good user experience for this is
- possible at this point.
-
-``IGNORE``
- Stop the current auto-discovery mechanism. If no more auto-discovery
- mechanisms are available, then the client may use other methods of
- determining the required parameters, such as prompting the user, or using
- default values.
-
-``FAIL_PROMPT``
- Inform the user that auto-discovery failed due to invalid/empty data and
- ``PROMPT`` for the parameter.
-
-``FAIL_ERROR``
- Inform the user that auto-discovery did not return any usable URLs. Do not
- continue further with the current login process. At this point, valid data
- was obtained, but no server is available to serve the client. No further
- guess should be attempted and the user should make a conscientious decision
- what to do next.
-
-Well-known URI
-~~~~~~~~~~~~~~
-
-.. Note::
- Servers hosting the ``.well-known`` JSON file SHOULD offer CORS headers, as
- per the `CORS`_ section in this specification.
-
-The ``.well-known`` method uses a JSON file at a predetermined location to
-specify parameter values. The flow for this method is as follows:
-
-1. Extract the server name from the user's Matrix ID by splitting the Matrix ID
- at the first colon.
-2. Extract the hostname from the server name.
-3. Make a GET request to ``https://hostname/.well-known/matrix/client``.
-
- a. If the returned status code is 404, then ``IGNORE``.
- b. If the returned status code is not 200, or the response body is empty,
- then ``FAIL_PROMPT``.
- c. Parse the response body as a JSON object
-
- i. If the content cannot be parsed, then ``FAIL_PROMPT``.
-
- d. Extract the ``base_url`` value from the ``m.homeserver`` property. This
- value is to be used as the base URL of the homeserver.
-
- i. If this value is not provided, then ``FAIL_PROMPT``.
-
- e. Validate the homeserver base URL:
-
- i. Parse it as a URL. If it is not a URL, then ``FAIL_ERROR``.
- ii. Clients SHOULD validate that the URL points to a valid homeserver
- before accepting it by connecting to the |/_matrix/client/versions|_
- endpoint, ensuring that it does not return an error, and parsing and
- validating that the data conforms with the expected response
- format. If any step in the validation fails, then
- ``FAIL_ERROR``. Validation is done as a simple check against
- configuration errors, in order to ensure that the discovered address
- points to a valid homeserver.
-
- f. If the ``m.identity_server`` property is present, extract the
- ``base_url`` value for use as the base URL of the identity server.
- Validation for this URL is done as in the step above, but using
- ``/_matrix/identity/api/v1`` as the endpoint to connect to. If the
- ``m.identity_server`` property is present, but does not have a
- ``base_url`` value, then ``FAIL_ERROR``.
-
-{{wellknown_cs_http_api}}
-
-Client Authentication
----------------------
-
-Most API endpoints require the user to identify themselves by presenting
-previously obtained credentials in the form of an ``access_token`` query
-parameter or through an Authorization Header of ``Bearer $access_token``.
-An access token is typically obtained via the `Login`_ or `Registration`_ processes.
-
-.. NOTE::
-
- This specification does not mandate a particular format for the access
- token. Clients should treat it as an opaque byte sequence. Servers are free
- to choose an appropriate format. Server implementors may like to investigate
- `macaroons `_.
-
-Using access tokens
-~~~~~~~~~~~~~~~~~~~
-
-Access tokens may be provided in two ways, both of which the homeserver MUST
-support:
-
-1. Via a query string parameter, ``access_token=TheTokenHere``.
-#. Via a request header, ``Authorization: Bearer TheTokenHere``.
-
-Clients are encouraged to use the ``Authorization`` header where possible
-to prevent the access token being leaked in access/HTTP logs. The query
-string should only be used in cases where the ``Authorization`` header is
-inaccessible for the client.
-
-When credentials are required but missing or invalid, the HTTP call will
-return with a status of 401 and the error code, ``M_MISSING_TOKEN`` or
-``M_UNKNOWN_TOKEN`` respectively.
-
-Relationship between access tokens and devices
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Client `devices`_ are closely related to access tokens. Matrix servers should
-record which device each access token is assigned to, so that subsequent
-requests can be handled correctly.
-
-By default, the `Login`_ and `Registration`_ processes auto-generate a new
-``device_id``. A client is also free to generate its own ``device_id`` or,
-provided the user remains the same, reuse a device: in either case the client
-should pass the ``device_id`` in the request body. If the client sets the
-``device_id``, the server will invalidate any access token previously assigned
-to that device. There is therefore at most one active access token assigned to
-each device at any one time.
-
-Soft logout
-~~~~~~~~~~~
-
-When a request fails due to a 401 status code per above, the server can
-include an extra response parameter, ``soft_logout``, to indicate if the client's
-persisted information can be retained. This defaults to ``false``, indicating
-that the server has destroyed the session. Any persisted state held by the client,
-such as encryption keys and device information, must not be reused and must be discarded.
-
-When ``soft_logout`` is true, the client can acquire a new access token by
-specifying the device ID it is already using to the login API. In most cases
-a ``soft_logout: true`` response indicates that the user's session has expired
-on the server-side and the user simply needs to provide their credentials again.
-
-In either case, the client's previously known access token will no longer function.
-
-.. _`user-interactive authentication`:
-
-User-Interactive Authentication API
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Overview
-<<<<<<<<
-
-Some API endpoints require authentication that interacts with the user. The
-homeserver may provide many different ways of authenticating, such as
-user/password auth, login via a single-sign-on server (SSO), etc. This
-specification does not define how homeservers should authorise their users but
-instead defines the standard interface which implementations should follow so
-that ANY client can login to ANY homeserver.
-
-The process takes the form of one or more 'stages'. At each stage the client
-submits a set of data for a given authentication type and awaits a response
-from the server, which will either be a final success or a request to perform
-an additional stage. This exchange continues until the final success.
-
-For each endpoint, a server offers one or more 'flows' that the client can use
-to authenticate itself. Each flow comprises a series of stages, as described
-above. The client is free to choose which flow it follows, however the flow's
-stages must be completed in order. Failing to follow the flows in order must
-result in an HTTP 401 response, as defined below. When all stages in a flow
-are complete, authentication is complete and the API call succeeds.
-
-User-interactive API in the REST API
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-In the REST API described in this specification, authentication works by the
-client and server exchanging JSON dictionaries. The server indicates what
-authentication data it requires via the body of an HTTP 401 response, and the
-client submits that authentication data via the ``auth`` request parameter.
-
-A client should first make a request with no ``auth`` parameter [#]_. The
-homeserver returns an HTTP 401 response, with a JSON body, as follows:
-
-.. code::
-
- HTTP/1.1 401 Unauthorized
- Content-Type: application/json
-
- {
- "flows": [
- {
- "stages": [ "example.type.foo", "example.type.bar" ]
- },
- {
- "stages": [ "example.type.foo", "example.type.baz" ]
- }
- ],
- "params": {
- "example.type.baz": {
- "example_key": "foobar"
- }
- },
- "session": "xxxxxx"
- }
-
-In addition to the ``flows``, this object contains some extra
-information:
-
-params
- This section contains any information that the client will need to know in
- order to use a given type of authentication. For each authentication type
- presented, that type may be present as a key in this dictionary. For example,
- the public part of an OAuth client ID could be given here.
-session
- This is a session identifier that the client must pass back to the homeserver,
- if one is provided, in subsequent attempts to authenticate in the same API call.
-
-The client then chooses a flow and attempts to complete the first stage. It
-does this by resubmitting the same request with the addition of an ``auth``
-key in the object that it submits. This dictionary contains a ``type`` key whose
-value is the name of the authentication type that the client is attempting to complete.
-It must also contain a ``session`` key with the value of the session key given
-by the homeserver, if one was given. It also contains other keys dependent on
-the auth type being attempted. For example, if the client is attempting to
-complete auth type ``example.type.foo``, it might submit something like this:
-
-.. code::
-
- POST /_matrix/client/r0/endpoint HTTP/1.1
- Content-Type: application/json
-
- {
- "a_request_parameter": "something",
- "another_request_parameter": "something else",
- "auth": {
- "type": "example.type.foo",
- "session": "xxxxxx",
- "example_credential": "verypoorsharedsecret"
- }
- }
-
-If the homeserver deems the authentication attempt to be successful but still
-requires more stages to be completed, it returns HTTP status 401 along with the
-same object as when no authentication was attempted, with the addition of the
-``completed`` key which is an array of auth types the client has completed
-successfully:
-
-.. code::
-
- HTTP/1.1 401 Unauthorized
- Content-Type: application/json
-
- {
- "completed": [ "example.type.foo" ],
- "flows": [
- {
- "stages": [ "example.type.foo", "example.type.bar" ]
- },
- {
- "stages": [ "example.type.foo", "example.type.baz" ]
- }
- ],
- "params": {
- "example.type.baz": {
- "example_key": "foobar"
- }
- },
- "session": "xxxxxx"
- }
-
-Individual stages may require more than one request to complete, in which case
-the response will be as if the request was unauthenticated with the addition of
-any other keys as defined by the auth type.
-
-If the homeserver decides that an attempt on a stage was unsuccessful, but the
-client may make a second attempt, it returns the same HTTP status 401 response
-as above, with the addition of the standard ``errcode`` and ``error`` fields
-describing the error. For example:
-
-.. code::
-
- HTTP/1.1 401 Unauthorized
- Content-Type: application/json
-
- {
- "errcode": "M_FORBIDDEN",
- "error": "Invalid password",
- "completed": [ "example.type.foo" ],
- "flows": [
- {
- "stages": [ "example.type.foo", "example.type.bar" ]
- },
- {
- "stages": [ "example.type.foo", "example.type.baz" ]
- }
- ],
- "params": {
- "example.type.baz": {
- "example_key": "foobar"
- }
- },
- "session": "xxxxxx"
- }
-
-If the request fails for a reason other than authentication, the server returns an error
-message in the standard format. For example:
-
-.. code::
-
- HTTP/1.1 400 Bad request
- Content-Type: application/json
-
- {
- "errcode": "M_EXAMPLE_ERROR",
- "error": "Something was wrong"
- }
-
-If the client has completed all stages of a flow, the homeserver performs the
-API call and returns the result as normal. Completed stages cannot be retried
-by clients, therefore servers must return either a 401 response with the completed
-stages, or the result of the API call if all stages were completed when a client
-retries a stage.
-
-Some authentication types may be completed by means other than through the
-Matrix client, for example, an email confirmation may be completed when the user
-clicks on the link in the email. In this case, the client retries the request
-with an auth dict containing only the session key. The response to this will be
-the same as if the client were attempting to complete an auth state normally,
-i.e. the request will either complete or request auth, with the presence or
-absence of that auth type in the 'completed' array indicating whether
-that stage is complete.
-
-.. [#] A request to an endpoint that uses User-Interactive Authentication never
- succeeds without auth. Homeservers may allow requests that don't require
- auth by offering a stage with only the ``m.login.dummy`` auth type, but
- they must still give a 401 response to requests with no auth data.
-
-Example
-+++++++
-At a high level, the requests made for an API call completing an auth flow with
-three stages will resemble the following diagram::
-
- _______________________
- | Stage 0 |
- | No auth |
- | ___________________ |
- | |_Request_1_________| | <-- Returns "session" key which is used throughout.
- |_______________________|
- |
- |
- _________V_____________
- | Stage 1 |
- | type: "" |
- | ___________________ |
- | |_Request_1_________| |
- |_______________________|
- |
- |
- _________V_____________
- | Stage 2 |
- | type: "" |
- | ___________________ |
- | |_Request_1_________| |
- | ___________________ |
- | |_Request_2_________| |
- | ___________________ |
- | |_Request_3_________| |
- |_______________________|
- |
- |
- _________V_____________
- | Stage 3 |
- | type: "" |
- | ___________________ |
- | |_Request_1_________| | <-- Returns API response
- |_______________________|
-
-
-Authentication types
-++++++++++++++++++++
-
-This specification defines the following auth types:
- - ``m.login.password``
- - ``m.login.recaptcha``
- - ``m.login.sso``
- - ``m.login.email.identity``
- - ``m.login.msisdn``
- - ``m.login.dummy``
-
-Password-based
-<<<<<<<<<<<<<<
-:Type:
- ``m.login.password``
-:Description:
- The client submits an identifier and secret password, both sent in plain-text.
-
-To use this authentication type, clients should submit an auth dict as follows:
-
-.. code:: json
-
- {
- "type": "m.login.password",
- "identifier": {
- ...
- },
- "password": "",
- "session": ""
- }
-
-where the ``identifier`` property is a user identifier object, as described in
-`Identifier types`_.
-
-For example, to authenticate using the user's Matrix ID, clients would submit:
-
-.. code:: json
-
- {
- "type": "m.login.password",
- "identifier": {
- "type": "m.id.user",
- "user": ""
- },
- "password": "",
- "session": ""
- }
-
-Alternatively reply using a 3PID bound to the user's account on the homeserver
-using the |/account/3pid|_ API rather then giving the ``user`` explicitly as
-follows:
-
-.. code:: json
-
- {
- "type": "m.login.password",
- "identifier": {
- "type": "m.id.thirdparty",
- "medium": "",
- "address": ""
- },
- "password": "",
- "session": ""
- }
-
-In the case that the homeserver does not know about the supplied 3PID, the
-homeserver must respond with 403 Forbidden.
-
-Google ReCaptcha
-<<<<<<<<<<<<<<<<
-:Type:
- ``m.login.recaptcha``
-:Description:
- The user completes a Google ReCaptcha 2.0 challenge
-
-To use this authentication type, clients should submit an auth dict as follows:
-
-.. code:: json
-
- {
- "type": "m.login.recaptcha",
- "response": "",
- "session": ""
- }
-
-Single Sign-On
-<<<<<<<<<<<<<<
-:Type:
- ``m.login.sso``
-:Description:
- Authentication is supported by authorising with an external single sign-on
- provider.
-
-A client wanting to complete authentication using SSO should use the
-`Fallback`_ mechanism. See `SSO during User-Interactive Authentication`_ for
-more information.
-
-Email-based (identity / homeserver)
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-:Type:
- ``m.login.email.identity``
-:Description:
- Authentication is supported by authorising an email address with an identity
- server, or homeserver if supported.
-
-Prior to submitting this, the client should authenticate with an identity
-server (or homeserver). After authenticating, the session information should
-be submitted to the homeserver.
-
-To use this authentication type, clients should submit an auth dict as follows:
-
-.. code:: json
-
- {
- "type": "m.login.email.identity",
- "threepidCreds": [
- {
- "sid": "",
- "client_secret": "",
- "id_server": "",
- "id_access_token": ""
- }
- ],
- "session": ""
- }
-
-Note that ``id_server`` (and therefore ``id_access_token``) is optional if the
-``/requestToken`` request did not include them.
-
-Phone number/MSISDN-based (identity / homeserver)
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-:Type:
- ``m.login.msisdn``
-:Description:
- Authentication is supported by authorising a phone number with an identity
- server, or homeserver if supported.
-
-Prior to submitting this, the client should authenticate with an identity
-server (or homeserver). After authenticating, the session information should
-be submitted to the homeserver.
-
-To use this authentication type, clients should submit an auth dict as follows:
-
-.. code:: json
-
- {
- "type": "m.login.msisdn",
- "threepidCreds": [
- {
- "sid": "",
- "client_secret": "",
- "id_server": "",
- "id_access_token": ""
- }
- ],
- "session": ""
- }
-
-Note that ``id_server`` (and therefore ``id_access_token``) is optional if the
-``/requestToken`` request did not include them.
-
-Dummy Auth
-<<<<<<<<<<
-:Type:
- ``m.login.dummy``
-:Description:
- Dummy authentication always succeeds and requires no extra parameters. Its
- purpose is to allow servers to not require any form of User-Interactive
- Authentication to perform a request. It can also be used to differentiate
- flows where otherwise one flow would be a subset of another flow. eg. if
- a server offers flows ``m.login.recaptcha`` and ``m.login.recaptcha,
- m.login.email.identity`` and the client completes the recaptcha stage first,
- the auth would succeed with the former flow, even if the client was intending
- to then complete the email auth stage. A server can instead send flows
- ``m.login.recaptcha, m.login.dummy`` and ``m.login.recaptcha,
- m.login.email.identity`` to fix the ambiguity.
-
-To use this authentication type, clients should submit an auth dict with just
-the type and session, if provided:
-
-.. code:: json
-
- {
- "type": "m.login.dummy",
- "session": ""
- }
-
-
-Fallback
-++++++++
-Clients cannot be expected to be able to know how to process every single login
-type. If a client does not know how to handle a given login type, it can direct
-the user to a web browser with the URL of a fallback page which will allow the
-user to complete that login step out-of-band in their web browser. The URL it
-should open is::
-
- /_matrix/client/%CLIENT_MAJOR_VERSION%/auth//fallback/web?session=
-
-Where ``auth type`` is the type name of the stage it is attempting and
-``session ID`` is the ID of the session given by the homeserver.
-
-.. _`user-interactive authentication fallback completion`:
-
-This MUST return an HTML page which can perform this authentication stage. This
-page must use the following JavaScript when the authentication has been
-completed:
-
-.. code:: javascript
-
- if (window.onAuthDone) {
- window.onAuthDone();
- } else if (window.opener && window.opener.postMessage) {
- window.opener.postMessage("authDone", "*");
- }
-
-This allows the client to either arrange for the global function ``onAuthDone``
-to be defined in an embedded browser, or to use the HTML5 `cross-document
-messaging `_ API, to receive
-a notification that the authentication stage has been completed.
-
-Once a client receives the notificaton that the authentication stage has been
-completed, it should resubmit the request with an auth dict with just the
-session ID:
-
-.. code:: json
-
- {
- "session": ""
- }
-
-
-Example
-<<<<<<<
-A client webapp might use the following javascript to open a popup window which will
-handle unknown login types:
-
-.. code:: javascript
-
- /**
- * Arguments:
- * homeserverUrl: the base url of the homeserver (eg "https://matrix.org")
- *
- * apiEndpoint: the API endpoint being used (eg
- * "/_matrix/client/%CLIENT_MAJOR_VERSION%/account/password")
- *
- * loginType: the loginType being attempted (eg "m.login.recaptcha")
- *
- * sessionID: the session ID given by the homeserver in earlier requests
- *
- * onComplete: a callback which will be called with the results of the request
- */
- function unknownLoginType(homeserverUrl, apiEndpoint, loginType, sessionID, onComplete) {
- var popupWindow;
-
- var eventListener = function(ev) {
- // check it's the right message from the right place.
- if (ev.data !== "authDone" || ev.origin !== homeserverUrl) {
- return;
- }
-
- // close the popup
- popupWindow.close();
- window.removeEventListener("message", eventListener);
-
- // repeat the request
- var requestBody = {
- auth: {
- session: sessionID,
- },
- };
-
- request({
- method:'POST', url:apiEndpint, json:requestBody,
- }, onComplete);
- };
-
- window.addEventListener("message", eventListener);
-
- var url = homeserverUrl +
- "/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/" +
- encodeURIComponent(loginType) +
- "/fallback/web?session=" +
- encodeURIComponent(sessionID);
-
-
- popupWindow = window.open(url);
- }
-
-
-Identifier types
-++++++++++++++++
-
-Some authentication mechanisms use a user identifier object to identify a
-user. The user identifier object has a ``type`` field to indicate the type of
-identifier being used, and depending on the type, has other fields giving the
-information required to identify the user as described below.
-
-This specification defines the following identifier types:
- - ``m.id.user``
- - ``m.id.thirdparty``
- - ``m.id.phone``
-
-Matrix User ID
-<<<<<<<<<<<<<<
-:Type:
- ``m.id.user``
-:Description:
- The user is identified by their Matrix ID.
-
-A client can identify a user using their Matrix ID. This can either be the
-fully qualified Matrix user ID, or just the localpart of the user ID.
-
-.. code:: json
-
- "identifier": {
- "type": "m.id.user",
- "user": ""
- }
-
-Third-party ID
-<<<<<<<<<<<<<<
-:Type:
- ``m.id.thirdparty``
-:Description:
- The user is identified by a third-party identifier in canonicalised form.
-
-A client can identify a user using a 3PID associated with the user's account on
-the homeserver, where the 3PID was previously associated using the
-|/account/3pid|_ API. See the `3PID Types`_ Appendix for a list of Third-party
-ID media.
-
-.. code:: json
-
- "identifier": {
- "type": "m.id.thirdparty",
- "medium": "",
- "address": ""
- }
-
-Phone number
-<<<<<<<<<<<<
-:Type:
- ``m.id.phone``
-:Description:
- The user is identified by a phone number.
-
-A client can identify a user using a phone number associated with the user's
-account, where the phone number was previously associated using the
-|/account/3pid|_ API. The phone number can be passed in as entered by the
-user; the homeserver will be responsible for canonicalising it. If the client
-wishes to canonicalise the phone number, then it can use the
-``m.id.thirdparty`` identifier type with a ``medium`` of ``msisdn`` instead.
-
-.. code:: json
-
- "identifier": {
- "type": "m.id.phone",
- "country": "",
- "phone": ""
- }
-
-The ``country`` is the two-letter uppercase ISO-3166-1 alpha-2 country code
-that the number in ``phone`` should be parsed as if it were dialled from.
-
-Login
-~~~~~
-
-A client can obtain access tokens using the ``/login`` API.
-
-Note that this endpoint does `not` currently use the `User-Interactive Authentication API`_.
-
-For a simple username/password login, clients should submit a ``/login``
-request as follows:
-
-.. code:: json
-
- {
- "type": "m.login.password",
- "identifier": {
- "type": "m.id.user",
- "user": ""
- },
- "password": ""
- }
-
-Alternatively, a client can use a 3PID bound to the user's account on the
-homeserver using the |/account/3pid|_ API rather then giving the ``user``
-explicitly, as follows:
-
-.. code:: json
-
- {
- "type": "m.login.password",
- "identifier": {
- "medium": "",
- "address": ""
- },
- "password": ""
- }
-
-In the case that the homeserver does not know about the supplied 3PID, the
-homeserver must respond with ``403 Forbidden``.
-
-To log in using a login token, clients should submit a ``/login`` request as
-follows:
-
-.. code:: json
-
- {
- "type": "m.login.token",
- "token": ""
- }
-
-As with `token-based`_ interactive login, the ``token`` must encode the
-user ID. In the case that the token is not valid, the homeserver must respond
-with ``403 Forbidden`` and an error code of ``M_FORBIDDEN``.
-
-If the homeserver advertises ``m.login.sso`` as a viable flow, and the client
-supports it, the client should redirect the user to the ``/redirect`` endpoint
-for `client login via SSO`_. After authentication is complete, the
-client will need to submit a ``/login`` request matching ``m.login.token``.
-
-{{login_cs_http_api}}
-
-{{logout_cs_http_api}}
-
-Login Fallback
-<<<<<<<<<<<<<<
-
-If a client does not recognize any or all login flows it can use the fallback
-login API::
-
- GET /_matrix/static/client/login/
-
-This returns an HTML and JavaScript page which can perform the entire login
-process. The page will attempt to call the JavaScript function
-``window.onLogin`` when login has been successfully completed.
-
-Non-credential parameters valid for the ``/login`` endpoint can be provided as query
-string parameters here. These are to be forwarded to the login endpoint during the login
-process. For example::
-
- GET /_matrix/static/client/login/?device_id=GHTYAJCE
-
-.. _Registration:
-
-Account registration and management
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-{{registration_cs_http_api}}
-
-Notes on password management
-++++++++++++++++++++++++++++
-
-.. WARNING::
- Clients SHOULD enforce that the password provided is suitably complex. The
- password SHOULD include a lower-case letter, an upper-case letter, a number
- and a symbol and be at a minimum 8 characters in length. Servers MAY reject
- weak passwords with an error code ``M_WEAK_PASSWORD``.
-
-
-Adding Account Administrative Contact Information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A homeserver may keep some contact information for administrative use.
-This is independent of any information kept by any identity servers, though
-can be proxied (bound) to the identity server in many cases.
-
-.. Note::
- This section deals with two terms: "add" and "bind". Where "add" (or "remove")
- is used, it is speaking about an identifier that was not bound to an identity
- server. As a result, "bind" (or "unbind") references an identifier that is found
- in an identity server. Note that an identifer can be added and bound at the same
- time, depending on context.
-
-{{administrative_contact_cs_http_api}}
-
-Current account information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-{{whoami_cs_http_api}}
-
-Notes on identity servers
-+++++++++++++++++++++++++
-
-Identity servers in Matrix store bindings (relationships) between a user's third
-party identifier, typically email or phone number, and their user ID. Once a user
-has chosen an identity server, that identity server should be used by all clients.
-
-Clients can see which identity server the user has chosen through the ``m.identity_server``
-account data event, as described below. Clients SHOULD refrain from making requests
-to any identity server until the presence of ``m.identity_server`` is confirmed as
-(not) present. If present, the client SHOULD check for the presence of the ``base_url``
-property in the event's content. If the ``base_url`` is present, the client SHOULD
-use the identity server in that property as the identity server for the user. If the
-``base_url`` is missing, or the account data event is not present, the client SHOULD
-use whichever default value it normally would for an identity server, if applicable.
-Clients SHOULD NOT update the account data with the default identity server when the
-user is missing an identity server in their account data.
-
-Clients SHOULD listen for changes to the ``m.identity_server`` account data event
-and update the identity server they are contacting as a result.
-
-If the client offers a way to set the identity server to use, it MUST update the
-value of ``m.identity_server`` accordingly. A ``base_url`` of ``null`` MUST be
-treated as though the user does not want to use an identity server, disabling all
-related functionality as a result.
-
-Clients SHOULD refrain from populating the account data as a migration step for users
-who are lacking the account data, unless the user sets the identity server within
-the client to a value. For example, a user which has no ``m.identity_server`` account
-data event should not end up with the client's default identity server in their
-account data, unless the user first visits their account settings to set the identity
-server.
-
-{{m_identity_server_event}}
-
-Capabilities negotiation
-------------------------
-
-A homeserver may not support certain operations and clients must be able to
-query for what the homeserver can and can't offer. For example, a homeserver
-may not support users changing their password as it is configured to perform
-authentication against an external system.
-
-The capabilities advertised through this system are intended to advertise
-functionality which is optional in the API, or which depend in some way on
-the state of the user or server. This system should not be used to advertise
-unstable or experimental features - this is better done by the ``/versions``
-endpoint.
-
-Some examples of what a reasonable capability could be are:
-
-* Whether the server supports user presence.
-
-* Whether the server supports optional features, such as the user or room
- directories.
-
-* The rate limits or file type restrictions imposed on clients by the server.
-
-Some examples of what should **not** be a capability are:
-
-* Whether the server supports a feature in the ``unstable`` specification.
-
-* Media size limits - these are handled by the ``/media/%CLIENT_MAJOR_VERSION%/config``
- API.
-
-* Optional encodings or alternative transports for communicating with the
- server.
-
-Capabilities prefixed with ``m.`` are reserved for definition in the Matrix
-specification while other values may be used by servers using the Java package
-naming convention. The capabilities supported by the Matrix specification are
-defined later in this section.
-
-{{capabilities_cs_http_api}}
-
-
-``m.change_password`` capability
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This capability has a single flag, ``enabled``, which indicates whether or not
-the user can use the ``/account/password`` API to change their password. If not
-present, the client should assume that password changes are possible via the
-API. When present, clients SHOULD respect the capability's ``enabled`` flag
-and indicate to the user if they are unable to change their password.
-
-An example of the capability API's response for this capability is::
-
- {
- "capabilities": {
- "m.change_password": {
- "enabled": false
- }
- }
- }
-
-
-``m.room_versions`` capability
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This capability describes the default and available room versions a server
-supports, and at what level of stability. Clients should make use of this
-capability to determine if users need to be encouraged to upgrade their rooms.
-
-An example of the capability API's response for this capability is::
-
- {
- "capabilities": {
- "m.room_versions": {
- "default": "1",
- "available": {
- "1": "stable",
- "2": "stable",
- "3": "unstable",
- "custom-version": "unstable"
- }
- }
- }
- }
-
-This capability mirrors the same restrictions of `room versions`_ to describe
-which versions are stable and unstable. Clients should assume that the ``default``
-version is ``stable``. Any version not explicitly labelled as ``stable`` in the
-``available`` versions is to be treated as ``unstable``. For example, a version
-listed as ``future-stable`` should be treated as ``unstable``.
-
-The ``default`` version is the version the server is using to create new rooms.
-Clients should encourage users with sufficient permissions in a room to upgrade
-their room to the ``default`` version when the room is using an ``unstable``
-version.
-
-When this capability is not listed, clients should use ``"1"`` as the default
-and only stable ``available`` room version.
-
-.. _`room versions`: ../index.html#room-versions
-
-
-Pagination
-----------
-
-.. NOTE::
- The paths referred to in this section are not actual endpoints. They only
- serve as examples to explain how pagination functions.
-
-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 `start` or `prev_batch`
-for retrieving the previous result set, or `end`, `next_batch` or `next_token`
-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 -> 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 -> 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 -> 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.
-
-.. _`filter`:
-
-Filtering
----------
-
-Filters can be created on the server and can be passed as a parameter to APIs
-which return events. These filters alter the data returned from those APIs.
-Not all APIs accept filters.
-
-Lazy-loading room members
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Membership events often take significant resources for clients to track. In an
-effort to reduce the number of resources used, clients can enable "lazy-loading"
-for room members. By doing this, servers will attempt to only send membership events
-which are relevant to the client.
-
-It is important to understand that lazy-loading is not intended to be a
-perfect optimisation, and that it may not be practical for the server to
-calculate precisely which membership events are relevant to the client. As a
-result, it is valid for the server to send redundant membership events to the
-client to ease implementation, although such redundancy should be minimised
-where possible to conserve bandwidth.
-
-In terms of filters, lazy-loading is enabled by enabling ``lazy_load_members``
-on a ``RoomEventFilter`` (or a ``StateFilter`` in the case of ``/sync`` only).
-When enabled, lazy-loading aware endpoints (see below) will only include
-membership events for the ``sender`` of events being included in the response.
-For example, if a client makes a ``/sync`` request with lazy-loading enabled,
-the server will only return membership events for the ``sender`` of events in
-the timeline, not all members of a room.
-
-When processing a sequence of events (e.g. by looping on ``/sync`` or
-paginating ``/messages``), it is common for blocks of events in the sequence
-to share a similar set of senders. Rather than responses in the sequence
-sending duplicate membership events for these senders to the client, the
-server MAY assume that clients will remember membership events they have
-already been sent, and choose to skip sending membership events for members
-whose membership has not changed. These are called 'redundant membership
-events'. Clients may request that redundant membership events are always
-included in responses by setting ``include_redundant_members`` to true in the
-filter.
-
-The expected pattern for using lazy-loading is currently:
-
-* Client performs an initial /sync with lazy-loading enabled, and receives
- only the membership events which relate to the senders of the events it
- receives.
-* Clients which support display-name tab-completion or other operations which
- require rapid access to all members in a room should call /members for the
- currently selected room, with an ``?at`` parameter set to the /sync
- response's from token. The member list for the room is then maintained by
- the state in subsequent incremental /sync responses.
-* Clients which do not support tab-completion may instead pull in profiles for
- arbitrary users (e.g. read receipts, typing notifications) on demand by
- querying the room state or ``/profile``.
-
-.. TODO-spec
- This implies that GET /state should also take an ``?at`` param
-
-The current endpoints which support lazy-loading room members are:
-
-* |/sync|_
-* |/rooms//messages|_
-* |/rooms/{roomId}/context/{eventId}|_
-
-API endpoints
-~~~~~~~~~~~~~
-
-{{filter_cs_http_api}}
-
-Events
-------
-
-.. _sect:events:
-
-The model of conversation history exposed by the client-server API can be
-considered as a list of events. The server 'linearises' the
-eventually-consistent event graph of events into an 'event stream' at any given
-point in time::
-
- [E0]->[E1]->[E2]->[E3]->[E4]->[E5]
-
-.. WARNING::
-
- The format of events can change depending on room version. Check the
- `room version specification`_ for specific details on what to expect for
- event formats. Examples contained within the client-server specification
- are expected to be compatible with all specified room versions, however
- some differences may still apply.
-
- For this version of the specification, clients only need to worry about
- the event ID format being different depending on room version. Clients
- should not be parsing the event ID, and instead be treating it as an
- opaque string. No changes should be required to support the currently
- available room versions.
-
-.. _`room version specification`: ../index.html#room-versions
-
-Types of room events
-~~~~~~~~~~~~~~~~~~~~
-
-Room events are split into two categories:
-
-:State Events:
- These are events which update the metadata state of the room (e.g. room topic,
- room membership etc). State is keyed by a tuple of event ``type`` and a
- ``state_key``. State in the room with the same key-tuple will be overwritten.
-
-:Message events:
- These are events which describe transient "once-off" activity in a room:
- typically communication such as sending an instant message or setting up a
- VoIP call.
-
-This specification outlines several events, all with the event type prefix
-``m.``. (See `Room Events`_ for the m. event specification.) However,
-applications may wish to add their own type of event, and this can be achieved
-using the REST API detailed in the following sections. If new events are added,
-the event ``type`` key SHOULD follow the Java package naming convention,
-e.g. ``com.example.myapp.event``. This ensures event types are suitably
-namespaced for each application and reduces the risk of clashes.
-
-.. Note::
- Events are not limited to the types defined in this specification. New or custom
- event types can be created on a whim using the Java package naming convention.
- For example, a ``com.example.game.score`` event can be sent by clients and other
- clients would receive it through Matrix, assuming the client has access to the
- ``com.example`` namespace.
-
-Note that the structure of these events may be different than those in the
-server-server API.
-
-{{common_event_fields}}
-
-{{common_room_event_fields}}
-
-.. This is normally where we'd put the common_state_event_fields variable for the
-.. magic table of what makes up a state event, however the table is verbose in our
-.. custom rendering of swagger. To combat this, we just hardcode this particular
-.. table.
-
-State Event Fields
-++++++++++++++++++
-
-In addition to the fields of a Room Event, State Events have the following fields.
-
-+--------------+--------------+-------------------------------------------------------------+
-| Key | Type | Description |
-+==============+==============+=============================================================+
-| state_key | string | **Required.** A unique key which defines the overwriting |
-| | | semantics for this piece of room state. This value is often |
-| | | a zero-length string. The presence of this key makes this |
-| | | event a State Event. State keys starting with an ``@`` are |
-| | | reserved for referencing user IDs, such as room members. |
-| | | With the exception of a few events, state events set with |
-| | | a given user's ID as the state key MUST only be set by that |
-| | | user. |
-+--------------+--------------+-------------------------------------------------------------+
-| prev_content | EventContent | Optional. The previous ``content`` for this event. If there |
-| | | is no previous content, this key will be missing. |
-+--------------+--------------+-------------------------------------------------------------+
-
-
-Size limits
-~~~~~~~~~~~
-
-The complete event MUST NOT be larger than 65535 bytes, when formatted as a
-`PDU for the Server-Server protocol <../server_server/%SERVER_RELEASE_LABEL%#pdus>`_,
-including any signatures, and encoded as `Canonical JSON`_.
-
-There are additional restrictions on sizes per key:
-
-- ``sender`` MUST NOT exceed 255 bytes (including domain).
-- ``room_id`` MUST NOT exceed 255 bytes.
-- ``state_key`` MUST NOT exceed 255 bytes.
-- ``type`` MUST NOT exceed 255 bytes.
-- ``event_id`` MUST NOT exceed 255 bytes.
-
-Some event types have additional size restrictions which are specified in
-the description of the event. Additional keys have no limit other than that
-implied by the total 65 KB limit on events.
-
-.. _`Canonical JSON`: ../appendices.html#canonical-json
-
-Room Events
-~~~~~~~~~~~
-.. NOTE::
- This section is a work in progress.
-
-This specification outlines several standard event types, all of which are
-prefixed with ``m.``
-
-{{m_room_canonical_alias_event}}
-
-{{m_room_create_event}}
-
-{{m_room_join_rules_event}}
-
-{{m_room_member_event}}
-
-{{m_room_power_levels_event}}
-
-{{m_room_redaction_event}}
-
-Historical events
-+++++++++++++++++
-
-Some events within the ``m.`` namespace might appear in rooms, however they
-serve no significant meaning in this version of the specification. They are:
-
-* ``m.room.aliases``
-
-Previous versions of the specification have more information on these events.
-
-Syncing
-~~~~~~~
-
-To read events, the intended flow of operation is for clients to first call the
-|/sync|_ API without a ``since`` parameter. This returns the most recent
-message events for each room, as well as the state of the room at the start of
-the returned timeline. The response also includes a ``next_batch`` field, which
-should be used as the value of the ``since`` parameter in the next call to
-``/sync``. Finally, the response includes, for each room, a ``prev_batch``
-field, which can be passed as a ``start`` parameter to the
-|/rooms//messages|_ API to retrieve earlier messages.
-
-You can visualise the range of events being returned as::
-
- [E0]->[E1]->[E2]->[E3]->[E4]->[E5]
- ^ ^
- | |
- prev_batch: '1-2-3' next_batch: 'a-b-c'
-
-
-Clients then receive new events by "long-polling" the homeserver via the
-``/sync`` API, passing the value of the ``next_batch`` field from the response
-to the previous call as the ``since`` parameter. The client should also pass a
-``timeout`` parameter. The server will then hold open the HTTP connection for a
-short period of time waiting for new events, returning early if an event
-occurs. Only the ``/sync`` API (and the deprecated ``/events`` API) support
-long-polling in this way.
-
-The response for such an incremental sync can be visualised as::
-
- [E0]->[E1]->[E2]->[E3]->[E4]->[E5]->[E6]
- ^ ^
- | |
- | next_batch: 'x-y-z'
- prev_batch: 'a-b-c'
-
-
-Normally, all new events which are visible to the client will appear in the
-response to the ``/sync`` API. However, if a large number of events arrive
-between calls to ``/sync``, a "limited" timeline is returned, containing only
-the most recent message events. A state "delta" is also returned, summarising
-any state changes in the omitted part of the timeline. The client may therefore
-end up with "gaps" in its knowledge of the message timeline. The client can
-fill these gaps using the |/rooms//messages|_ API. This situation
-looks like this::
-
- | gap |
- | <-> |
- [E0]->[E1]->[E2]->[E3]->[E4]->[E5]->[E6]->[E7]->[E8]->[E9]->[E10]
- ^ ^
- | |
- prev_batch: 'd-e-f' next_batch: 'u-v-w'
-
-
-.. Warning::
- Events are ordered in this API according to the arrival time of the event on
- the homeserver. This can conflict with other APIs which order events based on
- their partial ordering in the event graph. This can result in duplicate events
- being received (once per distinct API called). Clients SHOULD de-duplicate
- events based on the event ID when this happens.
-
-.. NOTE::
-
- The ``/sync`` API returns a ``state`` list which is separate from the
- ``timeline``. This ``state`` list allows clients to keep their model of the
- room state in sync with that on the server. In the case of an initial
- (``since``-less) sync, the ``state`` list represents the complete state of
- the room at the **start** of the returned timeline (so in the case of a
- recently-created room whose state fits entirely in the ``timeline``, the
- ``state`` list will be empty).
-
- In the case of an incremental sync, the ``state`` list gives a delta
- between the state of the room at the ``since`` parameter and that at the
- start of the returned ``timeline``. (It will therefore be empty
- unless the timeline was ``limited``.)
-
- In both cases, it should be noted that the events returned in the ``state``
- list did **not** necessarily take place just before the returned
- ``timeline``, so clients should not display them to the user in the timeline.
-
-.. admonition:: Rationale
-
- An early design of this specification made the ``state`` list represent the
- room state at the end of the returned timeline, instead of the start. This
- was unsatisfactory because it led to duplication of events between the
- ``state`` list and the ``timeline``, but more importantly, it made it
- difficult for clients to show the timeline correctly.
-
- In particular, consider a returned timeline [M0, S1, M2], where M0 and M2 are
- both messages sent by the same user, and S1 is a state event where that user
- changes their displayname. If the ``state`` list represents the room state at
- the end of the timeline, the client must take a copy of the state dictionary,
- and *rewind* S1, in order to correctly calculate the display name for M0.
-
-.. TODO-spec
- Do we ever support streaming requests? Why not websockets?
-
-{{sync_cs_http_api}}
-
-{{old_sync_cs_http_api}}
-
-
-Getting events for a room
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-There are several APIs provided to ``GET`` events for a room:
-
-{{rooms_cs_http_api}}
-
-{{message_pagination_cs_http_api}}
-
-{{room_initial_sync_cs_http_api}}
-
-
-Sending events to a room
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-{{room_state_cs_http_api}}
-
-
-**Examples**
-
-Valid requests look like::
-
- PUT /rooms/!roomid:domain/state/m.example.event
- { "key" : "without a state key" }
-
- PUT /rooms/!roomid:domain/state/m.another.example.event/foo
- { "key" : "with 'foo' as the state key" }
-
-In contrast, these requests are invalid::
-
- POST /rooms/!roomid:domain/state/m.example.event/
- { "key" : "cannot use POST here" }
-
- PUT /rooms/!roomid:domain/state/m.another.example.event/foo/11
- { "key" : "txnIds are not supported" }
-
-Care should be taken to avoid setting the wrong ``state key``::
-
- PUT /rooms/!roomid:domain/state/m.another.example.event/11
- { "key" : "with '11' as the state key, but was probably intended to be a txnId" }
-
-The ``state_key`` is often used to store state about individual users, by using
-the user ID as the ``state_key`` value. For example::
-
- PUT /rooms/!roomid:domain/state/m.favorite.animal.event/%40my_user%3Aexample.org
- { "animal" : "cat", "reason": "fluffy" }
-
-In some cases, there may be no need for a ``state_key``, so it can be omitted::
-
- PUT /rooms/!roomid:domain/state/m.room.bgd.color
- { "color": "red", "hex": "#ff0000" }
-
-{{room_send_cs_http_api}}
-
-
-Redactions
-~~~~~~~~~~
-Since events are extensible it is possible for malicious users and/or servers
-to add keys that are, for example offensive or illegal. Since some events
-cannot be simply deleted, e.g. membership events, we instead 'redact' events.
-This involves removing all keys from an event that are not required by the
-protocol. This stripped down event is thereafter returned anytime a client or
-remote server requests it. Redacting an event cannot be undone, allowing server
-owners to delete the offending content from the databases. Events that have been
-redacted include a ``redacted_because`` key whose value is the event that caused
-it to be redacted, which may include a reason.
-
-
-The exact algorithm to apply against an event is defined in the `room version specification`_.
-
-The server should add the event causing the redaction to the ``unsigned``
-property of the redacted event, under the ``redacted_because`` key. When a
-client receives a redaction event it should change the redacted event in the
-same way a server does.
-
-.. NOTE::
-
- Redacted events can still affect the state of the room. When redacted,
- state events behave as though their properties were simply not specified,
- except those protected by the redaction algorithm. For example,
- a redacted ``join`` event will still result in the user being considered joined.
- Similarly, a redacted topic does not necessarily cause the topic to revert to
- what is was prior to the event - it causes the topic to be removed from the room.
-
-
-Events
-++++++
-
-{{m_room_redaction_event}}
-
-Client behaviour
-++++++++++++++++
-
-{{redaction_cs_http_api}}
-
-Rooms
------
-
-Creation
-~~~~~~~~
-The homeserver will create an ``m.room.create`` event when a room is created,
-which serves as the root of the event graph for this room. This event also has a
-``creator`` key which contains the user ID of the room creator. It will also
-generate several other events in order to manage permissions in this room. This
-includes:
-
-- ``m.room.power_levels`` : Sets the power levels of users and required power
- levels for various actions within the room such as sending events.
-- ``m.room.join_rules`` : Whether the room is "invite-only" or not.
-
-See `Room Events`_ for more information on these events. To create a room, a
-client has to use the following API.
-
-{{create_room_cs_http_api}}
-
-Room aliases
-~~~~~~~~~~~~
-
-Servers may host aliases for rooms with human-friendly names. Aliases take the
-form ``#friendlyname:server.name``.
-
-As room aliases are scoped to a particular homeserver domain name, it is
-likely that a homeserver will reject attempts to maintain aliases on other
-domain names. This specification does not provide a way for homeservers to
-send update requests to other servers. However, homeservers MUST handle
-``GET`` requests to resolve aliases on other servers; they should do this using
-the federation API if necessary.
-
-Rooms do not store a list of all aliases present on a room, though members
-of the room with relevant permissions may publish preferred aliases through
-the ``m.room.canonical_alias`` state event. The aliases in the state event
-should point to the room ID they are published within, however room aliases
-can and do drift to other room IDs over time. Clients SHOULD NOT treat the
-aliases as accurate. They SHOULD be checked before they are used or shared
-with another user. If a room appears to have a room alias of ``#alias:example.com``,
-this SHOULD be checked to make sure that the room's ID matches the ``room_id``
-returned from the request.
-
-{{directory_cs_http_api}}
-
-
-Permissions
-~~~~~~~~~~~
-.. NOTE::
- This section is a work in progress.
-
-Permissions for rooms are done via the concept of power levels - to do any
-action in a room a user must have a suitable power level. Power levels are
-stored as state events in a given room. The power levels required for operations
-and the power levels for users are defined in ``m.room.power_levels``, where
-both a default and specific users' power levels can be set.
-By default all users have a power level of 0, other than the room creator whose
-power level defaults to 100. Users can grant other users increased power levels
-up to their own power level. For example, user A with a power level of 50 could
-increase the power level of user B to a maximum of level 50. Power levels for
-users are tracked per-room even if the user is not present in the room.
-The keys contained in ``m.room.power_levels`` determine the levels required for
-certain operations such as kicking, banning and sending state events. See
-`m.room.power_levels`_ for more information.
-
-Clients may wish to assign names to particular power levels. A suggested mapping is as follows:
-- 0 User
-- 50 Moderator
-- 100 Admin
-
-
-Room membership
-~~~~~~~~~~~~~~~
-Users need to be a member of a room in order to send and receive events in that
-room. There are several states in which a user may be, in relation to a room:
-
-- Unrelated (the user cannot send or receive events in the room)
-- Invited (the user has been invited to participate in the room, but is not
- yet participating)
-- Joined (the user can send and receive events in the room)
-- Banned (the user is not allowed to join the room)
-
-There is an exception to the requirement that a user join a room before sending
-events to it: users may send an ``m.room.member`` event to a room with
-``content.membership`` set to ``leave`` to reject an invitation if they have
-currently been invited to a room but have not joined it.
-
-Some rooms require that users be invited to it before they can join; others
-allow anyone to join. Whether a given room is an "invite-only" room is
-determined by the room config key ``m.room.join_rules``. It can have one of the
-following values:
-
-``public``
- This room is free for anyone to join without an invite.
-
-``invite``
- This room can only be joined if you were invited.
-
-The allowable state transitions of membership are::
-
- /ban
- +------------------------------------------------------+
- | |
- | +----------------+ +----------------+ |
- | | /leave | | | |
- | | v v | |
- /invite +--------+ +-------+ | |
- ------------>| invite |<----------| leave |----+ | |
- +--------+ /invite +-------+ | | |
- | | ^ | | |
- | | | | | |
- /join | +---------------+ | | | |
- | | /join if | | | |
- | | join_rules | | /ban | /unban |
- | | public /leave | | | |
- v v or | | | |
- +------+ /kick | | | |
- ------------>| join |-------------------+ | | |
- /join +------+ v | |
- if | +-----+ | |
- join_rules +-------------------------->| ban |-----+ |
- public /ban +-----+ |
- ^ ^ |
- | | |
- ----------------------------------------------+ +----------------------+
- /ban
-
-{{list_joined_rooms_cs_http_api}}
-
-Joining rooms
-+++++++++++++
-
-{{inviting_cs_http_api}}
-
-{{joining_cs_http_api}}
-
-Leaving rooms
-+++++++++++++
-A user can leave a room to stop receiving events for that room. A user must
-have been invited to or have joined the room before they are eligible to leave
-the room. Leaving a room to which the user has been invited rejects the invite.
-Once a user leaves a room, it will no longer appear in the response to the
-|/sync|_ API unless it is explicitly requested via a filter with the
-``include_leave`` field set to ``true``.
-
-Whether or not they actually joined the room, if the room is an "invite-only"
-room the user will need to be re-invited before they can re-join the room.
-
-A user can also forget a room which they have left. Rooms which have been
-forgotten will never appear the response to the |/sync|_ API, until the user
-re-joins or is re-invited.
-
-A user may wish to force another user to leave a room. This can be done by
-'kicking' the other user. To do so, the user performing the kick MUST have the
-required power level. Once a user has been kicked, the behaviour is the same as
-if they had left of their own accord. In particular, the user is free to
-re-join if the room is not "invite-only".
-
-{{leaving_cs_http_api}}
-
-{{kicking_cs_http_api}}
-
-Banning users in a room
-+++++++++++++++++++++++
-A user may decide to ban another user in a room. 'Banning' forces the target
-user to leave the room and prevents them from re-joining the room. A banned
-user will not be treated as a joined user, and so will not be able to send or
-receive events in the room. In order to ban someone, the user performing the
-ban MUST have the required power level. To ban a user, a request should be made
-to |/rooms//ban|_ with::
-
- {
- "user_id": ""
- "reason": "string: "
- }
-
-Banning a user adjusts the banned member's membership state to ``ban``.
-Like with other membership changes, a user can directly adjust the target
-member's state, by making a request to
-``/rooms//state/m.room.member/``::
-
- {
- "membership": "ban"
- }
-
-A user must be explicitly unbanned with a request to |/rooms//unban|_
-before they can re-join the room or be re-invited.
-
-{{banning_cs_http_api}}
-
-
-
-Listing rooms
-~~~~~~~~~~~~~
-
-{{list_public_rooms_cs_http_api}}
-
-
-User Data
----------
-
-User Directory
-~~~~~~~~~~~~~~
-
-{{users_cs_http_api}}
-
-
-Profiles
-~~~~~~~~
-
-{{profile_cs_http_api}}
-
-Events on Change of Profile Information
-+++++++++++++++++++++++++++++++++++++++
-Because the profile display name and avatar information are likely to be used in
-many places of a client's display, changes to these fields cause an automatic
-propagation event to occur, informing likely-interested parties of the new
-values. This change is conveyed using two separate mechanisms:
-
-- a ``m.room.member`` event (with a ``join`` membership) is sent to every room
- the user is a member of, to update the ``displayname`` and ``avatar_url``.
-- a ``m.presence`` presence status update is sent, again containing the new
- values of the ``displayname`` and ``avatar_url`` keys, in addition to the
- required ``presence`` key containing the current presence state of the user.
-
-Both of these should be done automatically by the homeserver when a user
-successfully changes their display name or avatar URL fields.
-
-Additionally, when homeservers emit room membership events for their own
-users, they should include the display name and avatar URL fields in these
-events so that clients already have these details to hand, and do not have to
-perform extra round trips to query it.
-
-Security
---------
-
-Rate limiting
-~~~~~~~~~~~~~
-Homeservers SHOULD implement rate limiting to reduce the risk of being
-overloaded. If a request is refused due to rate limiting, it should return a
-standard error response of the form::
-
- {
- "errcode": "M_LIMIT_EXCEEDED",
- "error": "string",
- "retry_after_ms": integer (optional)
- }
-
-The ``retry_after_ms`` key SHOULD be included to tell the client how long they
-have to wait in milliseconds before they can try again.
-
-.. TODO-spec
- - Surely we should recommend an algorithm for the rate limiting, rather than letting every
- homeserver come up with their own idea, causing totally unpredictable performance over
- federated rooms?
-
-.. References
-
-.. _`macaroon`: http://research.google.com/pubs/pub41892.html
-.. _`devices`: ../index.html#devices
-
-.. Links through the external API docs are below
-.. =============================================
-
-.. |/initialSync| replace:: ``/initialSync``
-.. _/initialSync: #get-matrix-client-%CLIENT_MAJOR_VERSION%-initialsync
-
-.. |/sync| replace:: ``/sync``
-.. _/sync: #get-matrix-client-%CLIENT_MAJOR_VERSION%-sync
-
-.. |/events| replace:: ``/events``
-.. _/events: #get-matrix-client-%CLIENT_MAJOR_VERSION%-events
-
-.. |/createRoom| replace:: ``/createRoom``
-.. _/createRoom: #post-matrix-client-%CLIENT_MAJOR_VERSION%-createroom
-
-.. |/rooms//initialSync| replace:: ``/rooms//initialSync``
-.. _/rooms//initialSync: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync
-
-.. |/rooms//messages| replace:: ``/rooms//messages``
-.. _/rooms//messages: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages
-
-.. |/rooms//members| replace:: ``/rooms//members``
-.. _/rooms//members: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-members
-
-.. |/rooms//state| replace:: ``/rooms//state``
-.. _/rooms//state: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state
-
-.. |/rooms//send| replace:: ``/rooms//send``
-.. _/rooms//send: #put-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-send-eventtype-txnid
-
-.. |/rooms//invite| replace:: ``/rooms//invite``
-.. _/rooms//invite: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-invite
-
-.. |/rooms//join| replace:: ``/rooms//join``
-.. _/rooms//join: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-join
-
-.. |/rooms//leave| replace:: ``/rooms//leave``
-.. _/rooms//leave: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-leave
-
-.. |/rooms//ban| replace:: ``/rooms//ban``
-.. _/rooms//ban: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-ban
-
-.. |/rooms//unban| replace:: ``/rooms//unban``
-.. _/rooms//unban: #post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-unban
-
-.. |/rooms/{roomId}/context/{eventId}| replace:: ``/rooms/{roomId}/context/{eventId}``
-.. _/rooms/{roomId}/context/{eventId}: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-context-eventid
-
-.. |/rooms/{roomId}/event/{eventId}| replace:: ``/rooms/{roomId}/event/{eventId}``
-.. _/rooms/{roomId}/event/{eventId}: #get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-event-eventid
-
-.. |/account/3pid| replace:: ``/account/3pid``
-.. _/account/3pid: #post-matrix-client-%CLIENT_MAJOR_VERSION%-account-3pid
-
-.. |/user//account_data/| replace:: ``/user//account_data/``
-.. _/user//account_data/: #put-matrix-client-%CLIENT_MAJOR_VERSION%-user-userid-account-data-type
-
-.. |/_matrix/client/versions| replace:: ``/_matrix/client/versions``
-.. _/_matrix/client/versions: #get-matrix-client-versions
-
-.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
-.. _`3PID Types`: ../appendices.html#pid-types
diff --git a/specification/feature_profiles.rst b/specification/feature_profiles.rst
deleted file mode 100644
index 8a3caf87cbb..00000000000
--- a/specification/feature_profiles.rst
+++ /dev/null
@@ -1,149 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-Feature Profiles
-================
-
-.. _sect:feature-profiles:
-
-Matrix supports many different kinds of clients: from embedded IoT devices to
-desktop clients. Not all clients can provide the same feature sets as other
-clients e.g. due to lack of physical hardware such as not having a screen.
-Clients can fall into one of several profiles and each profile contains a set
-of features that the client MUST support. This section details a set of
-"feature profiles". Clients are expected to implement a profile in its entirety
-in order for it to be classified as that profile.
-
-Summary
--------
-
-===================================== ========== ========== ========== ========== ==========
- Module / Profile Web Mobile Desktop CLI Embedded
-===================================== ========== ========== ========== ========== ==========
- `Instant Messaging`_ Required Required Required Required Optional
- `Direct Messaging`_ Required Required Required Required Optional
- `Mentions`_ Required Required Required Optional Optional
- `Presence`_ Required Required Required Required Optional
- `Push Notifications`_ Optional Required Optional Optional Optional
- `Receipts`_ Required Required Required Required Optional
- `Fully read markers`_ Optional Optional Optional Optional Optional
- `Typing Notifications`_ Required Required Required Required Optional
- `VoIP`_ Required Required Required Optional Optional
- `Ignoring Users`_ Required Required Required Optional Optional
- `Reporting Content`_ Optional Optional Optional Optional Optional
- `Content Repository`_ Required Required Required Optional Optional
- `Managing History Visibility`_ Required Required Required Required Optional
- `Server Side Search`_ Optional Optional Optional Optional Optional
- `Room Upgrades`_ Required Required Required Required Optional
- `Server Administration`_ Optional Optional Optional Optional Optional
- `Event Context`_ Optional Optional Optional Optional Optional
- `Third Party Networks`_ Optional Optional Optional Optional Optional
- `Send-to-Device Messaging`_ Optional Optional Optional Optional Optional
- `Device Management`_ Optional Optional Optional Optional Optional
- `End-to-End Encryption`_ Optional Optional Optional Optional Optional
- `Guest Accounts`_ Optional Optional Optional Optional Optional
- `Room Previews`_ Optional Optional Optional Optional Optional
- `Client Config`_ Optional Optional Optional Optional Optional
- `SSO Login`_ Optional Optional Optional Optional Optional
- `OpenID`_ Optional Optional Optional Optional Optional
- `Stickers`_ Optional Optional Optional Optional Optional
- `Server ACLs`_ Optional Optional Optional Optional Optional
- `Server Notices`_ Optional Optional Optional Optional Optional
- `Moderation policies`_ Optional Optional Optional Optional Optional
-===================================== ========== ========== ========== ========== ==========
-
-*Please see each module for more details on what clients need to implement.*
-
-.. _Instant Messaging: `module:im`_
-.. _Direct Messaging: `module:dm`_
-.. _Mentions: `module:mentions`_
-.. _Presence: `module:presence`_
-.. _Push Notifications: `module:push`_
-.. _Receipts: `module:receipts`_
-.. _Fully read markers: `module:read-markers`_
-.. _Typing Notifications: `module:typing`_
-.. _VoIP: `module:voip`_
-.. _Ignoring Users: `module:ignore_users`_
-.. _Reporting Content: `module:report_content`_
-.. _Content Repository: `module:content`_
-.. _Managing History Visibility: `module:history-visibility`_
-.. _Server Side Search: `module:search`_
-.. _Room Upgrades: `module:room-upgrades`_
-.. _Server Administration: `module:admin`_
-.. _Event Context: `module:event-context`_
-.. _Third Party Networks: `module:third-party-networks`_
-.. _Send-to-Device Messaging: `module:to_device`_
-.. _Device Management: `module:device-management`_
-.. _End-to-End Encryption: `module:e2e`_
-.. _Guest Accounts: `module:guest-access`_
-.. _Room Previews: `module:room-previews`_
-.. _Client Config: `module:account_data`_
-.. _SSO Login: `module:sso_login`_
-.. _OpenID: `module:openid`_
-.. _Stickers: `module:stickers`_
-.. _Server ACLs: `module:server-acls`_
-.. Server Notices already has a link elsewhere.
-.. _Moderation Policies: `module:moderation-policies`_
-
-Clients
--------
-
-Stand-alone web (``Web``)
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This is a web page which heavily uses Matrix for communication. Single-page web
-apps would be classified as a stand-alone web client, as would multi-page web
-apps which use Matrix on nearly every page.
-
-Mobile (``Mobile``)
-~~~~~~~~~~~~~~~~~~~
-
-This is a Matrix client specifically designed for consumption on mobile devices.
-This is typically a mobile app but need not be so provided the feature set can
-be reached (e.g. if a mobile site could display push notifications it could be
-classified as a mobile client).
-
-Desktop (``Desktop``)
-~~~~~~~~~~~~~~~~~~~~~
-
-This is a native GUI application which can run in its own environment outside a
-browser.
-
-Command Line Interface (``CLI``)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This is a client which is used via a text-based terminal.
-
-Embedded (``Embedded``)
-~~~~~~~~~~~~~~~~~~~~~~~
-
-This is a client which is embedded into another application or an embedded
-device.
-
-Application
-+++++++++++
-
-This is a Matrix client which is embedded in another website, e.g. using
-iframes. These embedded clients are typically for a single purpose
-related to the website in question, and are not intended to be fully-fledged
-communication apps.
-
-Device
-++++++
-
-This is a client which is typically running on an embedded device such as a
-kettle, fridge or car. These clients tend to perform a few operations and run
-in a resource constrained environment. Like embedded applications, they are
-not intended to be fully-fledged communication systems.
diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst
deleted file mode 100644
index 704f763e8a3..00000000000
--- a/specification/identity_service_api.rst
+++ /dev/null
@@ -1,499 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2017 Kamax.io
-.. Copyright 2017 New Vector Ltd
-.. Copyright 2018 New Vector Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Identity Service API
-====================
-
-{{unstable_warning_block_IDENTITY_RELEASE_LABEL}}
-
-The Matrix client-server and server-server APIs are largely expressed in Matrix
-user identifiers. From time to time, it is useful to refer to users by other
-("third-party") identifiers, or "3PID"s, e.g. their email address or phone
-number. This Identity Service Specification describes how mappings between
-third-party identifiers and Matrix user identifiers can be established,
-validated, and used. This description technically may apply to any 3PID, but in
-practice has only been applied specifically to email addresses and phone numbers.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Changelog
----------
-
-.. topic:: Version: %IDENTITY_RELEASE_LABEL%
-{{identity_service_changelog}}
-
-This version of the specification is generated from
-`matrix-doc `_ as of Git commit
-`{{git_version}} `_.
-
-For the full historical changelog, see
-https://github.com/matrix-org/matrix-doc/blob/master/changelogs/identity_service.rst
-
-
-Other versions of this specification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following other versions are also available, in reverse chronological order:
-
-- `HEAD `_: Includes all changes since the latest versioned release.
-- `r0.3.0 `_
-- `r0.2.1 `_
-- `r0.2.0 `_
-- `r0.1.0 `_
-
-General principles
-------------------
-
-The purpose of an identity server is to validate, store, and answer questions
-about the identities of users. In particular, it stores associations of the form
-"identifier X represents the same user as identifier Y", where identities may
-exist on different systems (such as email addresses, phone numbers,
-Matrix user IDs, etc).
-
-The identity server has some private-public keypairs. When asked about an
-association, it will sign details of the association with its private key.
-Clients may validate the assertions about associations by verifying the signature
-with the public key of the identity server.
-
-In general, identity servers are treated as reliable oracles. They do not
-necessarily provide evidence that they have validated associations, but claim to
-have done so. Establishing the trustworthiness of an individual identity server
-is left as an exercise for the client.
-
-3PID types are described in `3PID Types`_ Appendix.
-
-API standards
--------------
-
-The mandatory baseline for identity server communication in Matrix is exchanging
-JSON objects over HTTP APIs. HTTPS is required for communication, and all API calls
-use a Content-Type of ``application/json``. In addition, strings MUST be encoded as
-UTF-8.
-
-Any errors which occur at the Matrix API level MUST return a "standard error response".
-This is a JSON object which looks like:
-
-.. code:: json
-
- {
- "errcode": "",
- "error": ""
- }
-
-The ``error`` string will be a human-readable error message, usually a sentence
-explaining what went wrong. The ``errcode`` string will be a unique string
-which can be used to handle an error message e.g. ``M_FORBIDDEN``. There may be
-additional keys depending on the error, but the keys ``error`` and ``errcode``
-MUST always be present.
-
-Some standard error codes are below:
-
-:``M_NOT_FOUND``:
- The resource requested could not be located.
-
-:``M_MISSING_PARAMS``:
- The request was missing one or more parameters.
-
-:``M_INVALID_PARAM``:
- The request contained one or more invalid parameters.
-
-:``M_SESSION_NOT_VALIDATED``:
- The session has not been validated.
-
-:``M_NO_VALID_SESSION``:
- A session could not be located for the given parameters.
-
-:``M_SESSION_EXPIRED``:
- The session has expired and must be renewed.
-
-:``M_INVALID_EMAIL``:
- The email address provided was not valid.
-
-:``M_EMAIL_SEND_ERROR``:
- There was an error sending an email. Typically seen when attempting to verify
- ownership of a given email address.
-
-:``M_INVALID_ADDRESS``:
- The provided third party address was not valid.
-
-:``M_SEND_ERROR``:
- There was an error sending a notification. Typically seen when attempting to
- verify ownership of a given third party address.
-
-:``M_UNRECOGNIZED``:
- The request contained an unrecognised value, such as an unknown token or medium.
-
-:``M_THREEPID_IN_USE``:
- The third party identifier is already in use by another user. Typically this
- error will have an additional ``mxid`` property to indicate who owns the
- third party identifier.
-
-:``M_UNKNOWN``:
- An unknown error has occurred.
-
-Privacy
--------
-
-Identity is a privacy-sensitive issue. While the identity server exists to
-provide identity information, access should be restricted to avoid leaking
-potentially sensitive data. In particular, being able to construct large-scale
-connections between identities should be avoided. To this end, in general APIs
-should allow a 3PID to be mapped to a Matrix user identity, but not in the other
-direction (i.e. one should not be able to get all 3PIDs associated with a Matrix
-user ID, or get all 3PIDs associated with a 3PID).
-
-Version 1 API deprecation
--------------------------
-
-.. TODO: Remove this section when the v1 API is removed.
-
-As described on each of the version 1 endpoints, the v1 API is deprecated in
-favour of the v2 API described here. The major difference, with the exception
-of a few isolated cases, is that the v2 API requires authentication to ensure
-the user has given permission for the identity server to operate on their data.
-
-The v1 API is planned to be removed from the specification in a future version.
-
-Clients SHOULD attempt the v2 endpoints first, and if they receive a ``404``,
-``400``, or similar error they should try the v1 endpoint or fail the operation.
-Clients are strongly encouraged to warn the user of the risks in using the v1 API,
-if they are planning on using it.
-
-Web browser clients
--------------------
-
-It is realistic to expect that some clients will be written to be run within a web
-browser or similar environment. In these cases, the identity server should respond to
-pre-flight requests and supply Cross-Origin Resource Sharing (CORS) headers on all
-requests.
-
-When a client approaches the server with a pre-flight (OPTIONS) request, the server
-should respond with the CORS headers for that route. The recommended CORS 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
-
-Authentication
---------------
-
-Most ``v2`` endpoints in the Identity Service API require authentication in order
-to ensure that the requesting user has accepted all relevant policies and is otherwise
-permitted to make the request. The ``v1`` API (currently deprecated) does not require
-this authentication, however using ``v1`` is strongly discouraged as it will be removed
-in a future release.
-
-Identity Servers use a scheme similar to the Client-Server API's concept of access
-tokens to authenticate users. The access tokens provided by an Identity Server cannot
-be used to authenticate Client-Server API requests.
-
-An access token is provided to an endpoint in one of two ways:
-
-1. Via a query string parameter, ``access_token=TheTokenHere``.
-2. Via a request header, ``Authorization: Bearer TheTokenHere``.
-
-Clients are encouraged to the use the ``Authorization`` header where possible to prevent
-the access token being leaked in access/HTTP logs. The query string should only be used
-in cases where the ``Authorization`` header is inaccessible for the client.
-
-When credentials are required but missing or invalid, the HTTP call will return with a
-status of 401 and the error code ``M_UNAUTHORIZED``.
-
-{{v2_auth_is_http_api}}
-
-
-.. _`agree to more terms`:
-
-Terms of service
-----------------
-
-Identity Servers are encouraged to have terms of service (or similar policies) to
-ensure that users have agreed to their data being processed by the server. To facilitate
-this, an identity server can respond to almost any authenticated API endpoint with a
-HTTP 403 and the error code ``M_TERMS_NOT_SIGNED``. The error code is used to indicate
-that the user must accept new terms of service before being able to continue.
-
-All endpoints which support authentication can return the ``M_TERMS_NOT_SIGNED`` error.
-When clients receive the error, they are expected to make a call to ``GET /terms`` to
-find out what terms the server offers. The client compares this to the ``m.accepted_terms``
-account data for the user (described later) and presents the user with option to accept
-the still-missing terms of service. After the user has made their selection, if applicable,
-the client sends a request to ``POST /terms`` to indicate the user's acceptance. The
-server cannot expect that the client will send acceptance for all pending terms, and the
-client should not expect that the server will not respond with another ``M_TERMS_NOT_SIGNED``
-on their next request. The terms the user has just accepted are appended to ``m.accepted_terms``.
-
-{{m_accepted_terms_event}}
-
-{{v2_terms_is_http_api}}
-
-
-Status check
-------------
-
-{{ping_is_http_api}}
-
-{{v2_ping_is_http_api}}
-
-Key management
---------------
-
-An identity server has some long-term public-private keypairs. These are named
-in a scheme ``algorithm:identifier``, e.g. ``ed25519:0``. When signing an
-association, the standard `Signing JSON`_ algorithm applies.
-
-.. TODO: Actually allow identity servers to revoke all keys
- See: https://github.com/matrix-org/matrix-doc/issues/1633
-.. In the event of key compromise, the identity server may revoke any of its keys.
- An HTTP API is offered to get public keys, and check whether a particular key is
- valid.
-
-The identity server may also keep track of some short-term public-private
-keypairs, which may have different usage and lifetime characteristics than the
-service's long-term keys.
-
-{{pubkey_is_http_api}}
-
-{{v2_pubkey_is_http_api}}
-
-Association lookup
-------------------
-
-{{lookup_is_http_api}}
-
-{{v2_lookup_is_http_api}}
-
-Client behaviour
-~~~~~~~~~~~~~~~~
-
-.. TODO: Remove this note when v1 is removed completely
-.. Note::
- This section only covers the v2 lookup endpoint. The v1 endpoint is described
- in isolation above.
-
-Prior to performing a lookup clients SHOULD make a request to the ``/hash_details``
-endpoint to determine what algorithms the server supports (described in more detail
-below). The client then uses this information to form a ``/lookup`` request and
-receive known bindings from the server.
-
-Clients MUST support at least the ``sha256`` algorithm.
-
-Server behaviour
-~~~~~~~~~~~~~~~~
-
-.. TODO: Remove this note when v1 is removed completely
-.. Note::
- This section only covers the v2 lookup endpoint. The v1 endpoint is described
- in isolation above.
-
-Servers, upon receipt of a ``/lookup`` request, will compare the query against
-known bindings it has, hashing the identifiers it knows about as needed to
-verify exact matches to the request.
-
-Servers MUST support at least the ``sha256`` algorithm.
-
-Algorithms
-~~~~~~~~~~
-
-Some algorithms are defined as part of the specification, however other formats
-can be negotiated between the client and server using ``/hash_details``.
-
-``sha256``
-++++++++++
-
-This algorithm MUST be supported by clients and servers at a minimum. It is
-additionally the preferred algorithm for lookups.
-
-When using this algorithm, the client converts the query first into strings
-separated by spaces in the format `` ``. The ````
-is retrieved from ``/hash_details``, the ```` is typically ``email`` or
-``msisdn`` (both lowercase), and the ```` is the 3PID to search for.
-For example, if the client wanted to know about ``alice@example.org``'s bindings,
-it would first format the query as ``alice@example.org email ThePepperGoesHere``.
-
-.. admonition:: Rationale
-
- Mediums and peppers are appended to the address to prevent a common prefix
- for each 3PID, helping prevent attackers from pre-computing the internal state
- of the hash function.
-
-After formatting each query, the string is run through SHA-256 as defined by
-`RFC 4634 `_. The resulting bytes are then
-encoded using URL-Safe `Unpadded Base64`_ (similar to `room version 4's
-event ID format <../rooms/v4.html#event-ids>`_).
-
-An example set of queries when using the pepper ``matrixrocks`` would be::
-
- "alice@example.com email matrixrocks" -> "4kenr7N9drpCJ4AfalmlGQVsOn3o2RHjkADUpXJWZUc"
- "bob@example.com email matrixrocks" -> "LJwSazmv46n0hlMlsb_iYxI0_HXEqy_yj6Jm636cdT8"
- "18005552067 msisdn matrixrocks" -> "nlo35_T5fzSGZzJApqu8lgIudJvmOQtDaHtr-I4rU7I"
-
-
-The set of hashes is then given as the ``addresses`` array in ``/lookup``. Note
-that the pepper used MUST be supplied as ``pepper`` in the ``/lookup`` request.
-
-``none``
-++++++++
-
-This algorithm performs plaintext lookups on the identity server. Typically this
-algorithm should not be used due to the security concerns of unhashed identifiers,
-however some scenarios (such as LDAP-backed identity servers) prevent the use of
-hashed identifiers. Identity servers (and optionally clients) can use this algorithm
-to perform those kinds of lookups.
-
-Similar to the ``sha256`` algorithm, the client converts the queries into strings
-separated by spaces in the format `` `` - note the lack of ````.
-For example, if the client wanted to know about ``alice@example.org``'s bindings,
-it would format the query as ``alice@example.org email``.
-
-The formatted strings are then given as the ``addresses`` in ``/lookup``. Note that
-the ``pepper`` is still required, and must be provided to ensure the client has made
-an appropriate request to ``/hash_details`` first.
-
-Security considerations
-~~~~~~~~~~~~~~~~~~~~~~~
-
-.. Note::
- `MSC2134 `_ has much more
- information about the security considerations made for this section of the
- specification. This section covers the high-level details for why the specification
- is the way it is.
-
-Typically the lookup endpoint is used when a client has an unknown 3PID it wants to
-find a Matrix User ID for. Clients normally do this kind of lookup when inviting new
-users to a room or searching a user's address book to find any Matrix users they may
-not have discovered yet. Rogue or malicious identity servers could harvest this
-unknown information and do nefarious things with it if it were sent in plain text.
-In order to protect the privacy of users who might not have a Matrix identifier bound
-to their 3PID addresses, the specification attempts to make it difficult to harvest
-3PIDs.
-
-.. admonition:: Rationale
-
- Hashing identifiers, while not perfect, helps make the effort required to harvest
- identifiers significantly higher. Phone numbers in particular are still difficult
- to protect with hashing, however hashing is objectively better than not.
-
- An alternative to hashing would be using bcrypt or similar with many rounds, however
- by nature of needing to serve mobile clients and clients on limited hardware the
- solution needs be kept relatively lightweight.
-
-Clients should be cautious of servers not rotating their pepper very often, and
-potentially of servers which use a weak pepper - these servers may be attempting to
-brute force the identifiers or use rainbow tables to mine the addresses. Similarly,
-clients which support the ``none`` algorithm should consider at least warning the user
-of the risks in sending identifiers in plain text to the identity server.
-
-Addresses are still potentially reversable using a calculated rainbow table given
-some identifiers, such as phone numbers, common email address domains, and leaked
-addresses are easily calculated. For example, phone numbers can have roughly 12
-digits to them, making them an easier target for attack than email addresses.
-
-
-Establishing associations
--------------------------
-
-The flow for creating an association is session-based.
-
-Within a session, one may prove that one has ownership of a 3PID.
-Once this has been established, the user can form an association between that
-3PID and a Matrix user ID. Note that this association is only proved one way;
-a user can associate *any* Matrix user ID with a validated 3PID,
-i.e. I can claim that any email address I own is associated with
-@billg:microsoft.com.
-
-Sessions are time-limited; a session is considered to have been modified when
-it was created, and then when a validation is performed within it. A session can
-only be checked for validation, and validation can only be performed within a
-session, within a 24 hour period since its most recent modification. Any
-attempts to perform these actions after the expiry will be rejected, and a new
-session should be created and used instead.
-
-To start a session, the client makes a request to the appropriate
-``/requestToken`` endpoint. The identity server then sends a validation token
-to the user, and the user provides the token to the client. The client then
-provides the token to the appropriate ``/submitToken`` endpoint, completing the
-session. At this point, the client should ``/bind`` the third party identifier
-or leave it for another entity to bind.
-
-Format of a validation token
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The format of the validation token is left up to the identity server: it
-should choose one appropriate to the 3PID type. (For example, it would be
-inappropriate to expect a user to copy a long passphrase including punctuation
-from an SMS message into a client.)
-
-Whatever format the identity server uses, the validation token must consist of
-at most 255 Unicode codepoints. Clients must pass the token through without
-modification.
-
-Email associations
-~~~~~~~~~~~~~~~~~~
-
-{{email_associations_is_http_api}}
-
-{{v2_email_associations_is_http_api}}
-
-Phone number associations
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-{{phone_associations_is_http_api}}
-
-{{v2_phone_associations_is_http_api}}
-
-General
-~~~~~~~
-
-{{associations_is_http_api}}
-
-{{v2_associations_is_http_api}}
-
-Invitation storage
-------------------
-
-An identity server can store pending invitations to a user's 3PID, which will
-be retrieved and can be either notified on or look up when the 3PID is
-associated with a Matrix user ID.
-
-At a later point, if the owner of that particular 3PID binds it with a Matrix user
-ID, the identity server will attempt to make an HTTP POST to the Matrix user's
-homeserver via the `/3pid/onbind`_ endpoint. The request MUST be signed with a
-long-term private key for the identity server.
-
-{{store_invite_is_http_api}}
-
-{{v2_store_invite_is_http_api}}
-
-Ephemeral invitation signing
-----------------------------
-
-To aid clients who may not be able to perform crypto themselves, the identity
-server offers some crypto functionality to help in accepting invitations.
-This is less secure than the client doing it itself, but may be useful where
-this isn't possible.
-
-{{invitation_signing_is_http_api}}
-
-{{v2_invitation_signing_is_http_api}}
-
-.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
-.. _`3PID Types`: ../appendices.html#pid-types
-.. _`Signing JSON`: ../appendices.html#signing-json
-.. _`/3pid/onbind`: ../server_server/%SERVER_RELEASE_LABEL%.html#put-matrix-federation-v1-3pid-onbind
diff --git a/specification/index.rst b/specification/index.rst
deleted file mode 100644
index aa8a4641a98..00000000000
--- a/specification/index.rst
+++ /dev/null
@@ -1,575 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Matrix Specification
-====================
-
-.. Note that this file is specifically unversioned because we don't want to
-.. have to add Yet Another version number, and the commentary on what specs we
-.. have should hopefully not get complex enough that we need to worry about
-.. versioning it.
-
-Matrix defines a set of open APIs for decentralised communication, suitable for
-securely publishing, persisting and subscribing to data over a global open
-federation of servers with no single point of control. Uses include Instant Messaging (IM),
-Voice over IP (VoIP) signalling, Internet of Things (IoT) communication, and bridging
-together existing communication silos - providing the basis of a new open real-time
-communication ecosystem.
-
-To propose a change to the Matrix Spec, see the explanations at
-`Proposals for Spec Changes to Matrix `_.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Matrix APIs
------------
-
-The specification consists of the following parts:
-
-{{apis}}
-
-Additionally, this introduction page contains the key baseline information required to
-understand the specific APIs, including the sections on `room versions`_
-and `overall architecture <#architecture>`_.
-
-The `Appendices `_ contain supplemental information not specific to
-one of the above APIs.
-
-The `Matrix Client-Server API Swagger Viewer `_
-is useful for browsing the Client-Server API.
-
-
-Matrix versions
-~~~~~~~~~~~~~~~
-
-.. Note::
- As of June 10th 2019, the Matrix specification is considered out of beta -
- 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.
-
-Matrix 1.0 (released June 10th, 2019) consists of the following minimum API
-versions:
-
-======================= =======
-API/Specification Version
-======================= =======
-Client-Server API r0.5.0
-Server-Server API r0.1.2
-Application Service API r0.1.1
-Identity Service API r0.1.1
-Push Gateway API r0.1.0
-Room Version v5
-======================= =======
-
-
-Introduction to the Matrix APIs
--------------------------------
-
-Matrix is a set of open APIs for open-federated Instant Messaging (IM), Voice
-over IP (VoIP) and Internet of Things (IoT) communication, designed to create
-and support a new global real-time communication ecosystem. The intention is to
-provide an open decentralised pubsub layer for the internet for securely
-persisting and publishing/subscribing JSON objects. This specification is the
-ongoing result of standardising the APIs used by the various components of the
-Matrix ecosystem to communicate with one another.
-
-The principles that Matrix attempts to follow are:
-
-- Pragmatic Web-friendly APIs (i.e. JSON over REST)
-- Keep It Simple & Stupid
-
- + provide a simple architecture with minimal third-party dependencies.
-
-- Fully open:
-
- + Fully open federation - anyone should be able to participate in the global
- Matrix network
- + Fully open standard - publicly documented standard with no IP or patent
- licensing encumbrances
- + Fully open source reference implementation - liberally-licensed example
- implementations with no IP or patent licensing encumbrances
-
-- Empowering the end-user
-
- + The user should be able to choose the server and clients they use
- + The user should be able to control how private their communication is
- + The user should know precisely where their data is stored
-
-- Fully decentralised - no single points of control over conversations or the
- network as a whole
-- Learning from history to avoid repeating it
-
- + Trying to take the best aspects of XMPP, SIP, IRC, SMTP, IMAP and NNTP
- whilst trying to avoid their failings
-
-
-The functionality that Matrix provides includes:
-
-- Creation and management of fully distributed chat rooms with no
- single points of control or failure
-- Eventually-consistent cryptographically secure synchronisation of room
- state across a global open network of federated servers and services
-- Sending and receiving extensible messages in a room with (optional)
- end-to-end encryption
-- Extensible user management (inviting, joining, leaving, kicking, banning)
- mediated by a power-level based user privilege system.
-- Extensible room state management (room naming, aliasing, topics, bans)
-- Extensible user profile management (avatars, display names, etc)
-- Managing user accounts (registration, login, logout)
-- Use of 3rd Party IDs (3PIDs) such as email addresses, phone numbers,
- Facebook accounts to authenticate, identify and discover users on Matrix.
-- Trusted federation of identity servers for:
-
- + Publishing user public keys for PKI
- + Mapping of 3PIDs to Matrix IDs
-
-
-The end goal of Matrix is to be a ubiquitous messaging layer for synchronising
-arbitrary data between sets of people, devices and services - be that for
-instant messages, VoIP call setups, or any other objects that need to be
-reliably and persistently pushed from A to B in an interoperable and federated
-manner.
-
-
-Spec Change Proposals
-~~~~~~~~~~~~~~~~~~~~~
-To propose a change to the Matrix Spec, see the explanations at `Proposals
-for Spec Changes to Matrix `_.
-
-
-.. _`architecture`:
-
-Architecture
-------------
-
-Matrix defines APIs for synchronising extensible JSON objects known as
-"events" between compatible clients, servers and services. Clients are
-typically messaging/VoIP applications or IoT devices/hubs and communicate by
-synchronising communication history with their "homeserver" using the
-"Client-Server API". Each homeserver stores the communication history and
-account information for all of its clients, and shares data with the wider
-Matrix ecosystem by synchronising communication history with other homeservers
-and their clients.
-
-Clients typically communicate with each other by emitting events in the
-context of a virtual "room". Room data is replicated across *all of the
-homeservers* whose users are participating in a given room. As such, *no
-single homeserver has control or ownership over a given room*. Homeservers
-model communication history as a partially ordered graph of events known as
-the room's "event graph", which is synchronised with eventual consistency
-between the participating servers using the "Server-Server API". This process
-of synchronising shared conversation history between homeservers run by
-different parties is called "Federation". Matrix optimises for the
-Availability and Partitioned properties of CAP theorem at
-the expense of Consistency.
-
-For example, for client A to send a message to client B, client A performs an
-HTTP PUT of the required JSON event on its homeserver (HS) using the
-client-server API. A's HS appends this event to its copy of the room's event
-graph, signing the message in the context of the graph for integrity. A's HS
-then replicates the message to B's HS by performing an HTTP PUT using the
-server-server API. B's HS authenticates the request, validates the event's
-signature, authorises the event's contents and then adds it to its copy of the
-room's event graph. Client B then receives the message from his homeserver via
-a long-lived GET request.
-
-::
-
- How data flows between clients
- ==============================
-
- { Matrix client A } { Matrix client B }
- ^ | ^ |
- | events | Client-Server API | events |
- | V | V
- +------------------+ +------------------+
- | |---------( HTTPS )--------->| |
- | homeserver | | homeserver |
- | |<--------( HTTPS )----------| |
- +------------------+ Server-Server API +------------------+
- History Synchronisation
- (Federation)
-
-
-Users
-~~~~~
-
-Each client is associated with a user account, which is identified in Matrix
-using a unique "user ID". This ID is namespaced to the homeserver which
-allocated the account and has the form::
-
- @localpart:domain
-
-See `'Identifier Grammar' in the appendices `_ for full details of
-the structure of user IDs.
-
-Devices
-~~~~~~~
-
-The Matrix specification has a particular meaning for the term "device". As a
-user, I might have several devices: a desktop client, some web browsers, an
-Android device, an iPhone, etc. They broadly relate to a real device in the
-physical world, but you might have several browsers on a physical device, or
-several Matrix client applications on a mobile device, each of which would be
-its own device.
-
-Devices are used primarily to manage the keys used for end-to-end encryption
-(each device gets its own copy of the decryption keys), but they also help
-users manage their access - for instance, by revoking access to particular
-devices.
-
-When a user first uses a client, it registers itself as a new device. The
-longevity of devices might depend on the type of client. A web client will
-probably drop all of its state on logout, and create a new device every time
-you log in, to ensure that cryptography keys are not leaked to a new user. In
-a mobile client, it might be acceptable to reuse the device if a login session
-expires, provided the user is the same.
-
-Devices are identified by a ``device_id``, which is unique within the scope of
-a given user.
-
-A user may assign a human-readable display name to a device, to help them
-manage their devices.
-
-Events
-~~~~~~
-
-All data exchanged over Matrix is expressed as an "event". Typically each client
-action (e.g. sending a message) correlates with exactly one event. Each event
-has a ``type`` which is used to differentiate different kinds of data. ``type``
-values MUST be uniquely globally namespaced following Java's `package naming
-conventions`_, e.g.
-``com.example.myapp.event``. The special top-level namespace ``m.`` is reserved
-for events defined in the Matrix specification - for instance ``m.room.message``
-is the event type for instant messages. Events are usually sent in the context
-of a "Room".
-
-.. _package naming conventions: https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions
-
-Event Graphs
-~~~~~~~~~~~~
-
-.. _sect:event-graph:
-
-Events exchanged in the context of a room are stored in a directed acyclic graph
-(DAG) called an "event graph". The partial ordering of this graph gives the
-chronological ordering of events within the room. Each event in the graph has a
-list of zero or more "parent" events, which refer to any preceding events
-which have no chronological successor from the perspective of the homeserver
-which created the event.
-
-Typically an event has a single parent: the most recent message in the room at
-the point it was sent. However, homeservers may legitimately race with each
-other when sending messages, resulting in a single event having multiple
-successors. The next event added to the graph thus will have multiple parents.
-Every event graph has a single root event with no parent.
-
-To order and ease chronological comparison between the events within the graph,
-homeservers maintain a ``depth`` metadata field on each event. An event's
-``depth`` is a positive integer that is strictly greater than the depths of any
-of its parents. The root event should have a depth of 1. Thus if one event is
-before another, then it must have a strictly smaller depth.
-
-Room structure
-~~~~~~~~~~~~~~
-
-A room is a conceptual place where users can send and receive events. Events are
-sent to a room, and all participants in that room with sufficient access will
-receive the event. Rooms are uniquely identified internally via "Room IDs",
-which have the form::
-
- !opaque_id:domain
-
-There is exactly one room ID for each room. Whilst the room ID does contain a
-domain, it is simply for globally namespacing room IDs. The room does NOT
-reside on the domain specified.
-
-See `'Identifier Grammar' in the appendices `_ for full details of
-the structure of a room ID.
-
-The following conceptual diagram shows an
-``m.room.message`` event being sent to the room ``!qporfwt:matrix.org``::
-
- { @alice:matrix.org } { @bob:example.org }
- | ^
- | |
- [HTTP POST] [HTTP GET]
- Room ID: !qporfwt:matrix.org Room ID: !qporfwt:matrix.org
- Event type: m.room.message Event type: m.room.message
- Content: { JSON object } Content: { JSON object }
- | |
- V |
- +------------------+ +------------------+
- | homeserver | | homeserver |
- | matrix.org | | example.org |
- +------------------+ +------------------+
- | ^
- | [HTTP PUT] |
- | Room ID: !qporfwt:matrix.org |
- | Event type: m.room.message |
- | Content: { JSON object } |
- `-------> Pointer to the preceding message ------`
- PKI signature from matrix.org
- Transaction-layer metadata
- PKI Authorization header
-
- ....................................
- | Shared Data |
- | State: |
- | Room ID: !qporfwt:matrix.org |
- | Servers: matrix.org, example.org |
- | Members: |
- | - @alice:matrix.org |
- | - @bob:example.org |
- | Messages: |
- | - @alice:matrix.org |
- | Content: { JSON object } |
- |....................................|
-
-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
- instant messages, VoIP call setups, file transfers, etc. They generally
- describe communication activity.
-
-State events:
- These describe updates to a given piece of persistent information
- ('state') related to a room, such as the room's name, topic, membership,
- participating servers, etc. State is modelled as a lookup table of key/value
- pairs per room, with each key being a tuple of ``state_key`` and ``event type``.
- Each state event updates the value of a given key.
-
-The state of the room at a given point is calculated by considering all events
-preceding and including a given event in the graph. Where events describe the
-same state, a merge conflict algorithm is applied. The state resolution
-algorithm is transitive and does not depend on server state, as it must
-consistently select the same event irrespective of the server or the order the
-events were received in. Events are signed by the originating server (the
-signature includes the parent relations, type, depth and payload hash) and are
-pushed over federation to the participating servers in a room, currently using
-full mesh topology. Servers may also request backfill of events over federation
-from the other servers participating in a room.
-
-.. Note::
- Events are not limited to the types defined in this specification. New or custom
- event types can be created on a whim using the Java package naming convention.
- For example, a ``com.example.game.score`` event can be sent by clients and other
- clients would receive it through Matrix, assuming the client has access to the
- ``com.example`` namespace.
-
-Room Aliases
-++++++++++++
-
-Each room can also have multiple "Room Aliases", which look like::
-
- #room_alias:domain
-
-See `'Identifier Grammar' in the appendices `_ for full details of
-the structure of a room alias.
-
-A room alias "points" to a room ID and is the human-readable label by which
-rooms are publicised and discovered. The room ID the alias is pointing to can
-be obtained by visiting the domain specified. Note that the mapping from a room
-alias to a room ID is not fixed, and may change over time to point to a
-different room ID. For this reason, Clients SHOULD resolve the room alias to a
-room ID once and then use that ID on subsequent requests.
-
-When resolving a room alias the server will also respond with a list of servers
-that are in the room that can be used to join via.
-
-::
-
- HTTP GET
- #matrix:example.org !aaabaa:matrix.org
- | ^
- | |
- _______V____________________|____
- | example.org |
- | Mappings: |
- | #matrix >> !aaabaa:matrix.org |
- | #golf >> !wfeiofh:sport.com |
- | #bike >> !4rguxf:matrix.org |
- |________________________________|
-
-Identity
-~~~~~~~~
-
-Users in Matrix are identified via their Matrix user ID. However,
-existing 3rd party ID namespaces can also be used in order to identify Matrix
-users. A Matrix "Identity" describes both the user ID and any other existing IDs
-from third party namespaces *linked* to their account.
-Matrix users can *link* third-party IDs (3PIDs) such as email addresses, social
-network accounts and phone numbers to their user ID. Linking 3PIDs creates a
-mapping from a 3PID to a user ID. This mapping can then be used by Matrix
-users in order to discover the user IDs of their contacts.
-In order to ensure that the mapping from 3PID to user ID is genuine, a globally
-federated cluster of trusted "identity servers" (IS) are used to verify the 3PID
-and persist and replicate the mappings.
-
-Usage of an IS is not required in order for a client application to be part of
-the Matrix ecosystem. However, without one clients will not be able to look up
-user IDs using 3PIDs.
-
-
-Profiles
-~~~~~~~~
-
-Users may publish arbitrary key/value data associated with their account - such
-as a human readable display name, a profile photo URL, contact information
-(email address, phone numbers, website URLs etc).
-
-.. TODO
- Actually specify the different types of data - e.g. what format are display
- names allowed to be?
-
-Private User Data
-~~~~~~~~~~~~~~~~~
-
-Users may also store arbitrary private key/value data in their account - such as
-client preferences, or server configuration settings which lack any other
-dedicated API. The API is symmetrical to managing Profile data.
-
-.. TODO
- Would it really be overengineered to use the same API for both profile &
- private user data, but with different ACLs?
-
-
-Common concepts
----------------
-
-Various things are common throughout all of the Matrix APIs. They are
-documented here.
-
-.. TODO: Some words about trailing slashes. See https://github.com/matrix-org/matrix-doc/issues/2107
-
-Namespacing
-~~~~~~~~~~~
-
-Namespacing helps prevent conflicts between multiple applications and the specification
-itself. Where namespacing is used, ``m.`` prefixes are used by the specification to
-indicate that the field is controlled by the specification. Custom or non-specified
-namespaces used in the wild MUST use the Java package naming convention to prevent
-conflicts.
-
-As an example, event types defined in the specification are namespaced under the
-special ``m.`` prefix, however any client can send a custom event type, such as
-``com.example.game.score`` (assuming the client has rights to the ``com.example``
-namespace) without needing to put the event into the ``m.`` namespace.
-
-Timestamps
-~~~~~~~~~~
-
-Unless otherwise stated, timestamps are measured as milliseconds since the Unix epoch.
-Throughout the specification this may be referred to as POSIX, Unix, or just "time in
-milliseconds".
-
-
-.. _`room versions`:
-
-Room Versions
--------------
-
-Rooms are central to how Matrix operates, and have strict rules for what
-is allowed to be contained within them. Rooms can also have various
-algorithms that handle different tasks, such as what to do when two or
-more events collide in the underlying DAG. To allow rooms to be improved
-upon through new algorithms or rules, "room versions" are employed to
-manage a set of expectations for each room. New room versions are assigned
-as needed.
-
-There is no implicit ordering or hierarchy to room versions, and their principles
-are immutable once placed in the specification. Although there is a recommended
-set of versions, some rooms may benefit from features introduced by other versions.
-Rooms move between different versions by "upgrading" to the desired version. Due
-to versions not being ordered or hierarchical, this means a room can "upgrade"
-from version 2 to version 1, if it is so desired.
-
-Room version grammar
-~~~~~~~~~~~~~~~~~~~~
-
-Room versions are used to change properties of rooms that may not be compatible
-with other servers. For example, changing the rules for event authorization would
-cause older servers to potentially end up in a split-brain situation due to not
-understanding the new rules.
-
-A room version is defined as a string of characters which MUST NOT exceed 32
-codepoints in length. Room versions MUST NOT be empty and SHOULD contain only
-the characters ``a-z``, ``0-9``, ``.``, and ``-``.
-
-Room versions are not intended to be parsed and should be treated as opaque
-identifiers. Room versions consisting only of the characters ``0-9`` and ``.``
-are reserved for future versions of the Matrix protocol.
-
-The complete grammar for a legal room version is::
-
- room_version = 1*room_version_char
- room_version_char = DIGIT
- / %x61-7A ; a-z
- / "-" / "."
-
-Examples of valid room versions are:
-
-* ``1`` (would be reserved by the Matrix protocol)
-* ``1.2`` (would be reserved by the Matrix protocol)
-* ``1.2-beta``
-* ``com.example.version``
-
-Complete list of room versions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Room versions are divided into two distinct groups: stable and unstable. Stable
-room versions may be used by rooms safely. Unstable room versions are everything
-else which is either not listed in the specification or flagged as unstable for
-some other reason. Versions can switch between stable and unstable periodically
-for a variety of reasons, including discovered security vulnerabilities and age.
-
-Clients should not ask room administrators to upgrade their rooms if the room is
-running a stable version. Servers SHOULD use room version 5 as the default room
-version when creating new rooms.
-
-The available room versions are:
-
-* `Version 1 `_ - **Stable**. The current version of most rooms.
-* `Version 2 `_ - **Stable**. Implements State Resolution Version 2.
-* `Version 3 `_ - **Stable**. Introduces events whose IDs are the event's hash.
-* `Version 4 `_ - **Stable**. Builds on v3 by using URL-safe base64 for event IDs.
-* `Version 5 `_ - **Stable**. Introduces enforcement of signing key validity periods.
-* `Version 6 `_ - **Stable**. Alters several authorization rules for events.
-
-Specification Versions
-----------------------
-
-The specification for each API is versioned in the form ``rX.Y.Z``.
- * A change to ``X`` reflects a breaking change: a client implemented against
- ``r1.0.0`` may need changes to work with a server which supports (only)
- ``r2.0.0``.
- * A change to ``Y`` represents a change which is backwards-compatible for
- existing clients, but not necessarily existing servers: a client implemented
- against ``r1.1.0`` will work without changes against a server which supports
- ``r1.2.0``; but a client which requires ``r1.2.0`` may not work correctly
- with a server which implements only ``r1.1.0``.
- * A change to ``Z`` represents a change which is backwards-compatible on both
- sides. Typically this implies a clarification to the specification, rather
- than a change which must be implemented.
-
-License
--------
-
-The Matrix specification is licensed under the `Apache License, Version 2.0
-`_.
diff --git a/specification/modules.rst b/specification/modules.rst
deleted file mode 100644
index 1bc8844592d..00000000000
--- a/specification/modules.rst
+++ /dev/null
@@ -1,27 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-Modules
-=======
-
-Modules are parts of the Client-Server API which are not universal to all
-endpoints. Modules are strictly defined within this specification and
-should not be mistaken for experimental extensions or optional features.
-A compliant server implementation MUST support all modules and supporting
-specification (unless the implementation only targets clients of certain
-profiles, in which case only the required modules for those feature profiles
-MUST be implemented). A compliant client implementation MUST support all
-the required modules and supporting specification for the `Feature Profile <#feature-profiles>`_
-it targets.
diff --git a/specification/modules/_template.rst b/specification/modules/_template.rst
deleted file mode 100644
index d1fef7f5f1b..00000000000
--- a/specification/modules/_template.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Module Heading
-==============
-
-.. NOTE: Prefer to identify-modules-with-dashes despite historical examples.
-.. _module:short-name:
-
-A short summary of the module. What features does this module provide? An anchor
-should be specified at the top of the module using the format ``module:name``.
-
-Complicated modules may wish to have architecture diagrams or event flows
-(e.g. VoIP call flows) here. Custom subsections can be included but they should
-be used *sparingly* to reduce the risk of putting client or server behaviour
-information in these custom sections.
-
-Events
-------
-List the new event types introduced by this module, if any. If there are no
-new events, this section can be omitted. Event types should be done as
-subsections. This section is intended to document the "common shared event
-structure" between client and server. Deviations from this shared structure
-should be documented in the relevant behaviour section.
-
-``m.example.event.type``
-~~~~~~~~~~~~~~~~~~~~~~~~
-There should be JSON Schema docs for this event. Once there is JSON schema,
-there will be a template variable with dots in the event type replaced with
-underscores and the suffix ``_event``. You can insert a template like so:
-
-{{m_example_event_type_event}}
-
-Client behaviour
-----------------
-List any new HTTP endpoints. These endpoints should be documented using Swagger.
-Once there is Swagger, there will be a template variable based on the name of
-the YAML file with the suffix ``_cs_http_api``. You can insert a template for
-swagger docs like so:
-
-{{name-of-yaml-file-without-file-ext_cs_http_api}}
-
-List the steps the client needs to take to
-correctly process this module. List what data structures the client should be
-storing in order to aid implementation.
-
-Server behaviour
-----------------
-Does the server need to handle any of the new events in a special way (e.g.
-typing timeouts, presence). Advice on how to persist events and/or requests are
-recommended to aid implementation. Federation-specific logic should be included
-here.
-
-Security considerations
------------------------
-This includes privacy leaks: for example leaking presence info. How do
-misbehaving clients or servers impact this module? This section should always be
-included, if only to say "we've thought about it but there isn't anything to do
-here".
diff --git a/specification/modules/account_data.rst b/specification/modules/account_data.rst
deleted file mode 100644
index a67e503a677..00000000000
--- a/specification/modules/account_data.rst
+++ /dev/null
@@ -1,48 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Client Config
-=============
-
-.. _module:account_data:
-
-Clients can store custom config data for their account on their homeserver.
-This account data will be synced between different devices and can persist
-across installations on a particular device. Users may only view the account
-data for their own account
-
-The account_data may be either global or scoped to a particular rooms.
-
-Events
-------
-
-The client receives the account data as events in the ``account_data`` sections
-of a ``/sync``.
-
-These events can also be received in a ``/events`` response or in the
-``account_data`` section of a room in ``/sync``. ``m.tag``
-events appearing in ``/events`` will have a ``room_id`` with the room
-the tags are for.
-
-Client Behaviour
-----------------
-
-{{account_data_cs_http_api}}
-
-
-Server Behaviour
-----------------
-
-Servers MUST reject clients from setting account data for event types that
-the server manages. Currently, this only includes `m.fully_read`_.
diff --git a/specification/modules/admin.rst b/specification/modules/admin.rst
deleted file mode 100644
index c4465675ea1..00000000000
--- a/specification/modules/admin.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Server Administration
-=====================
-
-.. _module:admin:
-
-This module adds capabilities for server administrators to inspect server state
-and data.
-
-Client Behaviour
-----------------
-
-{{admin_cs_http_api}}
diff --git a/specification/modules/anonymous_access.rst b/specification/modules/anonymous_access.rst
deleted file mode 100644
index a6ffbfb6ec6..00000000000
--- a/specification/modules/anonymous_access.rst
+++ /dev/null
@@ -1,64 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Guest access
-================
-
-.. _module:guest-access:
-
-It may be desirable to allow users without a fully registered user account to
-ephemerally access Matrix rooms. This module specifies limited ways of doing so.
-
-Note that this is not currently a complete anonymous access solution; in
-particular, it only allows servers to provided anonymous access to rooms in
-which they are already participating, and relies on individual homeservers to
-adhere to the conventions which this module sets, rather than allowing all
-participating homeservers to enforce them.
-
-Events
-------
-
-{{m_room_guest_accessibility}}
-
-Client behaviour
-----------------
-A client can register for guest access using the FOO endpoint. From that point
-on, they can interact with a limited subset of the existing client-server API,
-as if they were a fully registered user, using the access token granted to them
-by the server.
-
-These users are only allowed to make calls in relation to rooms which have the
-``m.room.history_visibility`` event set to ``world_readable``.
-
-The APIs they are allowed to hit are:
-
-/rooms/{roomId}/messages
-/rooms/{roomId}/state
-/rooms/{roomId}/state/{eventType}/{stateKey}
-/events
-
-Server behaviour
-----------------
-Does the server need to handle any of the new events in a special way (e.g.
-typing timeouts, presence). Advice on how to persist events and/or requests are
-recommended to aid implementation. Federation-specific logic should be included
-here.
-
-Security considerations
------------------------
-This includes privacy leaks: for example leaking presence info. How do
-misbehaving clients or servers impact this module? This section should always be
-included, if only to say "we've thought about it but there isn't anything to do
-here".
-
diff --git a/specification/modules/content_repo.rst b/specification/modules/content_repo.rst
deleted file mode 100644
index 192250d22bf..00000000000
--- a/specification/modules/content_repo.rst
+++ /dev/null
@@ -1,136 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-Content repository
-==================
-
-.. _module:content:
-
-The content repository (or "media repository") allows users to upload
-files to their homeserver for later use. For example, files which the
-user wants to send to a room would be uploaded here, as would an avatar
-the user wants to use.
-
-Uploads are POSTed to a resource on the user's local homeserver which
-returns a MXC URI which can later be used to GET the download. Content
-is downloaded from the recipient's local homeserver, which must first
-transfer the content from the origin homeserver using the same API
-(unless the origin and destination homeservers are the same).
-
-When serving content, the server SHOULD provide a ``Content-Security-Policy``
-header. The recommended policy is ``sandbox; default-src 'none'; script-src
-'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src
-'self';``.
-
-Matrix Content (MXC) URIs
--------------------------
-
-.. _`MXC URI`:
-
-Content locations are represented as Matrix Content (MXC) URIs. They look
-like::
-
- mxc:///
-
- : The name of the homeserver where this content originated, e.g. matrix.org
- : An opaque ID which identifies the content.
-
-
-Client behaviour
-----------------
-
-Clients can upload and download content using the following HTTP APIs.
-
-{{content_repo_cs_http_api}}
-
-Thumbnails
-~~~~~~~~~~
-The homeserver SHOULD be able to supply thumbnails for uploaded images and
-videos. The exact file types which can be thumbnailed are not currently
-specified - see `Issue #1938 `_
-for more information.
-
-The thumbnail methods are "crop" and "scale". "scale" tries to return an
-image where either the width or the height is smaller than the requested
-size. The client should then scale and letterbox the image if it needs to
-fit within a given rectangle. "crop" tries to return an image where the
-width and height are close to the requested size and the aspect matches
-the requested size. The client should scale the image if it needs to fit
-within a given rectangle.
-
-The dimensions given to the thumbnail API are the minimum size the client
-would prefer. Servers must never return thumbnails smaller than the client's
-requested dimensions, unless the content being thumbnailed is smaller than
-the dimensions. When the content is smaller than the requested dimensions,
-servers should return the original content rather than thumbnail it.
-
-Servers SHOULD produce thumbnails with the following dimensions and methods:
-
-* 32x32, crop
-* 96x96, crop
-* 320x240, scale
-* 640x480, scale
-* 800x600, scale
-
-In summary:
- * "scale" maintains the original aspect ratio of the image
- * "crop" provides an image in the aspect ratio of the sizes given in the request
- * The server will return an image larger than or equal to the dimensions requested
- where possible.
-
-Servers MUST NOT upscale thumbnails under any circumstance. Servers MUST NOT
-return a smaller thumbnail than requested, unless the original content makes
-that impossible.
-
-Security considerations
------------------------
-
-The HTTP GET endpoint does not require any authentication. Knowing the URL of
-the content is sufficient to retrieve the content, even if the entity isn't in
-the room.
-
-MXC URIs are vulnerable to directory traversal attacks such as
-``mxc://127.0.0.1/../../../some_service/etc/passwd``. This would cause the target
-homeserver to try to access and return this file. As such, homeservers MUST
-sanitise MXC URIs by allowing only alphanumeric (``A-Za-z0-9``), ``_``
-and ``-`` characters in the ``server-name`` and ``media-id`` values. This set
-of whitelisted characters allows URL-safe base64 encodings specified in RFC 4648.
-Applying this character whitelist is preferable to blacklisting ``.`` and ``/``
-as there are techniques around blacklisted characters (percent-encoded characters,
-UTF-8 encoded traversals, etc).
-
-Homeservers have additional content-specific concerns:
-
-- Clients may try to upload very large files. Homeservers should not store files
- that are too large and should not serve them to clients, returning a HTTP 413
- error with the ``M_TOO_LARGE`` code.
-
-- Clients may try to upload very large images. Homeservers should not attempt to
- generate thumbnails for images that are too large, returning a HTTP 413 error
- with the ``M_TOO_LARGE`` code.
-
-- Remote homeservers may host very large files or images. Homeservers should not
- proxy or thumbnail large files or images from remote homeservers, returning a
- HTTP 502 error with the ``M_TOO_LARGE`` code.
-
-- Clients may try to upload a large number of files. Homeservers should limit the
- number and total size of media that can be uploaded by clients, returning a
- HTTP 403 error with the ``M_FORBIDDEN`` code.
-
-- Clients may try to access a large number of remote files through a homeserver.
- Homeservers should restrict the number and size of remote files that it caches.
-
-- Clients or remote homeservers may try to upload malicious files targeting
- vulnerabilities in either the homeserver thumbnailing or the client decoders.
diff --git a/specification/modules/device_management.rst b/specification/modules/device_management.rst
deleted file mode 100644
index 3f9c84529bf..00000000000
--- a/specification/modules/device_management.rst
+++ /dev/null
@@ -1,41 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Device Management
-=================
-
-.. _module:device-management:
-
-This module provides a means for a user to manage their `devices`_.
-
-Client behaviour
-----------------
-Clients that implement this module should offer the user a list of registered
-devices, as well as the means to update their display names. Clients should
-also allow users to delete disused devices.
-
-{{device_management_cs_http_api}}
-
-Security considerations
------------------------
-
-Deleting devices has security implications: it invalidates the access_token
-assigned to the device, so an attacker could use it to log out the real user
-(and do it repeatedly every time the real user tries to log in to block the
-attacker). Servers should require additional authentication beyond the access
-token when deleting devices (for example, requiring that the user resubmit
-their password).
-
-The display names of devices are publicly visible. Clients should consider
-advising the user of this.
diff --git a/specification/modules/dm.rst b/specification/modules/dm.rst
deleted file mode 100644
index a89d3522aca..00000000000
--- a/specification/modules/dm.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Direct Messaging
-================
-
-.. _module:dm:
-
-All communication over Matrix happens within a room. It is sometimes
-desirable to offer users the concept of speaking directly to one
-particular person. This module defines a way of marking certain rooms
-as 'direct chats' with a given person. This does not restrict the chat
-to being between exactly two people since this would preclude the
-presence of automated 'bot' users or even a 'personal assistant' who is
-able to answer direct messages on behalf of the user in their absence.
-
-A room may not necessarily be considered 'direct' by all members of the
-room, but a signalling mechanism exists to propagate the information of
-whether a chat is 'direct' to an invitee.
-
-Events
-------
-
-{{m_direct_event}}
-
-Client behaviour
-----------------
-To start a direct chat with another user, the inviting user's client
-should set the ``is_direct`` flag to |/createRoom|_. The client should do
-this whenever the flow the user has followed is one where their
-intention is to speak directly with another person, as opposed to bringing that
-person in to a shared room. For example, clicking on 'Start Chat' beside a
-person's profile picture would imply the ``is_direct`` flag should be set.
-
-The invitee's client may use the ``is_direct`` flag in the `m.room.member`_
-event to automatically mark the room as a direct chat but this is not
-required: it may for example, prompt the user, or ignore the flag altogether.
-
-Both the inviting client and the invitee's client should record the fact that
-the room is a direct chat by storing an ``m.direct`` event in the account data
-using |/user//account_data/|_.
-
-Server behaviour
-----------------
-When the ``is_direct`` flag is given to |/createRoom|_, the home
-server must set the ``is_direct`` flag in the invite member event for any users
-invited in the |/createRoom|_ call.
diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst
deleted file mode 100644
index 0e57636f521..00000000000
--- a/specification/modules/end_to_end_encryption.rst
+++ /dev/null
@@ -1,1361 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-End-to-End Encryption
-=====================
-
-.. _module:e2e:
-
-Matrix optionally supports end-to-end encryption, allowing rooms to be created
-whose conversation contents are not decryptable or interceptable on any of the
-participating homeservers.
-
-Key Distribution
-----------------
-Encryption and Authentication in Matrix is based around public-key
-cryptography. The Matrix protocol provides a basic mechanism for exchange of
-public keys, though an out-of-band channel is required to exchange fingerprints
-between users to build a web of trust.
-
-Overview
-~~~~~~~~
-
-.. code::
-
- 1) Bob publishes the public keys and supported algorithms for his
- device. This may include long-term identity keys, and/or one-time
- keys.
-
- +----------+ +--------------+
- | Bob's HS | | Bob's Device |
- +----------+ +--------------+
- | |
- |<=============|
- /keys/upload
-
- 2) Alice requests Bob's public identity keys and supported algorithms.
-
- +----------------+ +------------+ +----------+
- | Alice's Device | | Alice's HS | | Bob's HS |
- +----------------+ +------------+ +----------+
- | | |
- |=================>|==============>|
- /keys/query
-
- 3) Alice selects an algorithm and claims any one-time keys needed.
-
- +----------------+ +------------+ +----------+
- | Alice's Device | | Alice's HS | | Bob's HS |
- +----------------+ +------------+ +----------+
- | | |
- |=================>|==============>|
- /keys/claim
-
-
-Key algorithms
-~~~~~~~~~~~~~~
-
-The name ``ed25519`` corresponds to the `Ed25519`_ signature algorithm. The key
-is a 32-byte Ed25519 public key, encoded using `unpadded Base64`_. Example:
-
-.. code:: json
-
- "SogYyrkTldLz0BXP+GYWs0qaYacUI0RleEqNT8J3riQ"
-
-The name ``curve25519`` corresponds to the `Curve25519`_ ECDH algorithm. The
-key is a 32-byte Curve25519 public key, encoded using `unpadded
-Base64`_. Example:
-
-.. code:: json
-
- "JGLn/yafz74HB2AbPLYJWIVGnKAtqECOBf11yyXac2Y"
-
-The name ``signed_curve25519`` also corresponds to the Curve25519 algorithm,
-but a key using this algorithm is represented by an object with a the following
-properties:
-
-``KeyObject``
-
-========== ================ =====================================================
-Parameter Type Description
-========== ================ =====================================================
-key string **Required.** The unpadded Base64-encoded 32-byte
- Curve25519 public key.
-signatures Signatures **Required.** Signatures of the key object.
-
- The signature is calculated using the process described
- at `Signing JSON`_.
-========== ================ =====================================================
-
-Example:
-
-.. code:: json
-
- {
- "key":"06UzBknVHFMwgi7AVloY7ylC+xhOhEX4PkNge14Grl8",
- "signatures": {
- "@user:example.com": {
- "ed25519:EGURVBUNJP": "YbJva03ihSj5mPk+CHMJKUKlCXCPFXjXOK6VqBnN9nA2evksQcTGn6hwQfrgRHIDDXO2le49x7jnWJHMJrJoBQ"
- }
- }
- }
-
-Device keys
-~~~~~~~~~~~
-
-Each device should have one Ed25519 signing key. This key should be generated
-on the device from a cryptographically secure source, and the private part of
-the key should never be exported from the device. This key is used as the
-fingerprint for a device by other clients.
-
-A device will generally need to generate a number of additional keys. Details
-of these will vary depending on the messaging algorithm in use.
-
-Algorithms generally require device identity keys as well as signing keys. Some
-algorithms also require one-time keys to improve their secrecy and deniability.
-These keys are used once during session establishment, and are then thrown
-away.
-
-For Olm version 1, each device requires a single Curve25519 identity key, and a
-number of signed Curve25519 one-time keys.
-
-Uploading keys
-~~~~~~~~~~~~~~
-
-A device uploads the public parts of identity keys to their homeserver as a
-signed JSON object, using the |/keys/upload|_ API.
-The JSON object must include the public part of the device's Ed25519 key, and
-must be signed by that key, as described in `Signing JSON`_.
-
-One-time keys are also uploaded to the homeserver using the |/keys/upload|_
-API.
-
-Devices must store the private part of each key they upload. They can
-discard the private part of a one-time key when they receive a message using
-that key. However it's possible that a one-time key given out by a homeserver
-will never be used, so the device that generates the key will never know that
-it can discard the key. Therefore a device could end up trying to store too
-many private keys. A device that is trying to store too many private keys may
-discard keys starting with the oldest.
-
-Tracking the device list for a user
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Before Alice can send an encrypted message to Bob, she needs a list of each of
-his devices and the associated identity keys, so that she can establish an
-encryption session with each device. This list can be obtained by calling
-|/keys/query|_, passing Bob's user ID in the ``device_keys`` parameter.
-
-From time to time, Bob may add new devices, and Alice will need to know this so
-that she can include his new devices for later encrypted messages. A naive
-solution to this would be to call |/keys/query|_ before sending each message -
-however, the number of users and devices may be large and this would be
-inefficient.
-
-It is therefore expected that each client will maintain a list of devices for a
-number of users (in practice, typically each user with whom we share an
-encrypted room). Furthermore, it is likely that this list will need to be
-persisted between invocations of the client application (to preserve device
-verification data and to alert Alice if Bob suddenly gets a new
-device).
-
-Alice's client can maintain a list of Bob's devices via the following
-process:
-
-#. It first sets a flag to record that it is now tracking Bob's device list,
- and a separate flag to indicate that its list of Bob's devices is
- outdated. Both flags should be in storage which persists over client
- restarts.
-
-#. It then makes a request to |/keys/query|_, passing Bob's user ID in the
- ``device_keys`` parameter. When the request completes, it stores the
- resulting list of devices in persistent storage, and clears the 'outdated'
- flag.
-
-#. During its normal processing of responses to |/sync|_, Alice's client
- inspects the ``changed`` property of the |device_lists|_ field. If it is
- tracking the device lists of any of the listed users, then it marks the
- device lists for those users outdated, and initiates another request to
- |/keys/query|_ for them.
-
-#. Periodically, Alice's client stores the ``next_batch`` field of the result
- from |/sync|_ in persistent storage. If Alice later restarts her client, it
- can obtain a list of the users who have updated their device list while it
- was offline by calling |/keys/changes|_, passing the recorded ``next_batch``
- field as the ``from`` parameter. If the client is tracking the device list
- of any of the users listed in the response, it marks them as outdated. It
- combines this list with those already flagged as outdated, and initiates a
- |/keys/query|_ request for all of them.
-
-.. Warning::
-
- Bob may update one of his devices while Alice has a request to
- ``/keys/query`` in flight. Alice's client may therefore see Bob's user ID in
- the ``device_lists`` field of the ``/sync`` response while the first request
- is in flight, and initiate a second request to ``/keys/query``. This may
- lead to either of two related problems.
-
- The first problem is that, when the first request completes, the client will
- clear the 'outdated' flag for Bob's devices. If the second request fails, or
- the client is shut down before it completes, this could lead to Alice using
- an outdated list of Bob's devices.
-
- The second possibility is that, under certain conditions, the second request
- may complete *before* the first one. When the first request completes, the
- client could overwrite the later results from the second request with those
- from the first request.
-
- Clients MUST guard against these situations. For example, a client could
- ensure that only one request to ``/keys/query`` is in flight at a time for
- each user, by queuing additional requests until the first completes.
- Alternatively, the client could make a new request immediately, but ensure
- that the first request's results are ignored (possibly by cancelling the
- request).
-
-.. Note::
-
- When Bob and Alice share a room, with Bob tracking Alice's devices, she may leave
- the room and then add a new device. Bob will not be notified of this change,
- as he doesn't share a room anymore with Alice. When they start sharing a
- room again, Bob has an out-of-date list of Alice's devices. In order to address
- this issue, Bob's homeserver will add Alice's user ID to the ``changed`` property of
- the ``device_lists`` field, thus Bob will update his list of Alice's devices as part
- of his normal processing. Note that Bob can also be notified when he stops sharing
- any room with Alice by inspecting the ``left`` property of the ``device_lists``
- field, and as a result should remove her from its list of tracked users.
-
-.. |device_lists| replace:: ``device_lists``
-.. _`device_lists`: `device_lists_sync`_
-
-
-Sending encrypted attachments
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When encryption is enabled in a room, files should be uploaded encrypted on
-the homeserver.
-
-In order to achieve this, a client should generate a single-use 256-bit AES
-key, and encrypt the file using AES-CTR. The counter should be 64-bit long,
-starting at 0 and prefixed by a random 64-bit Initialization Vector (IV), which
-together form a 128-bit unique counter block.
-
-.. Warning::
- An IV must never be used multiple times with the same key. This implies that
- if there are multiple files to encrypt in the same message, typically an
- image and its thumbnail, the files must not share both the same key and IV.
-
-Then, the encrypted file can be uploaded to the homeserver.
-The key and the IV must be included in the room event along with the resulting
-``mxc://`` in order to allow recipients to decrypt the file. As the event
-containing those will be Megolm encrypted, the server will never have access to
-the decrypted file.
-
-A hash of the ciphertext must also be included, in order to prevent the homeserver from
-changing the file content.
-
-A client should send the data as an encrypted ``m.room.message`` event, using
-either ``m.file`` as the msgtype, or the appropriate msgtype for the file
-type. The key is sent using the `JSON Web Key`_ format, with a `W3C
-extension`_.
-
-.. anchor for link from m.message api spec
-.. |encrypted_files| replace:: End-to-end encryption
-.. _encrypted_files:
-
-Extensions to ``m.message`` msgtypes
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-This module adds ``file`` and ``thumbnail_file`` properties, of type
-``EncryptedFile``, to ``m.message`` msgtypes that reference files, such as
-`m.file`_ and `m.image`_, replacing the ``url`` and ``thumbnail_url``
-properties.
-
-.. todo: generate this from a swagger definition?
-
-``EncryptedFile``
-
-========= ================ =====================================================
-Parameter Type Description
-========= ================ =====================================================
-url string **Required.** The URL to the file.
-key JWK **Required.** A `JSON Web Key`_ object.
-iv string **Required.** The 128-bit unique counter block used by
- AES-CTR, encoded as unpadded base64.
-hashes {string: string} **Required.** A map from an algorithm name to a hash
- of the ciphertext, encoded as unpadded base64. Clients
- should support the SHA-256 hash, which uses the key
- ``sha256``.
-v string **Required.** Version of the encrypted attachments
- protocol. Must be ``v2``.
-========= ================ =====================================================
-
-``JWK``
-
-========= ========= ============================================================
-Parameter Type Description
-========= ========= ============================================================
-kty string **Required.** Key type. Must be ``oct``.
-key_ops [string] **Required.** Key operations. Must at least contain
- ``encrypt`` and ``decrypt``.
-alg string **Required.** Algorithm. Must be ``A256CTR``.
-k string **Required.** The key, encoded as urlsafe unpadded base64.
-ext boolean **Required.** Extractable. Must be ``true``. This is a
- `W3C extension`_.
-========= ========= ============================================================
-
-Example:
-
-.. code :: json
-
- {
- "content": {
- "body": "something-important.jpg",
- "file": {
- "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
- "mimetype": "image/jpeg",
- "v": "v2",
- "key": {
- "alg": "A256CTR",
- "ext": true,
- "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0",
- "key_ops": ["encrypt","decrypt"],
- "kty": "oct"
- },
- "iv": "w+sE15fzSc0AAAAAAAAAAA",
- "hashes": {
- "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA"
- }
- },
- "info": {
- "mimetype": "image/jpeg",
- "h": 1536,
- "size": 422018,
- "thumbnail_file": {
- "hashes": {
- "sha256": "/NogKqW5bz/m8xHgFiH5haFGjCNVmUIPLzfvOhHdrxY"
- },
- "iv": "U+k7PfwLr6UAAAAAAAAAAA",
- "key": {
- "alg": "A256CTR",
- "ext": true,
- "k": "RMyd6zhlbifsACM1DXkCbioZ2u0SywGljTH8JmGcylg",
- "key_ops": ["encrypt", "decrypt"],
- "kty": "oct"
- },
- "mimetype": "image/jpeg",
- "url": "mxc://example.org/pmVJxyxGlmxHposwVSlOaEOv",
- "v": "v2"
- },
- "thumbnail_info": {
- "h": 768,
- "mimetype": "image/jpeg",
- "size": 211009,
- "w": 432
- },
- "w": 864
- },
- "msgtype": "m.image"
- },
- "event_id": "$143273582443PhrSn:example.org",
- "origin_server_ts": 1432735824653,
- "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
- "sender": "@example:example.org",
- "type": "m.room.message",
- "unsigned": {
- "age": 1234
- }
- }
-
-Claiming one-time keys
-~~~~~~~~~~~~~~~~~~~~~~
-
-A client wanting to set up a session with another device can claim a one-time
-key for that device. This is done by making a request to the |/keys/claim|_
-API.
-
-A homeserver should rate-limit the number of one-time keys that a given user or
-remote server can claim. A homeserver should discard the public part of a one
-time key once it has given that key to another user.
-
-Device verification
--------------------
-
-Before Alice sends Bob encrypted data, or trusts data received from him, she
-may want to verify that she is actually communicating with him, rather than a
-man-in-the-middle. This verification process requires an out-of-band channel:
-there is no way to do it within Matrix without trusting the administrators of
-the homeservers.
-
-In Matrix, verification works by Alice meeting Bob in person, or contacting him
-via some other trusted medium, and use `SAS Verification`_ to interactively
-verify Bob's devices. Alice and Bob may also read aloud their unpadded base64
-encoded Ed25519 public key, as returned by ``/keys/query``.
-
-Device verification may reach one of several conclusions. For example:
-
-* Alice may "accept" the device. This means that she is satisfied that the
- device belongs to Bob. She can then encrypt sensitive material for that
- device, and knows that messages received were sent from that device.
-
-* Alice may "reject" the device. She will do this if she knows or suspects
- that Bob does not control that device (or equivalently, does not trust
- Bob). She will not send sensitive material to that device, and cannot trust
- messages apparently received from it.
-
-* Alice may choose to skip the device verification process. She is not able
- to verify that the device actually belongs to Bob, but has no reason to
- suspect otherwise. The encryption protocol continues to protect against
- passive eavesdroppers.
-
-.. NOTE::
-
- Once the signing key has been verified, it is then up to the encryption
- protocol to verify that a given message was sent from a device holding that
- Ed25519 private key, or to encrypt a message so that it may only be
- decrypted by such a device. For the Olm protocol, this is documented at
- https://matrix.org/docs/olm_signing.html.
-
-
-Key verification framework
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Verifying keys manually by reading out the Ed25519 key is not very user friendly,
-and can lead to errors. In order to help mitigate errors, and to make the process
-easier for users, some verification methods are supported by the specification.
-The methods all use a common framework for negotiating the key verification.
-
-To use this framework, Alice's client would send ``m.key.verification.request``
-events to Bob's devices. All of the ``to_device`` messages sent to Bob MUST have
-the same ``transaction_id`` to indicate they are part of the same request. This
-allows Bob to reject the request on one device, and have it apply to all of his
-devices. Similarly, it allows Bob to process the verification on one device without
-having to involve all of his devices.
-
-When Bob's device receives a ``m.key.verification.request``, it should prompt Bob
-to verify keys with Alice using one of the supported methods in the request. If
-Bob's device does not understand any of the methods, it should not cancel the request
-as one of his other devices may support the request. Instead, Bob's device should
-tell Bob that an unsupported method was used for starting key verification. The
-prompt for Bob to accept/reject Alice's request (or the unsupported method prompt)
-should be automatically dismissed 10 minutes after the ``timestamp`` field or 2
-minutes after Bob's client receives the message, whichever comes first, if Bob
-does not interact with the prompt. The prompt should additionally be hidden if
-an appropriate ``m.key.verification.cancel`` message is received.
-
-If Bob rejects the request, Bob's client must send a ``m.key.verification.cancel``
-message to Alice's device. Upon receipt, Alice's device should tell her that Bob
-does not want to verify her device and send ``m.key.verification.cancel`` messages
-to all of Bob's devices to notify them that the request was rejected.
-
-If Bob accepts the request, Bob's device starts the key verification process by
-sending a ``m.key.verification.start`` message to Alice's device. Upon receipt
-of this message, Alice's device should send a ``m.key.verification.cancel`` message
-to all of Bob's other devices to indicate the process has been started. The start
-message must use the same ``transaction_id`` from the original key verification
-request if it is in response to the request. The start message can be sent indepdently
-of any request.
-
-Individual verification methods may add additional steps, events, and properties to
-the verification messages. Event types for methods defined in this specification must
-be under the ``m.key.verification`` namespace and any other event types must be namespaced
-according to the Java package naming convention.
-
-Any of Alice's or Bob's devices can cancel the key verification request or process
-at any time with a ``m.key.verification.cancel`` message to all applicable devices.
-
-This framework yields the following handshake, assuming both Alice and Bob each have
-2 devices, Bob's first device accepts the key verification request, and Alice's second
-device initiates the request. Note how Alice's first device is not involved in the
-request or verification process.
-
-::
-
- +---------------+ +---------------+ +-------------+ +-------------+
- | AliceDevice1 | | AliceDevice2 | | BobDevice1 | | BobDevice2 |
- +---------------+ +---------------+ +-------------+ +-------------+
- | | | |
- | | m.key.verification.request | |
- | |---------------------------------->| |
- | | | |
- | | m.key.verification.request | |
- | |-------------------------------------------------->|
- | | | |
- | | m.key.verification.start | |
- | |<----------------------------------| |
- | | | |
- | | m.key.verification.cancel | |
- | |-------------------------------------------------->|
- | | | |
-
-
-After the handshake, the verification process begins.
-
-{{m_key_verification_request_event}}
-
-{{m_key_verification_start_event}}
-
-{{m_key_verification_cancel_event}}
-
-
-.. _`SAS Verification`:
-
-Short Authentication String (SAS) verification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-SAS verification is a user-friendly key verification process built off the common
-framework outlined above. SAS verification is intended to be a highly interactive
-process for users, and as such exposes verfiication methods which are easier for
-users to use.
-
-The verification process is heavily inspired by Phil Zimmermann's ZRTP key agreement
-handshake. A key part of key agreement in ZRTP is the hash commitment: the party that
-begins the Diffie-Hellman key sharing sends a hash of their part of the Diffie-Hellman
-exchange, and does not send their part of the Diffie-Hellman exchange until they have
-received the other party's part. Thus an attacker essentially only has one attempt to
-attack the Diffie-Hellman exchange, and hence we can verify fewer bits while still
-achieving a high degree of security: if we verify n bits, then an attacker has a 1 in
-2\ :sup:`n` chance of success. For example, if we verify 40 bits, then an attacker has
-a 1 in 1,099,511,627,776 chance (or less than 1 in 10\ :sup:`12` chance) of success. A failed
-attack would result in a mismatched Short Authentication String, alerting users to the
-attack.
-
-The verification process takes place over `to-device`_ messages in two phases:
-
-1. Key agreement phase (based on `ZRTP key agreement `_).
-#. Key verification phase (based on HMAC).
-
-The process between Alice and Bob verifying each other would be:
-
-.. |AlicePublicKey| replace:: :math:`K_{A}^{public}`
-.. |AlicePrivateKey| replace:: :math:`K_{A}^{private}`
-.. |AliceCurve25519| replace:: :math:`K_{A}^{private},K_{A}^{public}`
-.. |BobPublicKey| replace:: :math:`K_{B}^{public}`
-.. |BobPrivateKey| replace:: :math:`K_{B}^{private}`
-.. |BobCurve25519| replace:: :math:`K_{B}^{private},K_{B}^{public}`
-.. |BobAliceCurve25519| replace:: :math:`K_{B}^{private}K_{A}^{public}`
-.. |AliceBobECDH| replace:: :math:`ECDH(K_{A}^{private},K_{B}^{public})`
-
-1. Alice and Bob establish a secure out-of-band connection, such as meeting
- in-person or a video call. "Secure" here means that either party cannot be
- impersonated, not explicit secrecy.
-#. Alice and Bob communicate which devices they'd like to verify with each other.
-#. Alice selects Bob's device from the device list and begins verification.
-#. Alice's client ensures it has a copy of Bob's device key.
-#. Alice's device sends Bob's device a ``m.key.verification.start`` message.
-#. Bob's device receives the message and selects a key agreement protocol, hash
- algorithm, message authentication code, and SAS method supported by Alice's
- device.
-#. Bob's device ensures it has a copy of Alice's device key.
-#. Bob's device creates an ephemeral Curve25519 key pair (|BobCurve25519|), and
- calculates the hash (using the chosen algorithm) of the public key |BobPublicKey|.
-#. Bob's device replies to Alice's device with a ``m.key.verification.accept`` message.
-#. Alice's device receives Bob's message and stores the commitment hash for later use.
-#. Alice's device creates an ephemeral Curve25519 key pair (|AliceCurve25519|) and
- replies to Bob's device with a ``m.key.verification.key``, sending only the public
- key |AlicePublicKey|.
-#. Bob's device receives Alice's message and replies with its own ``m.key.verification.key``
- message containing its public key |BobPublicKey|.
-#. Alice's device receives Bob's message and verifies the commitment hash from earlier
- matches the hash of the key Bob's device just sent and the content of Alice's
- ``m.key.verification.start`` message.
-#. Both Alice and Bob's devices perform an Elliptic-curve Diffie-Hellman (|AliceBobECDH|),
- using the result as the shared secret.
-#. Both Alice and Bob's devices display a SAS to their users, which is derived
- from the shared key using one of the methods in this section. If multiple SAS
- methods are available, clients should allow the users to select a method.
-#. Alice and Bob compare the strings shown by their devices, and tell their devices if
- they match or not.
-#. Assuming they match, Alice and Bob's devices calculate the HMAC of their own device keys
- and a comma-separated sorted list of of the key IDs that they wish the other user
- to verify, using SHA-256 as the hash function. HMAC is defined in `RFC 2104 `_.
- The key for the HMAC is different for each item and is calculated by generating
- 32 bytes (256 bits) using `the key verification HKDF <#sas-hkdf>`_.
-#. Alice's device sends Bob's device a ``m.key.verification.mac`` message containing the
- MAC of Alice's device keys and the MAC of her key IDs to be verified. Bob's device does
- the same for Bob's device keys and key IDs concurrently with Alice.
-#. When the other device receives the ``m.key.verification.mac`` message, the device
- calculates the HMAC of its copies of the other device's keys given in the message,
- as well as the HMAC of the comma-separated, sorted, list of key IDs in the message.
- The device compares these with the HMAC values given in the message, and if everything
- matches then the device keys are verified.
-
-The wire protocol looks like the following between Alice and Bob's devices::
-
- +-------------+ +-----------+
- | AliceDevice | | BobDevice |
- +-------------+ +-----------+
- | |
- | m.key.verification.start |
- |-------------------------------->|
- | |
- | m.key.verification.accept |
- |<--------------------------------|
- | |
- | m.key.verification.key |
- |-------------------------------->|
- | |
- | m.key.verification.key |
- |<--------------------------------|
- | |
- | m.key.verification.mac |
- |-------------------------------->|
- | |
- | m.key.verification.mac |
- |<--------------------------------|
- | |
-
-Error and exception handling
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-At any point the interactive verfication can go wrong. The following describes what
-to do when an error happens:
-
-* Alice or Bob can cancel the verification at any time. A ``m.key.verification.cancel``
- message must be sent to signify the cancellation.
-* The verification can time out. Clients should time out a verification that does not
- complete within 10 minutes. Additionally, clients should expire a ``transaction_id``
- which goes unused for 10 minutes after having last sent/received it. The client should
- inform the user that the verification timed out, and send an appropriate
- ``m.key.verification.cancel`` message to the other device.
-* When the same device attempts to intiate multiple verification attempts, the receipient
- should cancel all attempts with that device.
-* When a device receives an unknown ``transaction_id``, it should send an appropriate
- ``m.key.verfication.cancel`` message to the other device indicating as such. This
- does not apply for inbound ``m.key.verification.start`` or ``m.key.verification.cancel``
- messages.
-* If the two devices do not share a common key share, hash, HMAC, or SAS method then
- the device should notify the other device with an appropriate ``m.key.verification.cancel``
- message.
-* If the user claims the Short Authentication Strings do not match, the device should
- send an appropriate ``m.key.verification.cancel`` message to the other device.
-* If the device receives a message out of sequence or that it was not expecting, it should
- notify the other device with an appropriate ``m.key.verification.cancel`` message.
-
-
-Verification messages specific to SAS
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-Building off the common framework, the following events are involved in SAS verification.
-
-The ``m.key.verification.cancel`` event is unchanged, however the following error codes
-are used in addition to those already specified:
-
-* ``m.unknown_method``: The devices are unable to agree on the key agreement, hash, MAC,
- or SAS method.
-* ``m.mismatched_commitment``: The hash commitment did not match.
-* ``m.mismatched_sas``: The SAS did not match.
-
-
-{{m_key_verification_start_m_sas_v1_event}}
-
-{{m_key_verification_accept_event}}
-
-{{m_key_verification_key_event}}
-
-{{m_key_verification_mac_event}}
-
-
-.. _sas-hkdf:
-
-HKDF calculation
-<<<<<<<<<<<<<<<<
-
-In all of the SAS methods, HKDF is as defined in `RFC 5869 `_
-and uses the previously agreed-upon hash function for the hash function. The shared
-secret is supplied as the input keying material. No salt is used. When the
-``key_agreement_protocol`` is ``curve25519-hkdf-sha256``, the info parameter is
-the concatenation of:
-
- * The string ``MATRIX_KEY_VERIFICATION_SAS|``.
- * The Matrix ID of the user who sent the ``m.key.verification.start`` message,
- followed by ``|``.
- * The Device ID of the device which sent the ``m.key.verification.start``
- message, followed by ``|``.
- * The public key from the ``m.key.verification.key`` message sent by the device
- which sent the ``m.key.verification.start`` message, followed by ``|``.
- * The Matrix ID of the user who sent the ``m.key.verification.accept`` message,
- followed by ``|``.
- * The Device ID of the device which sent the ``m.key.verification.accept``
- message, followed by ``|``.
- * The public key from the ``m.key.verification.key`` message sent by the device
- which sent the ``m.key.verification.accept`` message, followed by ``|``.
- * The ``transaction_id`` being used.
-
-When the ``key_agreement_protocol`` is the deprecated method ``curve25519``,
-the info parameter is the concatenation of:
-
- * The string ``MATRIX_KEY_VERIFICATION_SAS``.
- * The Matrix ID of the user who sent the ``m.key.verification.start`` message.
- * The Device ID of the device which sent the ``m.key.verification.start`` message.
- * The Matrix ID of the user who sent the ``m.key.verification.accept`` message.
- * The Device ID of the device which sent the ``m.key.verification.accept`` message.
- * The ``transaction_id`` being used.
-
-New implementations are discouraged from implementing the ``curve25519`` method.
-
-.. admonition:: Rationale
-
- HKDF is used over the plain shared secret as it results in a harder attack
- as well as more uniform data to work with.
-
-For verification of each party's device keys, HKDF is as defined in RFC 5869 and
-uses SHA-256 as the hash function. The shared secret is supplied as the input keying
-material. No salt is used, and in the info parameter is the concatenation of:
-
- * The string ``MATRIX_KEY_VERIFICATION_MAC``.
- * The Matrix ID of the user whose key is being MAC-ed.
- * The Device ID of the device sending the MAC.
- * The Matrix ID of the other user.
- * The Device ID of the device receiving the MAC.
- * The ``transaction_id`` being used.
- * The Key ID of the key being MAC-ed, or the string ``KEY_IDS`` if the item
- being MAC-ed is the list of key IDs.
-
-SAS method: ``decimal``
-<<<<<<<<<<<<<<<<<<<<<<<
-
-Generate 5 bytes using `HKDF <#sas-hkdf>`_ then take sequences of 13 bits to
-convert to decimal numbers (resulting in 3 numbers between 0 and 8191 inclusive
-each). Add 1000 to each calculated number.
-
-The bitwise operations to get the numbers given the 5 bytes
-:math:`B_{0}, B_{1}, B_{2}, B_{3}, B_{4}` would be:
-
-* First: :math:`(B_{0} \ll 5 | B_{1} \gg 3) + 1000`
-* Second: :math:`((B_{1} \& 0x7) \ll 10 | B_{2} \ll 2 | B_{3} \gg 6) + 1000`
-* Third: :math:`((B_{3} \& 0x3F) \ll 7 | B_{4} \gg 1) + 1000`
-
-The digits are displayed to the user either with an appropriate separator,
-such as dashes, or with the numbers on individual lines.
-
-SAS method: ``emoji``
-<<<<<<<<<<<<<<<<<<<<<
-
-Generate 6 bytes using `HKDF <#sas-hkdf>`_ then split the first 42 bits into
-7 groups of 6 bits, similar to how one would base64 encode something. Convert
-each group of 6 bits to a number and use the following table to get the corresponding
-emoji:
-
-{{sas_emoji_table}}
-
-.. Note::
- This table is available as JSON at
- https://github.com/matrix-org/matrix-doc/blob/master/data-definitions/sas-emoji.json
-
-.. admonition:: Rationale
-
- The emoji above were chosen to:
-
- * Be recognisable without colour.
- * Be recognisable at a small size.
- * Be recognisable by most cultures.
- * Be distinguishable from each other.
- * Easily described by a few words.
- * Avoid symbols with negative connotations.
- * Be likely similar across multiple platforms.
-
-Clients SHOULD show the emoji with the descriptions from the table, or appropriate
-translation of those descriptions. Client authors SHOULD collaborate to create a
-common set of translations for all languages.
-
-.. Note::
- Known translations for the emoji are available from
- https://github.com/matrix-org/matrix-doc/blob/master/data-definitions/ and can be
- translated online: https://translate.riot.im/projects/matrix-doc/sas-emoji-v1
-
-
-.. section name changed, so make sure that old links keep working
-.. _key-sharing:
-
-Sharing keys between devices
-----------------------------
-
-If Bob has an encrypted conversation with Alice on his computer, and then logs in
-through his phone for the first time, he may want to have access to the previously
-exchanged messages. To address this issue, several methods are provided to
-allow users to transfer keys from one device to another.
-
-Key requests
-~~~~~~~~~~~~
-
-When a device is missing keys to decrypt messages, it can request the keys by
-sending `m.room_key_request`_ to-device messages to other devices with
-``action`` set to ``request``. If a device wishes to share the keys with that
-device, it can forward the keys to the first device by sending an encrypted
-`m.forwarded_room_key`_ to-device message. The first device should then send an
-`m.room_key_request`_ to-device message with ``action`` set to
-``request_cancellation`` to the other devices that it had originally sent the key
-request to; a device that receives a ``request_cancellation`` should disregard any
-previously-received ``request`` message with the same ``request_id`` and
-``requesting_device_id``.
-
-.. NOTE::
-
- Key sharing can be a big attack vector, thus it must be done very carefully.
- A reasonable strategy is for a user's client to only send keys requested by the
- verified devices of the same user.
-
-Server-side key backups
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Devices may upload encrypted copies of keys to the server. When a device tries
-to read a message that it does not have keys for, it may request the key from
-the server and decrypt it. Backups are per-user, and users may replace backups
-with new backups.
-
-In contrast with `Key requests`_, Server-side key backups do not require another
-device to be online from which to request keys. However, as the session keys are
-stored on the server encrypted, it requires users to enter a decryption key to
-decrypt the session keys.
-
-To create a backup, a client will call `POST
-/_matrix/client/r0/room_keys/version`_ and define how the keys are to be
-encrypted through the backup's ``auth_data``; other clients can discover the
-backup by calling `GET /_matrix/client/r0/room_keys/version`_. Keys are
-encrypted according to the backup's ``auth_data`` and added to the backup by
-calling `PUT /_matrix/client/r0/room_keys/keys`_ or one of its variants, and
-can be retrieved by calling `GET /_matrix/client/r0/room_keys/keys`_ or one of
-its variants. Keys can only be written to the most recently created version of
-the backup. Backups can also be deleted using `DELETE
-/_matrix/client/r0/room_keys/version/{version}`_, or individual keys can be
-deleted using `DELETE /_matrix/client/r0/room_keys/keys`_ or one of its
-variants.
-
-Clients must only store keys in backups after they have ensured that the
-``auth_data`` is trusted, either by checking the signatures on it, or by
-deriving the public key from a private key that it obtained from a trusted
-source.
-
-When a client uploads a key for a session that the server already has a key
-for, the server will choose to either keep the existing key or replace it with
-the new key based on the key metadata as follows:
-
-- if the keys have different values for ``is_verified``, then it will keep the
- key that has ``is_verified`` set to ``true``;
-- if they have the same values for ``is_verified``, then it will keep the key
- with a lower ``first_message_index``;
-- and finally, is ``is_verified`` and ``first_message_index`` are equal, then
- it will keep the key with a lower ``forwarded_count``.
-
-Recovery key
-<<<<<<<<<<<<
-
-If the recovery key (the private half of the backup encryption key) is
-presented to the user to save, it is presented as a string constructed as
-follows:
-
-1. The 256-bit curve25519 private key is prepended by the bytes ``0x8B`` and
- ``0x01``
-2. All the bytes in the string above, including the two header bytes, are XORed
- together to form a parity byte. This parity byte is appended to the byte
- string.
-3. The byte string is encoded using base58, using the same `mapping as is used
- for Bitcoin addresses
- `_,
- that is, using the alphabet
- ``123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz``.
-4. A space should be added after every 4th character.
-
-When reading in a recovery key, clients must disregard whitespace, and perform
-the reverse of steps 1 through 3.
-
-Backup algorithm: ``m.megolm_backup.v1.curve25519-aes-sha2``
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-When a backup is created with the ``algorithm`` set to
-``m.megolm_backup.v1.curve25519-aes-sha2``, the ``auth_data`` should have the
-following format:
-
-``AuthData``
-
-.. table::
- :widths: auto
-
- ========== =========== ======================================================
- Parameter Type Description
- ========== =========== ======================================================
- public_key string **Required.** The curve25519 public key used to encrypt
- the backups, encoded in unpadded base64.
- signatures Signatures Optional. Signatures of the ``auth_data``, as Signed
- JSON
- ========== =========== ======================================================
-
-The ``session_data`` field in the backups is constructed as follows:
-
-1. Encode the session key to be backed up as a JSON object with the properties:
-
- .. table::
- :widths: auto
-
- =============================== ======== =========================================
- Parameter Type Description
- =============================== ======== =========================================
- algorithm string **Required.** The end-to-end message
- encryption algorithm that the key is
- for. Must be ``m.megolm.v1.aes-sha2``.
- forwarding_curve25519_key_chain [string] **Required.** Chain of Curve25519 keys
- through which this session was
- forwarded, via
- `m.forwarded_room_key`_ events.
- sender_key string **Required.** Unpadded base64-encoded
- device curve25519 key.
- sender_claimed_keys {string: **Required.** A map from algorithm name
- string} (``ed25519``) to the identity key
- for the sending device.
- session_key string **Required.** Unpadded base64-encoded
- session key in `session-sharing format
- `_.
- =============================== ======== =========================================
-
-2. Generate an ephemeral curve25519 key, and perform an ECDH with the ephemeral
- key and the backup's public key to generate a shared secret. The public
- half of the ephemeral key, encoded using unpadded base64, becomes the ``ephemeral``
- property of the ``session_data``.
-3. Using the shared secret, generate 80 bytes by performing an HKDF using
- SHA-256 as the hash, with a salt of 32 bytes of 0, and with the empty string
- as the info. The first 32 bytes are used as the AES key, the next 32 bytes
- are used as the MAC key, and the last 16 bytes are used as the AES
- initialization vector.
-4. Stringify the JSON object, and encrypt it using AES-CBC-256 with PKCS#7
- padding. This encrypted data, encoded using unpadded base64, becomes the
- ``ciphertext`` property of the ``session_data``.
-5. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256
- using the MAC key generated above. The first 8 bytes of the resulting MAC
- are base64-encoded, and become the ``mac`` property of the ``session_data``.
-
-{{key_backup_cs_http_api}}
-
-Key exports
-~~~~~~~~~~~
-
-Keys can be manually exported from one device to an encrypted file, copied to
-another device, and imported. The file is encrypted using a user-supplied
-passphrase, and is created as follows:
-
-1. Encode the sessions as a JSON object, formatted as described in `Key export
- format`_.
-2. Generate a 512-bit key from the user-entered passphrase by computing
- `PBKDF2`_\(HMAC-SHA-512, passphrase, S, N, 512), where S is a 128-bit
- cryptographically-random salt and N is the number of rounds. N should be at
- least 100,000. The keys K and K' are set to the first and last 256 bits of
- this generated key, respectively. K is used as an AES-256 key, and K' is
- used as an HMAC-SHA-256 key.
-3. Serialize the JSON object as a UTF-8 string, and encrypt it using
- AES-CTR-256 with the key K generated above, and with a 128-bit
- cryptographically-random initialization vector, IV, that has bit 63 set to
- zero. (Setting bit 63 to zero in IV is needed to work around differences in
- implementations of AES-CTR.)
-4. Concatenate the following data:
-
- ============ ===============================================================
- Size (bytes) Description
- ============ ===============================================================
- 1 Export format version, which must be ``0x01``.
- 16 The salt S.
- 16 The initialization vector IV.
- 4 The number of rounds N, as a big-endian unsigned 32-bit integer.
- variable The encrypted JSON object.
- 32 The HMAC-SHA-256 of all the above string concatenated together,
- using K' as the key.
- ============ ===============================================================
-
-5. Base64-encode the string above. Newlines may be added to avoid overly long
- lines.
-6. Prepend the resulting string with ``-----BEGIN MEGOLM SESSION DATA-----``,
- with a trailing newline, and append ``-----END MEGOLM SESSION DATA-----``,
- with a leading and trailing newline.
-
-Key export format
-<<<<<<<<<<<<<<<<<
-
-The exported sessions are formatted as a JSON array of ``SessionData`` objects
-described as follows:
-
-``SessionData``
-
-.. table::
- :widths: auto
-
- =============================== =========== ====================================
- Parameter Type Description
- =============================== =========== ====================================
- algorithm string Required. The encryption algorithm
- that the session uses. Must be
- ``m.megolm.v1.aes-sha2``.
- forwarding_curve25519_key_chain [string] Required. Chain of Curve25519 keys
- through which this session was
- forwarded, via
- `m.forwarded_room_key`_ events.
- room_id string Required. The room where the
- session is used.
- sender_key string Required. The Curve25519 key of the
- device which initiated the session
- originally.
- sender_claimed_keys {string: Required. The Ed25519 key of the
- string} device which initiated the session
- originally.
- session_id string Required. The ID of the session.
- session_key string Required. The key for the session.
- =============================== =========== ====================================
-
-This is similar to the format before encryption used for the session keys in
-`Server-side key backups`_ but adds the ``room_id`` and ``session_id`` fields.
-
-Example:
-
-.. code:: json
-
- [
- {
- "algorithm": "m.megolm.v1.aes-sha2",
- "forwarding_curve25519_key_chain": [
- "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw"
- ],
- "room_id": "!Cuyf34gef24t:localhost",
- "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU",
- "sender_claimed_keys": {
- "ed25519": "",
- },
- "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
- "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..."
- },
- ...
- ]
-
-Messaging Algorithms
---------------------
-
-Messaging Algorithm Names
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Messaging algorithm names use the extensible naming scheme used throughout this
-specification. Algorithm names that start with ``m.`` are reserved for
-algorithms defined by this specification. Implementations wanting to experiment
-with new algorithms must be uniquely globally namespaced following Java's package
-naming conventions.
-
-Algorithm names should be short and meaningful, and should list the primitives
-used by the algorithm so that it is easier to see if the algorithm is using a
-broken primitive.
-
-A name of ``m.olm.v1`` is too short: it gives no information about the primitives
-in use, and is difficult to extend for different primitives. However a name of
-``m.olm.v1.ecdh-curve25519-hdkfsha256.hmacsha256.hkdfsha256-aes256-cbc-hmac64sha256``
-is too long despite giving a more precise description of the algorithm: it adds
-to the data transfer overhead and sacrifices clarity for human readers without
-adding any useful extra information.
-
-``m.olm.v1.curve25519-aes-sha2``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The name ``m.olm.v1.curve25519-aes-sha2`` corresponds to version 1 of the Olm
-ratchet, as defined by the `Olm specification`_. This uses:
-
-* Curve25519 for the initial key agreement.
-* HKDF-SHA-256 for ratchet key derivation.
-* Curve25519 for the root key ratchet.
-* HMAC-SHA-256 for the chain key ratchet.
-* HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256 for authenticated encryption.
-
-Devices that support Olm must include "m.olm.v1.curve25519-aes-sha2" in their
-list of supported messaging algorithms, must list a Curve25519 device key, and
-must publish Curve25519 one-time keys.
-
-An event encrypted using Olm has the following format:
-
-.. code:: json
-
- {
- "type": "m.room.encrypted",
- "content": {
- "algorithm": "m.olm.v1.curve25519-aes-sha2",
- "sender_key": "",
- "ciphertext": {
- "": {
- "type": 0,
- "body": ""
- }
- }
- }
- }
-
-``ciphertext`` is a mapping from device Curve25519 key to an encrypted payload
-for that device. ``body`` is a Base64-encoded Olm message body. ``type`` is an
-integer indicating the type of the message body: 0 for the initial pre-key
-message, 1 for ordinary messages.
-
-Olm sessions will generate messages with a type of 0 until they receive a
-message. Once a session has decrypted a message it will produce messages with
-a type of 1.
-
-When a client receives a message with a type of 0 it must first check if it
-already has a matching session. If it does then it will use that session to
-try to decrypt the message. If there is no existing session then the client
-must create a new session and use the new session to decrypt the message. A
-client must not persist a session or remove one-time keys used by a session
-until it has successfully decrypted a message using that session.
-
-Messages with type 1 can only be decrypted with an existing session. If there
-is no matching session, the client must treat this as an invalid message.
-
-The plaintext payload is of the form:
-
-.. code:: json
-
- {
- "type": "",
- "content": "",
- "sender": "",
- "recipient": "",
- "recipient_keys": {
- "ed25519": ""
- },
- "keys": {
- "ed25519": ""
- }
- }
-
-The type and content of the plaintext message event are given in the payload.
-
-Other properties are included in order to prevent an attacker from publishing
-someone else's curve25519 keys as their own and subsequently claiming to have
-sent messages which they didn't.
-``sender`` must correspond to the user who sent the event, ``recipient`` to
-the local user, and ``recipient_keys`` to the local ed25519 key.
-
-Clients must confirm that the ``sender_key`` and the ``ed25519`` field value
-under the ``keys`` property match the keys returned by |/keys/query|_ for
-the given user, and must also verify the signature of the payload. Without
-this check, a client cannot be sure that the sender device owns the private
-part of the ed25519 key it claims to have in the Olm payload.
-This is crucial when the ed25519 key corresponds to a verified device.
-
-If a client has multiple sessions established with another device, it should
-use the session from which it last received and successfully decrypted a
-message. For these purposes, a session that has not received any messages
-should use its creation time as the time that it last received a message.
-A client may expire old sessions by defining a maximum number of olm sessions
-that it will maintain for each device, and expiring sessions on a Least Recently
-Used basis. The maximum number of olm sessions maintained per device should
-be at least 4.
-
-Recovering from undecryptable messages
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-Occasionally messages may be undecryptable by clients due to a variety of reasons.
-When this happens to an Olm-encrypted message, the client should assume that the Olm
-session has become corrupted and create a new one to replace it.
-
-.. Note::
- Megolm-encrypted messages generally do not have the same problem. Usually the key
- for an undecryptable Megolm-encrypted message will come later, allowing the client
- to decrypt it successfully. Olm does not have a way to recover from the failure,
- making this session replacement process required.
-
-To establish a new session, the client sends a `m.dummy <#m-dummy>`_ to-device event
-to the other party to notify them of the new session details.
-
-Clients should rate-limit the number of sessions it creates per device that it receives
-a message from. Clients should not create a new session with another device if it has
-already created one for that given device in the past 1 hour.
-
-Clients should attempt to mitigate loss of the undecryptable messages. For example,
-Megolm sessions that were sent using the old session would have been lost. The client
-can attempt to retrieve the lost sessions through ``m.room_key_request`` messages.
-
-
-``m.megolm.v1.aes-sha2``
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-The name ``m.megolm.v1.aes-sha2`` corresponds to version 1 of the Megolm
-ratchet, as defined by the `Megolm specification`_. This uses:
-
-* HMAC-SHA-256 for the hash ratchet.
-* HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated HMAC-SHA-256 for authenticated encryption.
-* Ed25519 for message authenticity.
-
-Devices that support Megolm must support Olm, and include "m.megolm.v1.aes-sha2" in
-their list of supported messaging algorithms.
-
-An event encrypted using Megolm has the following format:
-
-.. code:: json
-
- {
- "type": "m.room.encrypted",
- "content": {
- "algorithm": "m.megolm.v1.aes-sha2",
- "sender_key": "",
- "device_id": "",
- "session_id": "",
- "ciphertext": ""
- }
- }
-
-The encrypted payload can contain any message event. The plaintext is of the form:
-
-.. code:: json
-
- {
- "type": "",
- "content": "",
- "room_id": ""
- }
-
-We include the room ID in the payload, because otherwise the homeserver would
-be able to change the room a message was sent in.
-
-Clients must guard against replay attacks by keeping track of the ratchet indices
-of Megolm sessions. They should reject messages with a ratchet index that they
-have already decrypted. Care should be taken in order to avoid false positives, as a
-client may decrypt the same event twice as part of its normal processing.
-
-As with Olm events, clients must confirm that the ``sender_key`` belongs to the user
-who sent the message. The same reasoning applies, but the sender ed25519 key has to be
-inferred from the ``keys.ed25519`` property of the event which established the Megolm
-session.
-
-In order to enable end-to-end encryption in a room, clients can send a
-``m.room.encryption`` state event specifying ``m.megolm.v1.aes-sha2`` as its
-``algorithm`` property.
-
-When creating a Megolm session in a room, clients must share the corresponding session
-key using Olm with the intended recipients, so that they can decrypt future messages
-encrypted using this session. A ``m.room_key`` event is used to do this. Clients
-must also handle ``m.room_key`` events sent by other devices in order to decrypt their
-messages.
-
-Protocol definitions
---------------------
-
-Events
-~~~~~~
-
-{{m_room_encryption_event}}
-
-{{m_room_encrypted_event}}
-
-{{m_room_key_event}}
-
-{{m_room_key_request_event}}
-
-{{m_forwarded_room_key_event}}
-
-{{m_dummy_event}}
-
-Key management API
-~~~~~~~~~~~~~~~~~~
-
-{{keys_cs_http_api}}
-
-
-.. anchor for link from /sync api spec
-.. |device_lists_sync| replace:: End-to-end encryption
-.. _device_lists_sync:
-
-Extensions to /sync
-~~~~~~~~~~~~~~~~~~~
-
-This module adds an optional ``device_lists`` property to the |/sync|_
-response, as specified below. The server need only populate this property for
-an incremental ``/sync`` (ie, one where the ``since`` parameter was
-specified). The client is expected to use |/keys/query|_ or |/keys/changes|_
-for the equivalent functionality after an initial sync, as documented in
-`Tracking the device list for a user`_.
-
-It also adds a ``one_time_keys_count`` property. Note the spelling difference
-with the ``one_time_key_counts`` property in the |/keys/upload|_ response.
-
-.. todo: generate this from a swagger definition?
-
-.. device_lists: { changed: ["@user:server", ... ]},
-
-============ =========== =====================================================
-Parameter Type Description
-============ =========== =====================================================
-device_lists DeviceLists Optional. Information on e2e device updates. Note:
- only present on an incremental sync.
-|device_otk| {string: Optional. For each key algorithm, the number of
- integer} unclaimed one-time keys currently held on the server
- for this device.
-============ =========== =====================================================
-
-``DeviceLists``
-
-========= ========= =============================================
-Parameter Type Description
-========= ========= =============================================
-changed [string] List of users who have updated their device identity keys,
- or who now share an encrypted room with the client since
- the previous sync response.
-left [string] List of users with whom we do not share any encrypted rooms
- anymore since the previous sync response.
-========= ========= =============================================
-
-.. NOTE::
-
- For optimal performance, Alice should be added to ``changed`` in Bob's sync only
- when she adds a new device, or when Alice and Bob now share a room but didn't
- share any room previously. However, for the sake of simpler logic, a server
- may add Alice to ``changed`` when Alice and Bob share a new room, even if they
- previously already shared a room.
-
-Example response:
-
-.. code:: json
-
- {
- "next_batch": "s72595_4483_1934",
- "rooms": {"leave": {}, "join": {}, "invite": {}},
- "device_lists": {
- "changed": [
- "@alice:example.com",
- ],
- "left": [
- "@bob:example.com",
- ],
- },
- "device_one_time_keys_count": {
- "curve25519": 10,
- "signed_curve25519": 20
- }
- }
-
-.. References
-
-.. _ed25519: http://ed25519.cr.yp.to/
-.. _curve25519: https://cr.yp.to/ecdh.html
-.. _`Olm specification`: http://matrix.org/docs/spec/olm.html
-.. _`Megolm specification`: http://matrix.org/docs/spec/megolm.html
-.. _`JSON Web Key`: https://tools.ietf.org/html/rfc7517#appendix-A.3
-.. _`W3C extension`: https://w3c.github.io/webcrypto/#iana-section-jwk
-.. _`PBKDF2`: https://tools.ietf.org/html/rfc2898#section-5.2
-
-.. _`Signing JSON`: ../appendices.html#signing-json
-
-.. |m.olm.v1.curve25519-aes-sha2| replace:: ``m.olm.v1.curve25519-aes-sha2``
-.. |device_otk| replace:: device_one_time_keys_count
-
-.. |/keys/upload| replace:: ``/keys/upload``
-.. _/keys/upload: #post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-upload
-
-.. |/keys/query| replace:: ``/keys/query``
-.. _/keys/query: #post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-query
-
-.. |/keys/claim| replace:: ``/keys/claim``
-.. _/keys/claim: #post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-claim
-
-.. |/keys/changes| replace:: ``/keys/changes``
-.. _/keys/changes: #get-matrix-client-%CLIENT_MAJOR_VERSION%-keys-changes
diff --git a/specification/modules/event_context.rst b/specification/modules/event_context.rst
deleted file mode 100644
index 2d5f22b14c2..00000000000
--- a/specification/modules/event_context.rst
+++ /dev/null
@@ -1,33 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Event Context
-=============
-
-.. _module:event-context:
-
-This API returns a number of events that happened just before and after the
-specified event. This allows clients to get the context surrounding an event.
-
-Client behaviour
-----------------
-
-There is a single HTTP API for retrieving event context, documented below.
-
-{{event_context_cs_http_api}}
-
-Security considerations
------------------------
-
-The server must only return results that the user has permission to see.
diff --git a/specification/modules/guest_access.rst b/specification/modules/guest_access.rst
deleted file mode 100644
index d579da833b0..00000000000
--- a/specification/modules/guest_access.rst
+++ /dev/null
@@ -1,102 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Guest Access
-============
-
-.. _module:guest-access:
-
-There are times when it is desirable for clients to be able to interact with
-rooms without having to fully register for an account on a homeserver or join
-the room. This module specifies how these clients should interact with servers
-in order to participate in rooms as guests.
-
-Guest users retrieve access tokens from a homeserver using the ordinary
-`register endpoint <#post-matrix-client-%CLIENT_MAJOR_VERSION%-register>`_, specifying
-the ``kind`` parameter as ``guest``. They may then interact with the
-client-server API as any other user would, but will only have access to a subset
-of the API as described the Client behaviour subsection below.
-Homeservers may choose not to allow this access at all to their local users, but
-have no information about whether users on other homeservers are guests or not.
-
-Guest users can also upgrade their account by going through the ordinary
-``register`` flow, but specifying the additional POST parameter
-``guest_access_token`` containing the guest's access token. They are also
-required to specify the ``username`` parameter to the value of the local part of
-their username, which is otherwise optional.
-
-This module does not fully factor in federation; it relies on individual
-homeservers properly adhering to the rules set out in this module, rather than
-allowing all homeservers to enforce the rules on each other.
-
-Events
-------
-{{m_room_guest_access_event}}
-
-Client behaviour
-----------------
-The following API endpoints are allowed to be accessed by guest accounts for
-retrieving events:
-
-* `GET /rooms/:room_id/state <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state>`_
-* `GET /rooms/:room_id/context/:event_id <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-context-eventid>`_
-* `GET /rooms/:room_id/event/:event_id <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-event-eventid>`_
-* `GET /rooms/:room_id/state/:event_type/:state_key <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state-eventtype-statekey>`_
-* `GET /rooms/:room_id/messages <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages>`_
-* `GET /rooms/:room_id/initialSync <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync>`_
-* `GET /sync <#get-matrix-client-%CLIENT_MAJOR_VERSION%-sync>`_
-* `GET /events`__ as used for room previews.
-
-__ `peeking_events_api`_
-
-The following API endpoints are allowed to be accessed by guest accounts for
-sending events:
-
-* `POST /rooms/:room_id/join <#post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-join>`_
-* `POST /rooms/:room_id/leave <#post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-leave>`_
-* `PUT /rooms/:room_id/send/m.room.message/:txn_id <#put-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-send-eventtype-txnid>`_
-* `PUT /sendToDevice/{eventType}/{txnId} <#put-matrix-client-%CLIENT_MAJOR_VERSION%-sendtodevice-eventtype-txnid>`_
-
-The following API endpoints are allowed to be accessed by guest accounts for
-their own account maintenance:
-
-* `PUT /profile/:user_id/displayname <#put-matrix-client-%CLIENT_MAJOR_VERSION%-profile-userid-displayname>`_
-* `GET /devices <#get-matrix-client-%CLIENT_MAJOR_VERSION%-devices>`_
-* `GET /devices/{deviceId} <#get-matrix-client-%CLIENT_MAJOR_VERSION%-devices-deviceid>`_
-* `PUT /devices/{deviceId} <#put-matrix-client-%CLIENT_MAJOR_VERSION%-devices-deviceid>`_
-
-The following API endpoints are allowed to be accessed by guest accounts for
-end-to-end encryption:
-
-* `POST /keys/upload <#post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-upload>`_
-* `POST /keys/query <#post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-query>`_
-* `POST /keys/claim <#post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-claim>`_
-
-Server behaviour
-----------------
-Servers MUST only allow guest users to join rooms if the ``m.room.guest_access``
-state event is present on the room, and has the ``guest_access`` value
-``can_join``. If the ``m.room.guest_access`` event is changed to stop this from
-being the case, the server MUST set those users' ``m.room.member`` state to
-``leave``.
-
-Security considerations
------------------------
-Each homeserver manages its own guest accounts itself, and whether an account
-is a guest account or not is not information passed from server to server.
-Accordingly, any server participating in a room is trusted to properly enforce
-the permissions outlined in this section.
-
-Homeservers may want to enable protections such as captchas for guest
-registration to prevent spam, denial of service, and similar attacks.
diff --git a/specification/modules/history_visibility.rst b/specification/modules/history_visibility.rst
deleted file mode 100644
index 84435edb06d..00000000000
--- a/specification/modules/history_visibility.rst
+++ /dev/null
@@ -1,101 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room History Visibility
-=======================
-
-.. _module:history-visibility:
-
-This module adds support for controlling the visibility of previous events in a
-room.
-
-In all cases except ``world_readable``, a user needs to join a room to view events in that room. Once they
-have joined a room, they will gain access to a subset of events in the room. How
-this subset is chosen is controlled by the ``m.room.history_visibility`` event
-outlined below. After a user has left a room, they may see any events which they
-were allowed to see before they left the room, but no events received after they
-left.
-
-The four options for the ``m.room.history_visibility`` event are:
-
-- ``world_readable`` - All events while this is the
- ``m.room.history_visibility`` value may be shared by any participating
- homeserver with anyone, regardless of whether they have ever joined the room.
-- ``shared`` - Previous events are always accessible to newly joined members. All
- events in the room are accessible, even those sent when the member was not a part
- of the room.
-- ``invited`` - Events are accessible to newly joined members from the point
- they were invited onwards. Events stop being accessible when the member's state
- changes to something other than ``invite`` or ``join``.
-- ``joined`` - Events are accessible to newly joined members from the point
- they joined the room onwards. Events stop being accessible when the member's state
- changes to something other than ``join``.
-
-.. WARNING::
- These options are applied at the point an event is *sent*. Checks are
- performed with the state of the ``m.room.history_visibility`` event when the
- event in question is added to the DAG. This means clients cannot
- retrospectively choose to show or hide history to new users if the setting at
- that time was more restrictive.
-
-Events
-------
-
-{{m_room_history_visibility_event}}
-
-Client behaviour
-----------------
-
-Clients that implement this module MUST present to the user the possible options
-for setting history visibility when creating a room.
-
-Clients may want to display a notice that their events may be read by non-joined
-people if the value is set to ``world_readable``.
-
-Server behaviour
-----------------
-
-By default if no ``history_visibility`` is set, or if the value is not understood, the visibility is assumed to be
-``shared``. The rules governing whether a user is allowed to see an event depend
-on the state of the room *at that event*.
-
-1. If the ``history_visibility`` was set to ``world_readable``, allow.
-2. If the user's ``membership`` was ``join``, allow.
-3. If ``history_visibility`` was set to ``shared``, and the user joined the
- room at any point after the event was sent, allow.
-4. If the user's ``membership`` was ``invite``, and the ``history_visibility``
- was set to ``invited``, allow.
-5. Otherwise, deny.
-
-For ``m.room.history_visibility`` events themselves, the user should be allowed
-to see the event if the ``history_visibility`` before *or* after the event
-would allow them to see it. (For example, a user should be able to see
-``m.room.history_visibility`` events which change the ``history_visibility``
-from ``world_readable`` to ``joined`` *or* from ``joined`` to
-``world_readable``, even if that user was not a member of the room.)
-
-Likewise, for the user's own ``m.room.member`` events, the user should be
-allowed to see the event if their ``membership`` before *or* after the event
-would allow them to see it. (For example, a user can always see
-``m.room.member`` events which set their membership to ``join``, or which
-change their membership from ``join`` to any other value, even if
-``history_visibility`` is ``joined``.)
-
-Security considerations
------------------------
-
-The default value for ``history_visibility`` is ``shared`` for
-backwards-compatibility reasons. Clients need to be aware that by not setting
-this event they are exposing all of their room history to anyone in the room.
-
diff --git a/specification/modules/ignore_users.rst b/specification/modules/ignore_users.rst
deleted file mode 100644
index 56a410d1388..00000000000
--- a/specification/modules/ignore_users.rst
+++ /dev/null
@@ -1,62 +0,0 @@
-.. Copyright 2018 Travis Ralston
-..
-.. 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.
-
-Ignoring Users
-==============
-
-.. _module:ignore_users:
-
-With all the communication through Matrix it may be desirable to ignore a
-particular user for whatever reason. This module defines how clients and
-servers can implement the ignoring of users.
-
-Events
-------
-
-{{m_ignored_user_list_event}}
-
-Client behaviour
-----------------
-To ignore a user, effectively blocking them, the client should add the target
-user to the ``m.ignored_user_list`` event in their account data using
-|/user//account_data/|_. Once ignored, the client will no longer
-receive events sent by that user, with the exception of state events. The client
-should either hide previous content sent by the newly ignored user or perform
-a new ``/sync`` with no previous token.
-
-Invites to new rooms by ignored users will not be sent to the client. The server
-may optionally reject the invite on behalf of the client.
-
-State events will still be sent to the client, even if the user is ignored.
-This is to ensure parts, such as the room name, do not appear different to the
-user just because they ignored the sender.
-
-To remove a user from the ignored users list, remove them from the account data
-event. The server will resume sending events from the previously ignored user,
-however it should not send events that were missed while the user was ignored.
-To receive the events that were sent while the user was ignored the client
-should perform a fresh sync. The client may also un-hide any events it previously
-hid due to the user becoming ignored.
-
-Server behaviour
-----------------
-Following an update of the ``m.ignored_user_list``, the sync API for all clients
-should immediately start ignoring (or un-ignoring) the user. Clients are responsible
-for determining if they should hide previously sent events or to start a new sync
-stream.
-
-Servers must still send state events sent by ignored users to clients.
-
-Servers must not send room invites from ignored users to clients. Servers may
-optionally decide to reject the invite, however.
diff --git a/specification/modules/instant_messaging.rst b/specification/modules/instant_messaging.rst
deleted file mode 100644
index 705fd2d954f..00000000000
--- a/specification/modules/instant_messaging.rst
+++ /dev/null
@@ -1,515 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Instant Messaging
-=================
-
-.. _module:im:
-
-This module adds support for sending human-readable messages to a room. It also
-adds support for associating human-readable information with the room itself
-such as a room name and topic.
-
-Events
-------
-
-{{m_room_message_event}}
-
-{{m_room_message_feedback_event}}
-
-Usage of this event is discouraged for several reasons:
- - The number of feedback events will grow very quickly with the number of users
- in the room. This event provides no way to "batch" feedback, unlike the
- `receipts module`_.
- - Pairing feedback to messages gets complicated when paginating as feedback
- arrives before the message it is acknowledging.
- - There are no guarantees that the client has seen the event ID being
- acknowledged.
-
-
-.. _`receipts module`: `module:receipts`_
-
-{{m_room_name_event}}
-
-{{m_room_topic_event}}
-
-{{m_room_avatar_event}}
-
-{{m_room_pinned_events_event}}
-
-m.room.message msgtypes
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Each `m.room.message`_ MUST have a ``msgtype`` key which identifies the type
-of message being sent. Each type has their own required and optional keys, as
-outlined below. If a client cannot display the given ``msgtype`` then it SHOULD
-display the fallback plain text ``body`` key instead.
-
-Some message types support HTML in the event content that clients should prefer
-to display if available. Currently ``m.text``, ``m.emote``, and ``m.notice``
-support an additional ``format`` parameter of ``org.matrix.custom.html``. When
-this field is present, a ``formatted_body`` with the HTML must be provided. The
-plain text version of the HTML should be provided in the ``body``.
-
-Clients should limit the HTML they render to avoid Cross-Site Scripting, HTML
-injection, and similar attacks. The strongly suggested set of HTML tags to permit,
-denying the use and rendering of anything else, is: ``font``, ``del``, ``h1``,
-``h2``, ``h3``, ``h4``, ``h5``, ``h6``, ``blockquote``, ``p``, ``a``, ``ul``,
-``ol``, ``sup``, ``sub``, ``li``, ``b``, ``i``, ``u``, ``strong``, ``em``,
-``strike``, ``code``, ``hr``, ``br``, ``div``, ``table``, ``thead``, ``tbody``,
-``tr``, ``th``, ``td``, ``caption``, ``pre``, ``span``, ``img``.
-
-Not all attributes on those tags should be permitted as they may be avenues for
-other disruption attempts, such as adding ``onclick`` handlers or excessively
-large text. Clients should only permit the attributes listed for the tags below.
-Where ``data-mx-bg-color`` and ``data-mx-color`` are listed, clients should
-translate the value (a 6-character hex color code) to the appropriate CSS/attributes
-for the tag.
-
-
-:``font``:
- ``data-mx-bg-color``, ``data-mx-color``
-
-:``span``:
- ``data-mx-bg-color``, ``data-mx-color``
-
-:``a``:
- ``name``, ``target``, ``href`` (provided the value is not relative and has a scheme
- matching one of: ``https``, ``http``, ``ftp``, ``mailto``, ``magnet``)
-
-:``img``:
- ``width``, ``height``, ``alt``, ``title``, ``src`` (provided it is a `Matrix Content (MXC) URI`_)
-
-:``ol``:
- ``start``
-
-:``code``:
- ``class`` (only classes which start with ``language-`` for syntax highlighting)
-
-
-Additionally, web clients should ensure that *all* ``a`` tags get a ``rel="noopener"``
-to prevent the target page from referencing the client's tab/window.
-
-Tags must not be nested more than 100 levels deep. Clients should only support the subset
-of tags they can render, falling back to other representations of the tags where possible.
-For example, a client may not be able to render tables correctly and instead could fall
-back to rendering tab-delimited text.
-
-In addition to not rendering unsafe HTML, clients should not emit unsafe HTML in events.
-Likewise, clients should not generate HTML that is not needed, such as extra paragraph tags
-surrounding text due to Rich Text Editors. HTML included in events should otherwise be valid,
-such as having appropriate closing tags, appropriate attributes (considering the custom ones
-defined in this specification), and generally valid structure.
-
-A special tag, ``mx-reply``, may appear on rich replies (described below) and should be
-allowed if, and only if, the tag appears as the very first tag in the ``formatted_body``.
-The tag cannot be nested and cannot be located after another tag in the tree. Because the
-tag contains HTML, an ``mx-reply`` is expected to have a partner closing tag and should
-be treated similar to a ``div``. Clients that support rich replies will end up stripping
-the tag and its contents and therefore may wish to exclude the tag entirely.
-
-.. Note::
- A future iteration of the specification will support more powerful and extensible
- message formatting options, such as the proposal `MSC1225 `_.
-
-{{msgtype_events}}
-
-
-Client behaviour
-----------------
-
-Clients SHOULD verify the structure of incoming events to ensure that the
-expected keys exist and that they are of the right type. Clients can discard
-malformed events or display a placeholder message to the user. Redacted
-``m.room.message`` events MUST be removed from the client. This can either be
-replaced with placeholder text (e.g. "[REDACTED]") or the redacted message can
-be removed entirely from the messages view.
-
-Events which have attachments (e.g. ``m.image``, ``m.file``) SHOULD be
-uploaded using the `content repository module`_ where available. The
-resulting ``mxc://`` URI can then be used in the ``url`` key.
-
-Clients MAY include a client generated thumbnail image for an attachment under
-a ``info.thumbnail_url`` key. The thumbnail SHOULD also be a ``mxc://`` URI.
-Clients displaying events with attachments can either use the client generated
-thumbnail or ask its homeserver to generate a thumbnail from the original
-attachment using the `content repository module`_.
-
-.. _`content repository module`: `module:content`_
-
-Recommendations when sending messages
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In the event of send failure, clients SHOULD retry requests using an
-exponential-backoff algorithm for a
-certain amount of time T. It is recommended that T is no longer than 5 minutes.
-After this time, the client should stop retrying and mark the message as "unsent".
-Users should be able to manually resend unsent messages.
-
-Users may type several messages at once and send them all in quick succession.
-Clients SHOULD preserve the order in which they were sent by the user. This
-means that clients should wait for the response to the previous request before
-sending the next request. This can lead to head-of-line blocking. In order to
-reduce the impact of head-of-line blocking, clients should use a queue per room
-rather than a global queue, as ordering is only relevant within a single room
-rather than between rooms.
-
-Local echo
-~~~~~~~~~~
-
-Messages SHOULD appear immediately in the message view when a user presses the
-"send" button. This should occur even if the message is still sending. This is
-referred to as "local echo". Clients SHOULD implement "local echo" of messages.
-Clients MAY display messages in a different format to indicate that the server
-has not processed the message. This format should be removed when the server
-responds.
-
-Clients need to be able to match the message they are sending with the same
-message which they receive from the event stream. The echo of the same message
-from the event stream is referred to as "remote echo". Both echoes need to be
-identified as the same message in order to prevent duplicate messages being
-displayed. Ideally this pairing would occur transparently to the user: the UI
-would not flicker as it transitions from local to remote. Flickering can be
-reduced through clients making use of the transaction ID they used to send
-a particular event. The transaction ID used will be included in the event's
-``unsigned`` data as ``transaction_id`` when it arrives through the event stream.
-
-Clients unable to make use of the transaction ID are likely to experience
-flickering when the remote echo arrives on the event stream *before*
-the request to send the message completes. In that case the event
-arrives before the client has obtained an event ID, making it impossible to
-identify it as a remote echo. This results in the client displaying the message
-twice for some time (depending on the server responsiveness) before the original
-request to send the message completes. Once it completes, the client can take
-remedial actions to remove the duplicate event by looking for duplicate event IDs.
-
-
-Calculating the display name for a user
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Clients may wish to show the human-readable display name of a room member as
-part of a membership list, or when they send a message. However, different
-members may have conflicting display names. Display names MUST be disambiguated
-before showing them to the user, in order to prevent spoofing of other users.
-
-To ensure this is done consistently across clients, clients SHOULD use the
-following algorithm to calculate a disambiguated display name for a given user:
-
-1. Inspect the ``m.room.member`` state event for the relevant user id.
-2. If the ``m.room.member`` state event has no ``displayname`` field, or if
- that field has a ``null`` value, use the raw user id as the display
- name. Otherwise:
-3. If the ``m.room.member`` event has a ``displayname`` which is unique among
- members of the room with ``membership: join`` or ``membership: invite``, use
- the given ``displayname`` as the user-visible display name. Otherwise:
-4. The ``m.room.member`` event has a non-unique ``displayname``. This should be
- disambiguated using the user id, for example "display name
- (@id:homeserver.org)".
-
- .. TODO-spec
- what does it mean for a ``displayname`` to be 'unique'? Are we
- case-sensitive? Do we care about homograph attacks? See
- https://matrix.org/jira/browse/SPEC-221.
-
-Developers should take note of the following when implementing the above
-algorithm:
-
-* The user-visible display name of one member can be affected by changes in the
- state of another member. For example, if ``@user1:matrix.org`` is present in
- a room, with ``displayname: Alice``, then when ``@user2:example.com`` joins
- the room, also with ``displayname: Alice``, *both* users must be given
- disambiguated display names. Similarly, when one of the users then changes
- their display name, there is no longer a clash, and *both* users can be given
- their chosen display name. Clients should be alert to this possibility and
- ensure that all affected users are correctly renamed.
-
-* The display name of a room may also be affected by changes in the membership
- list. This is due to the room name sometimes being based on user display
- names (see `Calculating the display name for a room`_).
-
-* If the entire membership list is searched for clashing display names, this
- leads to an O(N^2) implementation for building the list of room members. This
- will be very inefficient for rooms with large numbers of members. It is
- recommended that client implementations maintain a hash table mapping from
- ``displayname`` to a list of room members using that name. Such a table can
- then be used for efficient calculation of whether disambiguation is needed.
-
-
-Displaying membership information with messages
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Clients may wish to show the display name and avatar URL of the room member who
-sent a message. This can be achieved by inspecting the ``m.room.member`` state
-event for that user ID (see `Calculating the display name for a user`_).
-
-When a user paginates the message history, clients may wish to show the
-**historical** display name and avatar URL for a room member. This is possible
-because older ``m.room.member`` events are returned when paginating. This can
-be implemented efficiently by keeping two sets of room state: old and current.
-As new events arrive and/or the user paginates back in time, these two sets of
-state diverge from each other. New events update the current state and paginated
-events update the old state. When paginated events are processed sequentially,
-the old state represents the state of the room *at the time the event was sent*.
-This can then be used to set the historical display name and avatar URL.
-
-
-Calculating the display name for a room
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Clients may wish to show a human-readable name for a room. There are a number
-of possibilities for choosing a useful name. To ensure that rooms are named
-consistently across clients, clients SHOULD use the following algorithm to
-choose a name:
-
-1. If the room has an `m.room.name`_ state event with a non-empty ``name``
- field, use the name given by that field.
-
-#. If the room has an `m.room.canonical_alias`_ state event with a valid
- ``alias`` field, use the alias given by that field as the name. Note that
- clients should avoid using ``alt_aliases`` when calculating the room name.
-
-#. If none of the above conditions are met, a name should be composed based
- on the members of the room. Clients should consider `m.room.member`_ events
- for users other than the logged-in user, as defined below.
-
- i. If the number of ``m.heroes`` for the room are greater or equal to
- ``m.joined_member_count + m.invited_member_count - 1``, then use the
- membership events for the heroes to calculate display names for the
- users (`disambiguating them if required`_) and concatenating them. For
- example, the client may choose to show "Alice, Bob, and Charlie
- (@charlie:example.org)" as the room name. The client may optionally
- limit the number of users it uses to generate a room name.
-
- #. If there are fewer heroes than ``m.joined_member_count + m.invited_member_count
- - 1``, and ``m.joined_member_count + m.invited_member_count`` is greater
- than 1, the client should use the heroes to calculate display names for
- the users (`disambiguating them if required`_) and concatenating them
- alongside a count of the remaining users. For example, "Alice, Bob, and
- 1234 others".
-
- #. If ``m.joined_member_count + m.invited_member_count`` is less than or
- equal to 1 (indicating the member is alone), the client should use the
- rules above to indicate that the room was empty. For example, "Empty
- Room (was Alice)", "Empty Room (was Alice and 1234 others)", or
- "Empty Room" if there are no heroes.
-
-Clients SHOULD internationalise the room name to the user's language when using
-the ``m.heroes`` to calculate the name. Clients SHOULD use minimum 5 heroes to
-calculate room names where possible, but may use more or less to fit better with
-their user experience.
-
-.. _`disambiguating them if required`: `Calculating the display name for a user`_
-
-Forming relationships between events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In some cases, events may wish to reference other events. This could be to form
-a thread of messages for the user to follow along with, or to provide more context
-as to what a particular event is describing. Currently, the only kind of relation
-defined is a "rich reply" where a user may reference another message to create a
-thread-like conversation.
-
-Relationships are defined under an ``m.relates_to`` key in the event's ``content``.
-If the event is of the type ``m.room.encrypted``, the ``m.relates_to`` key MUST NOT
-be covered by the encryption and instead be put alongside the encryption information
-held in the ``content``.
-
-
-Rich replies
-++++++++++++
-
-Users may wish to reference another message when forming their own message, and
-clients may wish to better embed the referenced message for the user to have a
-better context for the conversation being had. This sort of embedding another
-message in a message is known as a "rich reply", or occasionally just a "reply".
-
-A rich reply is formed through use of an ``m.relates_to`` relation for ``m.in_reply_to``
-where a single key, ``event_id``, is used to reference the event being replied to.
-The referenced event ID SHOULD belong to the same room where the reply is being sent.
-Clients should be cautious of the event ID belonging to another room, or being invalid
-entirely. Rich replies can only be constructed in the form of ``m.room.message`` events
-with a ``msgtype`` of ``m.text`` or ``m.notice``. Due to the fallback requirements, rich
-replies cannot be constructed for types of ``m.emote``, ``m.file``, etc. Rich replies
-may reference any other ``m.room.message`` event, however. Rich replies may reference
-another event which also has a rich reply, infinitely.
-
-An ``m.in_reply_to`` relationship looks like the following::
-
- {
- ...
- "type": "m.room.message",
- "content": {
- "msgtype": "m.text",
- "body": "",
- "format": "org.matrix.custom.html",
- "formatted_body": "",
- "m.relates_to": {
- "m.in_reply_to": {
- "event_id": "$another:event.com"
- }
- }
- }
- }
-
-
-Fallbacks and event representation
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some clients may not have support for rich replies and therefore need a fallback
-to use instead. Clients that do not support rich replies should render the event
-as if rich replies were not special.
-
-Clients that do support rich replies MUST provide the fallback format on replies,
-and MUST strip the fallback before rendering the reply. Rich replies MUST have
-a ``format`` of ``org.matrix.custom.html`` and therefore a ``formatted_body``
-alongside the ``body`` and appropriate ``msgtype``. The specific fallback text
-is different for each ``msgtype``, however the general format for the ``body`` is:
-
-.. code-block:: text
-
- > <@alice:example.org> This is the original body
-
- This is where the reply goes
-
-
-The ``formatted_body`` should use the following template:
-
-.. code-block:: html
-
-
-
-
- This is where the reply goes.
-
-
-If the related event does not have a ``formatted_body``, the event's ``body`` should
-be considered after encoding any HTML special characters. Note that the ``href`` in
-both of the anchors use a `matrix.to URI <../appendices.html#matrix-to-navigation>`_.
-
-Stripping the fallback
-``````````````````````
-
-Clients which support rich replies MUST strip the fallback from the event before
-rendering the event. This is because the text provided in the fallback cannot be
-trusted to be an accurate representation of the event. After removing the fallback,
-clients are recommended to represent the event referenced by ``m.in_reply_to``
-similar to the fallback's representation, although clients do have creative freedom
-for their user interface. Clients should prefer the ``formatted_body`` over the
-``body``, just like with other ``m.room.message`` events.
-
-To strip the fallback on the ``body``, the client should iterate over each line of
-the string, removing any lines that start with the fallback prefix ("> ",
-including the space, without quotes) and stopping when a line is encountered without
-the prefix. This prefix is known as the "fallback prefix sequence".
-
-To strip the fallback on the ``formatted_body``, the client should remove the
-entirety of the ``mx-reply`` tag.
-
-Fallback for ``m.text``, ``m.notice``, and unrecognised message types
-`````````````````````````````````````````````````````````````````````
-
-Using the prefix sequence, the first line of the related event's ``body`` should
-be prefixed with the user's ID, followed by each line being prefixed with the fallback
-prefix sequence. For example::
-
- > <@alice:example.org> This is the first line
- > This is the second line
-
- This is the reply
-
-
-The ``formatted_body`` uses the template defined earlier in this section.
-
-Fallback for ``m.emote``
-````````````````````````
-
-Similar to the fallback for ``m.text``, each line gets prefixed with the fallback
-prefix sequence. However an asterisk should be inserted before the user's ID, like
-so::
-
- > * <@alice:example.org> feels like today is going to be a great day
-
- This is the reply
-
-
-The ``formatted_body`` has a subtle difference for the template where the asterisk
-is also inserted ahead of the user's ID:
-
-.. code-block:: html
-
-
-
-
- This is where the reply goes.
-
-
-Fallback for ``m.image``, ``m.video``, ``m.audio``, and ``m.file``
-``````````````````````````````````````````````````````````````````
-
-The related event's ``body`` would be a file name, which may not be very descriptive.
-The related event should additionally not have a ``format`` or ``formatted_body``
-in the ``content`` - if the event does have a ``format`` and/or ``formatted_body``,
-those fields should be ignored. Because the filename alone may not be descriptive,
-the related event's ``body`` should be considered to be ``"sent a file."`` such that
-the output looks similar to the following::
-
- > <@alice:example.org> sent a file.
-
- This is the reply
-
-
-.. code-block:: html
-
-
-
-
- This is where the reply goes.
-
-
-For ``m.image``, the text should be ``"sent an image."``. For ``m.video``, the text
-should be ``"sent a video."``. For ``m.audio``, the text should be ``"sent an audio file"``.
-
-
-Server behaviour
-----------------
-
-Homeservers SHOULD reject ``m.room.message`` events which don't have a
-``msgtype`` key, or which don't have a textual ``body`` key, with an HTTP status
-code of 400.
-
-Security considerations
------------------------
-
-Messages sent using this module are not encrypted, although end to end encryption is in development (see `E2E module`_).
-
-Clients should sanitise **all displayed keys** for unsafe HTML to prevent Cross-Site
-Scripting (XSS) attacks. This includes room names and topics.
-
-.. _`E2E module`: `module:e2e`_
-.. _`Matrix Content (MXC) URI`: `module:content`_
diff --git a/specification/modules/mentions.rst b/specification/modules/mentions.rst
deleted file mode 100644
index dc078f3b6c7..00000000000
--- a/specification/modules/mentions.rst
+++ /dev/null
@@ -1,74 +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.
-
-User, room, and group mentions
-==============================
-
-.. _module:mentions:
-
-This module allows users to mention other users, rooms, and groups within
-a room message. This is achieved by including a `matrix.to URI`_ in the HTML
-body of an `m.room.message`_ event. This module does not have any server-specific
-behaviour to it.
-
-Mentions apply only to `m.room.message`_ events where the ``msgtype`` is ``m.text``,
-``m.emote``, or ``m.notice``. The ``format`` for the event must be ``org.matrix.custom.html``
-and therefore requires a ``formatted_body``.
-
-To make a mention, reference the entity being mentioned in the ``formatted_body``
-using an anchor, like so::
-
- {
- "body": "Hello Alice!",
- "msgtype": "m.text",
- "format": "org.matrix.custom.html",
- "formatted_body": "Hello Alice!"
- }
-
-
-Client behaviour
-----------------
-
-In addition to using the appropriate ``matrix.to URI`` for the mention,
-clients should use the following guidelines when making mentions in events
-to be sent:
-
-* When mentioning users, use the user's potentially ambiguous display name for
- the anchor's text. If the user does not have a display name, use the user's
- ID.
-
-* When mentioning rooms, use the canonical alias for the room. If the room
- does not have a canonical alias, prefer one of the aliases listed on the
- room. If no alias can be found, fall back to the room ID. In all cases,
- use the alias/room ID being linked to as the anchor's text.
-
-* When referencing groups, use the group ID as the anchor's text.
-
-The text component of the anchor should be used in the event's ``body`` where
-the mention would normally be represented, as shown in the example above.
-
-Clients should display mentions differently from other elements. For example,
-this may be done by changing the background color of the mention to indicate
-that it is different from a normal link.
-
-If the current user is mentioned in a message (either by a mention as defined
-in this module or by a push rule), the client should show that mention differently
-from other mentions, such as by using a red background color to signify to the
-user that they were mentioned.
-
-When clicked, the mention should navigate the user to the appropriate room, group,
-or user information.
-
-
-.. _`matrix.to URI`: ../appendices.html#matrix-to-navigation
\ No newline at end of file
diff --git a/specification/modules/moderation_policies.rst b/specification/modules/moderation_policies.rst
deleted file mode 100644
index 2e85fb07840..00000000000
--- a/specification/modules/moderation_policies.rst
+++ /dev/null
@@ -1,128 +0,0 @@
-.. Copyright 2020 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-Moderation policy lists
-=======================
-
-.. _module:moderation-policies:
-
-With Matrix being an open network where anyone can participate, a very wide
-range of content exists and it is important that users are empowered to select
-which content they wish to see, and which content they wish to block. By
-extension, room moderators and server admins should also be able to select
-which content they do not wish to host in their rooms and servers.
-
-The protocol's position on this is one of neutrality: it should not be deciding
-what content is undesirable for any particular entity and should instead be
-empowering those entities to make their own decisions. As such, a generic
-framework for communicating "moderation policy lists" or "moderation policy rooms"
-is described. Note that this module only describes the data structures and not
-how they should be interpreting: the entity making the decisions on filtering
-is best positioned to interpret the rules how it sees fit.
-
-Moderation policy lists are stored as room state events. There are no restrictions
-on how the rooms can be configured (they could be public, private, encrypted, etc).
-
-There are currently 3 kinds of entities which can be affected by rules: ``user``,
-``server``, and ``room``. All 3 are described with ``m.policy.rule.`` state
-events. The ``state_key`` for a policy rule is an arbitrary string decided by the
-sender of the rule.
-
-Rules contain recommendations and reasons for the rule existing. The ``reason``
-is a human-readable string which describes the ``recommendation``. Currently only
-one recommendation, ``m.ban``, is specified.
-
-``m.ban`` recommendation
-------------------------
-
-When this recommendation is used, the entities affected by the rule should be
-banned from participation where possible. The enforcement of this is deliberately
-left as an implementation detail to avoid the protocol imposing its opinion on how
-the policy list is to be interpreted. However, a suggestion for a simple implementation
-is as follows:
-
-* Is a ``user`` rule...
-
- * Applied to a user: The user should be added to the subscriber's ignore list.
- * Applied to a room: The user should be banned from the room (either on sight or immediately).
- * Applied to a server: The user should not be allowed to send invites to users on the server.
-
-* Is a ``room`` rule...
-
- * Applied to a user: The user should leave the room and not join it
- (`MSC2270 `_-style ignore).
- * Applied to a room: No-op because a room cannot ban itself.
- * Applied to a server: The server should prevent users from joining the room and from receiving
- invites to it.
-
-* Is a ``server`` rule...
-
- * Applied to a user: The user should not receive events or invites from the server.
- * Applied to a room: The server is added as a denied server in the ACLs.
- * Applied to a server: The subscriber should avoid federating with the server as much as
- possible by blocking invites from the server and not sending traffic unless strictly
- required (no outbound invites).
-
-Subscribing to policy lists
----------------------------
-
-This is deliberatly left as an implementation detail. For implementations using the
-Client-Server API, this could be as easy as joining or peeking the room. Joining or peeking
-is not required, however: an implementation could poll for updates or use a different
-technique for receiving updates to the policy's rules.
-
-Sharing
--------
-
-In addition to sharing a direct reference to the room which contains the policy's rules,
-plain http or https URLs can be used to share links to the list. When the URL is approached
-with a ``Accept: application/json`` header or has ``.json`` appended to the end of the URL, it
-should return a JSON object containing a ``room_uri`` property which references the room.
-Currently this would be a ``matrix.to`` URI, however in future it could be a Matrix-schemed
-URI instead. When not approached with the intent of JSON, the service could return a
-user-friendly page describing what is included in the ban list.
-
-Events
-------
-
-The ``entity`` described by the state events can contain ``*`` and ``?`` to match zero or more
-and one or more characters respectively. Note that rules against rooms can describe a room ID
-or room alias - the subscriber is responsible for resolving the alias to a room ID if desired.
-
-{{m_policy_rule_user_event}}
-
-{{m_policy_rule_room_event}}
-
-{{m_policy_rule_server_event}}
-
-Client behaviour
-----------------
-As described above, the client behaviour is deliberatly left undefined.
-
-Server behaviour
-----------------
-Servers have no additional requirements placed on them by this module.
-
-Security considerations
------------------------
-This module could be used to build a system of shared blacklists, which may create
-a divide within established communities if not carefully deployed. This may well not
-be a suitable solution for all communities.
-
-Depending on how implementations handle subscriptions, user IDs may be linked to
-policy lists and therefore expose the views of that user. For example, a client implementation
-which joins the user to the policy room would expose the user's ID to observers of the
-policy room. In future, `MSC1228 `_
-and `MSC1777 `_ (or similar) could
-help solve this concern.
diff --git a/specification/modules/openid.rst b/specification/modules/openid.rst
deleted file mode 100644
index 63406719cff..00000000000
--- a/specification/modules/openid.rst
+++ /dev/null
@@ -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.
-
-OpenID
-======
-
-.. _module:openid:
-
-This module allows users to verify their identity with a third party service. The
-third party service does need to be matrix-aware in that it will need to know to
-resolve matrix homeservers to exchange the user's token for identity information.
-
-{{openid_cs_http_api}}
diff --git a/specification/modules/presence.rst b/specification/modules/presence.rst
deleted file mode 100644
index 53a33550965..00000000000
--- a/specification/modules/presence.rst
+++ /dev/null
@@ -1,88 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Presence
-========
-
-.. _module:presence:
-
-Each user has the concept of presence information. This encodes:
-
-* Whether the user is currently online
-* How recently the user was last active (as seen by the server)
-* Whether a given client considers the user to be currently idle
-* Arbitrary information about the user's current status (e.g. "in a meeting").
-
-This information is collated from both per-device (``online``, ``idle``,
-``last_active``) and per-user (status) data, aggregated by the user's homeserver
-and transmitted as an ``m.presence`` event. Presence events are sent to
-interested parties where users share a room membership.
-
-User's presence state is represented by the ``presence`` key, which is an enum
-of one of the following:
-
-- ``online`` : The default state when the user is connected to an event
- stream.
-- ``unavailable`` : The user is not reachable at this time e.g. they are
- idle.
-- ``offline`` : The user is not connected to an event stream or is
- explicitly suppressing their profile information from being sent.
-
-Events
-------
-
-{{presence_events}}
-
-Client behaviour
-----------------
-
-Clients can manually set/get their presence using the HTTP APIs listed below.
-
-{{presence_cs_http_api}}
-
-Last active ago
-~~~~~~~~~~~~~~~
-The server maintains a timestamp of the last time it saw a pro-active event from
-the user. A pro-active event may be sending a message to a room or changing
-presence state to ``online``. This timestamp is presented via a key called
-``last_active_ago`` which gives the relative number of milliseconds since the
-pro-active event.
-
-To reduce the number of presence updates sent to clients the server may include
-a ``currently_active`` boolean field when the presence state is ``online``. When
-true, the server will not send further updates to the last active time until an
-update is sent to the client with either a) ``currently_active`` set to false or
-b) a presence state other than ``online``. During this period clients must
-consider the user to be currently active, irrespective of the last active time.
-
-The last active time must be up to date whenever the server gives a presence
-event to the client. The ``currently_active`` mechanism should purely be used by
-servers to stop sending continuous presence updates, as opposed to disabling
-last active tracking entirely. Thus clients can fetch up to date last active
-times by explicitly requesting the presence for a given user.
-
-Idle timeout
-~~~~~~~~~~~~
-
-The server will automatically set a user's presence to ``unavailable`` if their
-last active time was over a threshold value (e.g. 5 minutes). Clients can
-manually set a user's presence to ``unavailable``. Any activity that bumps the
-last active time on any of the user's clients will cause the server to
-automatically set their presence to ``online``.
-
-Security considerations
------------------------
-
-Presence information is shared with all users who share a room with the target
-user. In large public rooms this could be undesirable.
diff --git a/specification/modules/push.rst b/specification/modules/push.rst
deleted file mode 100644
index ec855e6b4b7..00000000000
--- a/specification/modules/push.rst
+++ /dev/null
@@ -1,768 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-Push Notifications
-==================
-
-.. _module:push:
-
-::
-
- +--------------------+ +-------------------+
- Matrix HTTP | | | |
- Notification Protocol | App Developer | | Device Vendor |
- | | | |
- +-------------------+ | +----------------+ | | +---------------+ |
- | | | | | | | | | |
- | Matrix homeserver +-----> Push Gateway +------> Push Provider | |
- | | | | | | | | | |
- +-^-----------------+ | +----------------+ | | +----+----------+ |
- | | | | | |
- Matrix | | | | | |
- Client/Server API + | | | | |
- | | +--------------------+ +-------------------+
- | +--+-+ |
- | | <-------------------------------------------+
- +---+ |
- | | Provider Push Protocol
- +----+
-
- Mobile Device or Client
-
-
-This module adds support for push notifications. Homeservers send notifications
-of events to user-configured HTTP endpoints. Users may also configure a
-number of rules that determine which events generate notifications. These are
-all stored and managed by the user's homeserver. This allows user-specific push
-settings to be reused between client applications.
-
-The above diagram shows the flow of push notifications being sent to a handset
-where push notifications are submitted via the handset vendor, such as Apple's
-APNS or Google's GCM. This happens as follows:
-
-1. The client app signs in to a homeserver.
-2. The client app registers with its vendor's Push Provider and
- obtains a routing token of some kind.
-3. The mobile app uses the Client/Server API to add a 'pusher', providing the
- URL of a specific Push Gateway which is configured for that
- application. It also provides the routing token it has acquired from the
- Push Provider.
-4. The homeserver starts sending HTTP requests to the Push Gateway using the
- supplied URL. The Push Gateway relays this notification to
- the Push Provider, passing the routing token along with any
- necessary private credentials the provider requires to send push
- notifications.
-5. The Push Provider sends the notification to the device.
-
-Definitions for terms used in this section are below:
-
-Push Provider
- A push provider is a service managed by the device vendor which can send
- notifications directly to the device. Google Cloud Messaging (GCM) and Apple
- Push Notification Service (APNS) are two examples of push providers.
-
-Push Gateway
- A push gateway is a server that receives HTTP event notifications from
- homeservers and passes them on to a different protocol such as APNS for iOS
- devices or GCM for Android devices. Clients inform the homeserver which
- Push Gateway to send notifications to when it sets up a Pusher.
-
-.. _def:pushers:
-
-Pusher
- A pusher is a worker on the homeserver that manages the sending
- of HTTP notifications for a user. A user can have multiple pushers: one per
- device.
-
-Push Rule
- A push rule is a single rule that states under what *conditions* an event should
- be passed onto a push gateway and *how* the notification should be presented.
- These rules are stored on the user's homeserver. They are manually configured
- by the user, who can create and view them via the Client/Server API.
-
-Push Ruleset
- A push ruleset *scopes a set of rules according to some criteria*. For example,
- some rules may only be applied for messages from a particular sender,
- a particular room, or by default. The push ruleset contains the entire set
- of scopes and rules.
-
-Client behaviour
-----------------
-
-Clients MUST configure a Pusher before they will receive push notifications.
-There is a single API endpoint for this, as described below.
-
-{{pusher_cs_http_api}}
-
-.. _pushers: `def:pushers`_
-
-Listing Notifications
-~~~~~~~~~~~~~~~~~~~~~
-
-A client can retrieve a list of events that it has been notified about. This
-may be useful so that users can see a summary of what important messages they
-have received.
-
-{{notifications_cs_http_api}}
-
-Receiving notifications
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Servers MUST include the number of unread notifications in a client's ``/sync``
-stream, and MUST update it as it changes. Notifications are determined by the
-push rules which apply to an event.
-
-When the user updates their read receipt (either by using the API or by sending an
-event), notifications prior to and including that event MUST be marked as read.
-
-Push Rules
-~~~~~~~~~~
-A push rule is a single rule that states under what *conditions* an event should
-be passed onto a push gateway and *how* the notification should be presented.
-There are different "kinds" of push rules and each rule has an associated
-priority. Every push rule MUST have a ``kind`` and ``rule_id``. The ``rule_id``
-is a unique string within the kind of rule and its' scope: ``rule_ids`` do not
-need to be unique between rules of the same kind on different devices. Rules may
-have extra keys depending on the value of ``kind``.
-
-The different ``kind``\ s of rule, in the order that they are checked, are:
-
-Override Rules ``override``
- The highest priority rules are user-configured overrides.
-Content-specific Rules ``content``
- These configure behaviour for (unencrypted) messages that match certain
- patterns. Content rules take one parameter: ``pattern``, that gives the glob
- pattern to match against. This is treated in the same way as ``pattern`` for
- ``event_match``.
-Room-specific Rules ``room``
- These rules change the behaviour of all messages for a given room. The
- ``rule_id`` of a room rule is always the ID of the room that it affects.
-Sender-specific rules ``sender``
- These rules configure notification behaviour for messages from a specific
- Matrix user ID. The ``rule_id`` of Sender rules is always the Matrix user
- ID of the user whose messages they'd apply to.
-Underride rules ``underride``
- These are identical to ``override`` rules, but have a lower priority than
- ``content``, ``room`` and ``sender`` rules.
-
-Rules with the same ``kind`` can specify an ordering priority. This determines
-which rule is selected in the event of multiple matches. For example, a rule
-matching "tea" and a separate rule matching "time" would both match the sentence
-"It's time for tea". The ordering of the rules would then resolve the tiebreak
-to determine which rule is executed. Only ``actions`` for highest priority rule
-will be sent to the Push Gateway.
-
-Each rule can be enabled or disabled. Disabled rules never match. If no rules
-match an event, the homeserver MUST NOT notify the Push Gateway for that event.
-Homeservers MUST NOT notify the Push Gateway for events that the user has sent
-themselves.
-
-Actions
-+++++++
-All rules have an associated list of ``actions``. An action affects if and how a
-notification is delivered for a matching event. The following actions are defined:
-
-``notify``
- This causes each matching event to generate a notification.
-``dont_notify``
- This prevents each matching event from generating a notification
-``coalesce``
- This enables notifications for matching events but activates homeserver
- specific behaviour to intelligently coalesce multiple events into a single
- notification. Not all homeservers may support this. Those that do not support
- it should treat it as the ``notify`` action.
-``set_tweak``
- Sets an entry in the ``tweaks`` dictionary key that is sent in the notification
- request to the Push Gateway. This takes the form of a dictionary with a
- ``set_tweak`` key whose value is the name of the tweak to set. It may also
- have a ``value`` key which is the value to which it should be set.
-
-Actions that have no parameters are represented as a string. Otherwise, they are
-represented as a dictionary with a key equal to their name and other keys as
-their parameters, e.g. ``{ "set_tweak": "sound", "value": "default" }``
-
-Tweaks
-^^^^^^
-The ``set_tweak`` action is used to add an entry to the 'tweaks' dictionary
-that is sent in the notification request to the Push Gateway. The following
-tweaks are defined:
-
-``sound``
- A string representing the sound to be played when this notification arrives.
- A value of ``default`` means to play a default sound. A device may choose to
- alert the user by some other means if appropriate, eg. vibration.
-``highlight``
- A boolean representing whether or not this message should be highlighted in
- the UI. This will normally take the form of presenting the message in a
- different colour and/or style. The UI might also be adjusted to draw
- particular attention to the room in which the event occurred. If a
- ``highlight`` tweak is given with no value, its value is defined to be
- ``true``. If no highlight tweak is given at all then the value of
- ``highlight`` is defined to be false.
-
-Tweaks are passed transparently through the homeserver so client applications
-and Push Gateways may agree on additional tweaks. For example, a tweak may be
-added to specify how to flash the notification light on a mobile device.
-
-Conditions
-++++++++++
-
-``override`` and ``underride`` rules MAY have a list of 'conditions'.
-All conditions must hold true for an event in order for the rule to match.
-A rule with no conditions always matches. The following conditions are defined:
-
-``event_match``
- This is a glob pattern match on a field of the event. Parameters:
-
- * ``key``: The dot-separated field of the event to match, e.g. ``content.body``
- * ``pattern``: The glob-style pattern to match against. Patterns with no
- special glob characters should be treated as having asterisks
- prepended and appended when testing the condition.
-
-``contains_display_name``
- This matches unencrypted messages where ``content.body`` contains the owner's
- display name in that room. This is a separate rule because display names may
- change and as such it would be hard to maintain a rule that matched the user's
- display name. This condition has no parameters.
-
-``room_member_count``
- This matches the current number of members in the room. Parameters:
-
- * ``is``: A decimal integer optionally prefixed by one of, ``==``, ``<``,
- ``>``, ``>=`` or ``<=``. A prefix of ``<`` matches rooms where the member
- count is strictly less than the given number and so forth. If no prefix is
- present, this parameter defaults to ``==``.
-
-``sender_notification_permission``
- This takes into account the current power levels in the room, ensuring the
- sender of the event has high enough power to trigger the notification.
-
- Parameters:
-
- * ``key``: A string that determines the power level the sender must have to trigger
- notifications of a given type, such as ``room``. Refer to the `m.room.power_levels`_
- event schema for information about what the defaults are and how to interpret the event.
- The ``key`` is used to look up the power level required to send a notification type
- from the ``notifications`` object in the power level event content.
-
-Unrecognised conditions MUST NOT match any events, effectively making the push
-rule disabled.
-
-``room``, ``sender`` and ``content`` rules do not have conditions in the same
-way, but instead have predefined conditions. In the cases of ``room`` and
-``sender`` rules, the ``rule_id`` of the rule determines its behaviour.
-
-
-Predefined Rules
-++++++++++++++++
-Homeservers can specify "server-default rules" which operate at a lower priority
-than "user-defined rules". The ``rule_id`` for all server-default rules MUST
-start with a dot (".") to identify them as "server-default". The following
-server-default rules are specified:
-
-
-Default Override Rules
-^^^^^^^^^^^^^^^^^^^^^^
-
-``.m.rule.master``
-``````````````````
-Matches all events. This can be enabled to turn off all push notifications
-other than those generated by override rules set by the user. By default this
-rule is disabled.
-
-Definition
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.master",
- "default": true,
- "enabled": false,
- "conditions": [],
- "actions": [
- "dont_notify"
- ]
- }
-
-``.m.rule.suppress_notices``
-````````````````````````````
-Matches messages with a ``msgtype`` of ``notice``.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.suppress_notices",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "event_match",
- "key": "content.msgtype",
- "pattern": "m.notice",
- }
- ],
- "actions": [
- "dont_notify",
- ]
- }
-
-``.m.rule.invite_for_me``
-`````````````````````````
-Matches any invites to a new room for this user.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.invite_for_me",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "key": "type",
- "kind": "event_match",
- "pattern": "m.room.member"
- },
- {
- "key": "content.membership",
- "kind": "event_match",
- "pattern": "invite"
- },
- {
- "key": "state_key",
- "kind": "event_match",
- "pattern": "[the user's Matrix ID]"
- }
- ],
- "actions": [
- "notify",
- {
- "set_tweak": "sound",
- "value": "default"
- }
- ]
- }
-
-``.m.rule.member_event``
-````````````````````````
-
-Matches any ``m.room.member_event``.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.member_event",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "key": "type",
- "kind": "event_match",
- "pattern": "m.room.member"
- }
- ],
- "actions": [
- "dont_notify"
- ]
- }
-
-
-``.m.rule.contains_display_name``
-`````````````````````````````````
-Matches any message whose content is unencrypted and contains the user's
-current display name in the room in which it was sent.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.contains_display_name",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "contains_display_name"
- }
- ],
- "actions": [
- "notify",
- {
- "set_tweak": "sound",
- "value": "default"
- },
- {
- "set_tweak": "highlight"
- }
- ]
- }
-
-
-``.m.rule.tombstone``
-`````````````````````
-Matches any state event whose type is ``m.room.tombstone``. This is intended
-to notify users of a room when it is upgraded, similar to what an
-``@room`` notification would accomplish.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.tombstone",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "event_match",
- "key": "type",
- "pattern": "m.room.tombstone"
- },
- {
- "kind": "event_match",
- "key": "state_key",
- "pattern": ""
- }
- ],
- "actions": [
- "notify",
- {
- "set_tweak": "highlight"
- }
- ]
- }
-
-
-``.m.rule.roomnotif``
-`````````````````````
-Matches any message whose content is unencrypted and contains the
-text ``@room``, signifying the whole room should be notified of
-the event.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.roomnotif",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "event_match",
- "key": "content.body",
- "pattern": "@room"
- },
- {
- "kind": "sender_notification_permission",
- "key": "room"
- }
- ],
- "actions": [
- "notify",
- {
- "set_tweak": "highlight"
- }
- ]
- }
-
-
-Default Content Rules
-^^^^^^^^^^^^^^^^^^^^^
-
-``.m.rule.contains_user_name``
-``````````````````````````````
-Matches any message whose content is unencrypted and contains the local part
-of the user's Matrix ID, separated by word boundaries.
-
-Definition (as a ``content`` rule):
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.contains_user_name",
- "default": true,
- "enabled": true,
- "pattern": "[the local part of the user's Matrix ID]",
- "actions": [
- "notify",
- {
- "set_tweak": "sound",
- "value": "default"
- },
- {
- "set_tweak": "highlight"
- }
- ]
- }
-
-Default Underride Rules
-^^^^^^^^^^^^^^^^^^^^^^^
-
-``.m.rule.call``
-````````````````
-Matches any incoming VOIP call.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.call",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "key": "type",
- "kind": "event_match",
- "pattern": "m.call.invite"
- }
- ],
- "actions": [
- "notify",
- {
- "set_tweak": "sound",
- "value": "ring"
- }
- ]
- }
-
-``.m.rule.encrypted_room_one_to_one``
-`````````````````````````````````````
-Matches any encrypted event sent in a room with exactly two members.
-Unlike other push rules, this rule cannot be matched against the content
-of the event by nature of it being encrypted. This causes the rule to
-be an "all or nothing" match where it either matches *all* events that
-are encrypted (in 1:1 rooms) or none.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.encrypted_room_one_to_one",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "room_member_count",
- "is": "2"
- },
- {
- "kind": "event_match",
- "key": "type",
- "pattern": "m.room.encrypted"
- }
- ],
- "actions": [
- "notify",
- {
- "set_tweak": "sound",
- "value": "default"
- }
- ]
- }
-
-``.m.rule.room_one_to_one``
-```````````````````````````
-Matches any message sent in a room with exactly two members.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.room_one_to_one",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "room_member_count",
- "is": "2"
- },
- {
- "kind": "event_match",
- "key": "type",
- "pattern": "m.room.message"
- }
- ],
- "actions": [
- "notify",
- {
- "set_tweak": "sound",
- "value": "default"
- }
- ]
- }
-
-``.m.rule.message``
-```````````````````
-Matches all chat messages.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.message",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "event_match",
- "key": "type",
- "pattern": "m.room.message"
- }
- ],
- "actions": [
- "notify"
- ]
- }
-
-``.m.rule.encrypted``
-`````````````````````
-Matches all encrypted events. Unlike other push rules, this rule cannot
-be matched against the content of the event by nature of it being encrypted.
-This causes the rule to be an "all or nothing" match where it either
-matches *all* events that are encrypted (in group rooms) or none.
-
-Definition:
-
-.. code:: json
-
- {
- "rule_id": ".m.rule.encrypted",
- "default": true,
- "enabled": true,
- "conditions": [
- {
- "kind": "event_match",
- "key": "type",
- "pattern": "m.room.encrypted"
- }
- ],
- "actions": [
- "notify"
- ]
- }
-
-Push Rules: API
-~~~~~~~~~~~~~~~
-
-Clients can retrieve, add, modify and remove push rules globally or per-device
-using the APIs below.
-
-{{pushrules_cs_http_api}}
-
-
-Push Rules: Events
-~~~~~~~~~~~~~~~~~~
-
-When a user changes their push rules a ``m.push_rules`` event is sent to all
-clients in the ``account_data`` section of their next ``/sync`` request. The
-content of the event is the current push rules for the user.
-
-{{m_push_rules_event}}
-
-Examples
-++++++++
-
-To create a rule that suppresses notifications for the room with ID
-``!dj234r78wl45Gh4D:matrix.org``::
-
- curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" -d \
- '{
- "actions" : ["dont_notify"]
- }'
-
-To suppress notifications for the user ``@spambot:matrix.org``::
-
- curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" -d \
- '{
- "actions" : ["dont_notify"]
- }'
-
-To always notify for messages that contain the work 'cake' and set a specific
-sound (with a rule_id of ``SSByZWFsbHkgbGlrZSBjYWtl``)::
-
- curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" -d \
- '{
- "pattern": "cake",
- "actions" : ["notify", {"set_sound":"cakealarm.wav"}]
- }'
-
-To add a rule suppressing notifications for messages starting with 'cake' but
-ending with 'lie', superseding the previous rule::
-
- curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" -d \
- '{
- "pattern": "cake*lie",
- "actions" : ["notify"]
- }'
-
-To add a custom sound for notifications messages containing the word 'beer' in
-any rooms with 10 members or fewer (with greater importance than the room,
-sender and content rules)::
-
- curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456" -d \
- '{
- "conditions": [
- {"kind": "event_match", "key": "content.body", "pattern": "beer" },
- {"kind": "room_member_count", "is": "<=10"}
- ],
- "actions" : [
- "notify",
- {"set_sound":"beeroclock.wav"}
- ]
- }'
-
-Server behaviour
-----------------
-
-Push Gateway behaviour
-----------------------
-
-Recommendations for APNS
-~~~~~~~~~~~~~~~~~~~~~~~~
-The exact format for sending APNS notifications is flexible and up to the
-client app and its' push gateway to agree on. As APNS requires that the sender
-has a private key owned by the app developer, each app must have its own push
-gateway. It is recommended that:
-
-* The APNS token be base64 encoded and used as the pushkey.
-* A different app_id be used for apps on the production and sandbox
- APS environments.
-* APNS push gateways do not attempt to wait for errors from the APNS
- gateway before returning and instead to store failures and return
- 'rejected' responses next time that pushkey is used.
-
-Security considerations
------------------------
-
-Clients specify the Push Gateway URL to use to send event notifications to. This
-URL should be over HTTPS and *never* over HTTP.
-
-As push notifications will pass through a Push Provider, message content
-shouldn't be sent in the push itself where possible. Instead, Push Gateways
-should send a "sync" command to instruct the client to get new events from the
-homeserver directly.
-
-
-.. _`Push Gateway Specification`: ../push_gateway/%PUSH_GATEWAY_RELEASE_LABEL%.html
diff --git a/specification/modules/read_markers.rst b/specification/modules/read_markers.rst
deleted file mode 100644
index be06d037362..00000000000
--- a/specification/modules/read_markers.rst
+++ /dev/null
@@ -1,67 +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.
-
-Fully read markers
-==================
-
-.. _module:read-markers:
-
-The history for a given room may be split into three sections: messages the
-user has read (or indicated they aren't interested in them), messages the user
-might have read some but not others, and messages the user hasn't seen yet.
-The "fully read marker" (also known as a "read marker") marks the last event
-of the first section, whereas the user's read receipt marks the last event of
-the second section.
-
-Events
-------
-The user's fully read marker is kept as an event in the room's `account data`_.
-The event may be read to determine the user's current fully read marker location
-in the room, and just like other account data events the event will be pushed down
-the event stream when updated.
-
-The fully read marker is kept under an ``m.fully_read`` event. If the event does
-not exist on the user's account data, the fully read marker should be considered
-to be the user's read receipt location.
-
-{{m_fully_read_event}}
-
-Client behaviour
-----------------
-The client cannot update fully read markers by directly modifying the ``m.fully_read``
-account data event. Instead, the client must make use of the read markers API
-to change the values.
-
-The read markers API can additionally update the user's read receipt (``m.read``)
-location in the same operation as setting the fully read marker location. This is
-because read receipts and read markers are commonly updated at the same time,
-and therefore the client might wish to save an extra HTTP call. Providing an
-``m.read`` location performs the same task as a request to ``/receipt/m.read/$event:example.org``.
-
-{{read_markers_cs_http_api}}
-
-Server behaviour
-----------------
-The server MUST prevent clients from setting ``m.fully_read`` directly in
-room account data. The server must additionally ensure that it treats the
-presence of ``m.read`` in the ``/read_markers`` request the same as how it
-would for a request to ``/receipt/m.read/$event:example.org``.
-
-Upon updating the ``m.fully_read`` event due to a request to ``/read_markers``,
-the server MUST send the updated account data event through to the client via
-the event stream (eg: ``/sync``), provided any applicable filters are also
-satisfied.
-
-
-.. _`account data`: #client-config
diff --git a/specification/modules/receipts.rst b/specification/modules/receipts.rst
deleted file mode 100644
index 4630091f01f..00000000000
--- a/specification/modules/receipts.rst
+++ /dev/null
@@ -1,98 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Receipts
-========
-
-.. _module:receipts:
-
-This module adds in support for receipts. These receipts are a form of
-acknowledgement of an event. This module defines a single acknowledgement:
-``m.read`` which indicates that the user has read up to a given event.
-
-Sending a receipt for each event can result in sending large amounts of traffic
-to a homeserver. To prevent this from becoming a problem, receipts are implemented
-using "up to" markers. This marker indicates that the acknowledgement applies
-to all events "up to and including" the event specified. For example, marking
-an event as "read" would indicate that the user had read all events *up to* the
-referenced event. See the `Receiving notifications <#receiving-notifications>`_
-section for more information on how read receipts affect notification counts.
-
-Events
-------
-Each ``user_id``, ``receipt_type`` pair must be associated with only a
-single ``event_id``.
-
-{{m_receipt_event}}
-
-Client behaviour
-----------------
-
-In ``/sync``, receipts are listed under the ``ephemeral`` array of events
-for a given room. New receipts that come down the event streams are deltas
-which update existing mappings. Clients should replace older receipt acknowledgements
-based on ``user_id`` and ``receipt_type`` pairs. For example::
-
- Client receives m.receipt:
- user = @alice:example.com
- receipt_type = m.read
- event_id = $aaa:example.com
-
- Client receives another m.receipt:
- user = @alice:example.com
- receipt_type = m.read
- event_id = $bbb:example.com
-
- The client should replace the older acknowledgement for $aaa:example.com with
- this one for $bbb:example.com
-
-Clients should send read receipts when there is some certainty that the event in
-question has been **displayed** to the user. Simply receiving an event does not
-provide enough certainty that the user has seen the event. The user SHOULD need
-to *take some action* such as viewing the room that the event was sent to or
-dismissing a notification in order for the event to count as "read". Clients
-SHOULD NOT send read receipts for events sent by their own user.
-
-A client can update the markers for its user by interacting with the following
-HTTP APIs.
-
-{{receipts_cs_http_api}}
-
-Server behaviour
-----------------
-
-For efficiency, receipts SHOULD be batched into one event per room before
-delivering them to clients.
-
-Receipts are sent across federation as EDUs with type ``m.receipt``. The
-format of the EDUs are::
-
- {
- : {
- : {
- : { }
- },
- ...
- },
- ...
- }
-
-These are always sent as deltas to previously sent receipts. Currently only a
-single ```` should be used: ``m.read``.
-
-Security considerations
------------------------
-
-As receipts are sent outside the context of the event graph, there are no
-integrity checks performed on the contents of ``m.receipt`` events.
diff --git a/specification/modules/report_content.rst b/specification/modules/report_content.rst
deleted file mode 100644
index 5eca69cd7db..00000000000
--- a/specification/modules/report_content.rst
+++ /dev/null
@@ -1,35 +0,0 @@
-.. Copyright 2018 Travis Ralston
-..
-.. 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.
-
-Reporting Content
-=================
-
-.. _module:report_content:
-
-Users may encounter content which they find inappropriate and should be able
-to report it to the server administrators or room moderators for review. This
-module defines a way for users to report content.
-
-Content is reported based upon a negative score, where -100 is "most offensive"
-and 0 is "inoffensive".
-
-Client behaviour
-----------------
-{{report_content_cs_http_api}}
-
-Server behaviour
-----------------
-Servers are free to handle the reported content however they desire. This may
-be a dedicated room to alert server administrators to the reported content or
-some other mechanism for notifying the appropriate people.
diff --git a/specification/modules/room_previews.rst b/specification/modules/room_previews.rst
deleted file mode 100644
index f40c89728dd..00000000000
--- a/specification/modules/room_previews.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Previews
-=============
-
-.. _module:room-previews:
-
-It is sometimes desirable to offer a preview of a room, where a user can "lurk"
-and read messages posted to the room, without joining the room. This can be
-particularly effective when combined with `Guest Access`_.
-
-Previews are implemented via the ``world_readable`` `Room History Visibility`_.
-setting, along with a special version of the
-`GET /events <#get-matrix-client-%CLIENT_MAJOR_VERSION%-events>`_ endpoint.
-
-Client behaviour
-----------------
-A client wishing to view a room without joining it should call
-`GET /rooms/:room_id/initialSync <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync>`_,
-followed by `GET /events`__. Clients will need to do this
-in parallel for each room they wish to view.
-
-__ `peeking_events_api`_
-
-Clients can of course also call other endpoints such as
-`GET /rooms/:room_id/messages <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages>`_
-and `GET /search <#get-matrix-client-%CLIENT_MAJOR_VERSION%-search>`_ to access
-events outside the ``/events`` stream.
-
-.. _peeking_events_api:
-
-{{peeking_events_cs_http_api}}
-
-Server behaviour
-----------------
-For clients which have not joined a room, servers are required to only return
-events where the room state at the event had the ``m.room.history_visibility``
-state event present with ``history_visibility`` value ``world_readable``.
-
-Security considerations
------------------------
-Clients may wish to display to their users that rooms which are
-``world_readable`` *may* be showing messages to non-joined users. There is no
-way using this module to find out whether any non-joined guest users *do* see
-events in the room, or to list or count any lurking users.
-
diff --git a/specification/modules/room_upgrades.rst b/specification/modules/room_upgrades.rst
deleted file mode 100644
index f1861f72024..00000000000
--- a/specification/modules/room_upgrades.rst
+++ /dev/null
@@ -1,78 +0,0 @@
-.. Copyright 2019 New Vector Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Upgrades
-=============
-
-.. _module:room-upgrades:
-
-From time to time, a room may need to be upgraded to a different room version for a
-variety for reasons. This module defines a way for rooms to upgrade to a different
-room version when needed.
-
-Events
-------
-
-{{m_room_tombstone_event}}
-
-Client behaviour
-----------------
-
-Clients which understand ``m.room.tombstone`` events and the ``predecessor`` field on
-``m.room.create`` events should communicate to the user that the room was upgraded.
-One way of accomplishing this would be hiding the old room from the user's room list
-and showing banners linking between the old and new room - ensuring that permalinks
-work when referencing the old room. Another approach may be to virtually merge the
-rooms such that the old room's timeline seamlessly continues into the new timeline
-without the user having to jump between the rooms.
-
-{{room_upgrades_cs_http_api}}
-
-Server behaviour
-----------------
-
-When the client requests to upgrade a known room to a known version, the server:
-
-1. Checks that the user has permission to send ``m.room.tombstone`` events in the room.
-2. Creates a replacement room with a ``m.room.create`` event containing a ``predecessor``
- field and the applicable ``room_version``.
-3. Replicates transferable state events to the new room. The exact details for what is
- transferred is left as an implementation detail, however the recommended state events
- to transfer are:
-
- * ``m.room.server_acl``
- * ``m.room.encryption``
- * ``m.room.name``
- * ``m.room.avatar``
- * ``m.room.topic``
- * ``m.room.guest_access``
- * ``m.room.history_visibility``
- * ``m.room.join_rules``
- * ``m.room.power_levels``
-
- Membership events should not be transferred to the new room due to technical limitations
- of servers not being able to impersonate people from other homeservers. Additionally,
- servers should not transfer state events which are sensitive to who sent them, such as
- events outside of the Matrix namespace where clients may rely on the sender to match
- certain criteria.
-
-4. Moves any local aliases to the new room.
-5. Sends a ``m.room.tombstone`` event to the old room to indicate that it is not intended
- to be used any further.
-6. If possible, the power levels in the old room should also be modified to prevent sending
- of events and inviting new users. For example, setting ``events_default`` and ``invite``
- to the greater of ``50`` and ``users_default + 1``.
-
-When a user joins the new room, the server should automatically transfer/replicate some of
-the user's personalized settings such as notifications, tags, etc.
diff --git a/specification/modules/search.rst b/specification/modules/search.rst
deleted file mode 100644
index 08926552340..00000000000
--- a/specification/modules/search.rst
+++ /dev/null
@@ -1,109 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Server Side Search
-==================
-
-.. _module:search:
-
-The search API allows clients to perform full text search across events in all
-rooms that the user has been in, including those that they have left. Only
-events that the user is allowed to see will be searched, e.g. it won't include
-events in rooms that happened after you left.
-
-Client behaviour
-----------------
-There is a single HTTP API for performing server-side search, documented below.
-
-{{search_cs_http_api}}
-
-Search Categories
------------------
-
-The search API allows clients to search in different categories of items.
-Currently the only specified category is ``room_events``.
-
-``room_events``
-~~~~~~~~~~~~~~~
-
-This category covers all events that the user is allowed to see, including
-events in rooms that they have left. The search is performed on certain keys of
-certain event types.
-
-The supported keys to search over are:
-
-- ``content.body`` in ``m.room.message``
-- ``content.name`` in ``m.room.name``
-- ``content.topic`` in ``m.room.topic``
-
-The search will *not* include rooms that are end to end encrypted.
-
-The results include a ``rank`` key that can be used to sort the results by
-relevancy. The higher the ``rank`` the more relevant the result is.
-
-The value of ``count`` gives an approximation of the total number of
-results. Homeservers may give an estimate rather than an exact value for this
-field.
-
-Ordering
---------
-
-The client can specify the ordering that the server returns results in. The two
-allowed orderings are:
-
-- ``rank``, which returns the most relevant results first.
-- ``recent``, which returns the most recent results first.
-
-The default ordering is ``rank``.
-
-Groups
-------
-
-The client can request that the results are returned along with grouping
-information, e.g. grouped by ``room_id``. In this case the response will
-contain a group entry for each distinct value of ``room_id``. Each group entry
-contains at least a list of the ``event_ids`` that are in that group, as well
-as potentially other metadata about the group.
-
-The current required supported groupings are:
-
-- ``room_id``
-- ``sender``
-
-
-Pagination
-----------
-
-The server may return a ``next_batch`` key at various places in the response.
-These are used to paginate the results. To fetch more results, the client
-should send the *same* request to the server with a ``next_batch`` query
-parameter set to that of the token.
-
-The scope of the pagination is defined depending on where the ``next_batch``
-token was returned. For example, using a token inside a group will return more
-results from within that group.
-
-The currently supported locations for the ``next_batch`` token are:
-
-- ``search_categories..next_batch``
-- ``search_categories..groups...next_batch``
-
-A server need not support pagination, even if there are more matching results.
-In that case, they must not return a ``next_batch`` token in the response.
-
-
-Security considerations
------------------------
-The server must only return results that the user has permission to see.
-
diff --git a/specification/modules/secrets.rst b/specification/modules/secrets.rst
deleted file mode 100644
index 2e8e38866de..00000000000
--- a/specification/modules/secrets.rst
+++ /dev/null
@@ -1,361 +0,0 @@
-.. Copyright 2020 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-Secrets
-=======
-
-Clients may have secret information that they wish to be made available to
-other authorised clients, but that the server should not be able to see, so the
-information must be encrypted as it passes through the server. This can be done
-either asynchronously, by storing encrypted data on the server for later
-retrieval, or synchronously, by sending messages to each other.
-
-Each secret has an identifier that is used by clients to refer to the secret
-when storing, fetching, requesting, or sharing the secret. Secrets are plain
-strings; structured data can be stored by encoding it as a string.
-
-Storage
--------
-
-When secrets are stored on the server, they are stored in the user's
-`account-data <#module-account-data>`_, using an event type equal to the
-secret's identifier. The keys that secrets are encrypted with are described by
-data that is also stored in the user's account-data. Users can have multiple
-keys, allowing them to control what sets of secrets clients can access,
-depending on what keys are given to them.
-
-Key storage
-~~~~~~~~~~~
-
-Each key has an ID, and the description of the key is stored in the user's
-account_data using the event type ``m.secret_storage.key.[key ID]``. The
-contents of the account data for the key will include an ``algorithm``
-property, which indicates the encryption algorithm used, as well as a ``name``
-property, which is a human-readable name. Key descriptions may also have a
-``passphrase`` property for generating the key from a user-entered
-passphrase, as described in `deriving keys from passphrases`_.
-
-``KeyDescription``
-
-============ =========== =======================================================
-Parameter Type Description
-============ =========== =======================================================
-name string **Required.** The name of the key.
-algorithm string **Required.** The encryption algorithm to be used for
- this key. Currently, only
- ``m.secret_storage.v1.aes-hmac-sha2`` is supported.
-passphrase string See `deriving keys from passphrases`_ section for a
- description of this property.
-============ =========== =======================================================
-
-Other properties depend on the encryption algorithm, and are described below.
-
-A key can be marked as the "default" key by setting the user's account_data
-with event type ``m.secret_storage.default_key`` to an object that has the ID
-of the key as its ``key`` property. The default key will be used to encrypt
-all secrets that the user would expect to be available on all their clients.
-Unless the user specifies otherwise, clients will try to use the default key to
-decrypt secrets.
-
-Secret storage
-~~~~~~~~~~~~~~
-
-Encrypted data is stored in the user's account_data using the event type
-defined by the feature that uses the data. The account_data will have an
-``encrypted`` property that is a map from key ID to an object. The algorithm
-from the ``m.secret_storage.key.[key ID]`` data for the given key defines how
-the other properties are interpreted, though it's expected that most encryption
-schemes would have ``ciphertext`` and ``mac`` properties, where the
-``ciphertext`` property is the unpadded base64-encoded ciphertext, and the
-``mac`` is used to ensure the integrity of the data.
-
-``Secret``
-
-============ =========== =======================================================
-Parameter Type Description
-============ =========== =======================================================
-encrypted {string: **Required.** Map from key ID the encrypted data. The
- object} exact format for the encrypted data is dependent on the
- key algorithm. See the definition of
- ``AesHmacSha2EncryptedData`` in the
- `m.secret_storage.v1.aes-hmac-sha2`_ section.
-============ =========== =======================================================
-
-Example:
-
-Some secret is encrypted using keys with ID ``key_id_1`` and ``key_id_2``:
-
-``org.example.some.secret``:
-
-.. code:: json
-
- {
- "encrypted": {
- "key_id_1": {
- "ciphertext": "base64+encoded+encrypted+data",
- "mac": "base64+encoded+mac",
- // ... other properties according to algorithm property in
- // m.secret_storage.key.key_id_1
- },
- "key_id_2": {
- // ...
- }
- }
- }
-
-and the key descriptions for the keys would be:
-
-``m.secret_storage.key.key_id_1``:
-
-.. code:: json
-
- {
- "name": "Some key",
- "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
- // ... other properties according to algorithm
- }
-
-``m.secret_storage.key.key_id_2``:
-
-.. code:: json
-
- {
- "name": "Some other key",
- "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
- // ... other properties according to algorithm
- }
-
-``m.secret_storage.v1.aes-hmac-sha2``
-+++++++++++++++++++++++++++++++++++++
-
-Secrets encrypted using the ``m.secret_storage.v1.aes-hmac-sha2`` algorithm are
-encrypted using AES-CTR-256, and authenticated using HMAC-SHA-256. The secret is
-encrypted as follows:
-
-1. Given the secret storage key, generate 64 bytes by performing an HKDF with
- SHA-256 as the hash, a salt of 32 bytes of 0, and with the secret name as
- the info. The first 32 bytes are used as the AES key, and the next 32 bytes
- are used as the MAC key
-2. Generate 16 random bytes, set bit 63 to 0 (in order to work around
- differences in AES-CTR implementations), and use this as the AES
- initialization vector. This becomes the ``iv`` property, encoded using base64.
-3. Encrypt the data using AES-CTR-256 using the AES key generated above. This
- encrypted data, encoded using base64, becomes the ``ciphertext`` property.
-4. Pass the raw encrypted data (prior to base64 encoding) through HMAC-SHA-256
- using the MAC key generated above. The resulting MAC is base64-encoded and
- becomes the ``mac`` property.
-
-``AesHmacSha2EncryptedData``
-
-============ =========== =======================================================
-Parameter Type Description
-============ =========== =======================================================
-iv string **Required.** The 16-byte initialization vector,
- encoded as base64.
-ciphertext string **Required.** The AES-CTR-encrypted data, encoded as
- base64.
-mac string **Required.** The MAC, encoded as base64.
-============ =========== =======================================================
-
-For the purposes of allowing clients to check whether a user has correctly
-entered the key, clients should:
-
-1. encrypt and MAC a message consisting of 32 bytes of 0 as described above,
- using the empty string as the info parameter to the HKDF in step 1.
-2. store the ``iv`` and ``mac`` in the ``m.secret_storage.key.[key ID]``
- account-data.
-
-``AesHmacSha2KeyDescription``
-
-============ =========== =======================================================
-Parameter Type Description
-============ =========== =======================================================
-name string **Required.** The name of the key.
-algorithm string **Required.** The encryption algorithm to be used for
- this key. Currently, only
- ``m.secret_storage.v1.aes-hmac-sha2`` is supported.
-passphrase object See `deriving keys from passphrases`_ section for a
- description of this property.
-iv string The 16-byte initialization vector, encoded as base64.
-mac string The MAC of the result of encrypting 32 bytes of 0,
- encoded as base64.
-============ =========== =======================================================
-
-For example, the ``m.secret_storage.key.key_id`` for a key using this algorithm
-could look like:
-
-.. code:: json
-
- {
- "name": "m.default",
- "algorithm": "m.secret_storage.v1.aes-hmac-sha2",
- "iv": "random+data",
- "mac": "mac+of+encrypted+zeros"
- }
-
-and data encrypted using this algorithm could look like this:
-
-.. code:: json
-
- {
- "encrypted": {
- "key_id": {
- "iv": "16+bytes+base64",
- "ciphertext": "base64+encoded+encrypted+data",
- "mac": "base64+encoded+mac"
- }
- }
- }
-
-Key representation
-++++++++++++++++++
-
-When a user is given a raw key for ``m.secret_storage.v1.aes-hmac-sha2``,
-it will be presented as a string constructed as follows:
-
-1. The key is prepended by the two bytes ``0x8b`` and ``0x01``
-2. All the bytes in the string above, including the two header bytes, are
- XORed together to form a parity byte. This parity byte is appended to the byte
- string.
-3. The byte string is encoded using base58, using the same `mapping as is used
- for Bitcoin addresses
- `_,
- that is, using the alphabet
- ``123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz``.
-4. The string is formatted into groups of four characters separated by spaces.
-
-When decoding a raw key, the process should be reversed, with the exception
-that whitespace is insignificant in the user's input.
-
-Deriving keys from passphrases
-++++++++++++++++++++++++++++++
-
-A user may wish to use a chosen passphrase rather than a randomly generated
-key. In this case, information on how to generate the key from a passphrase
-will be stored in the ``passphrase`` property of the ``m.secret_storage.key.[key
-ID]`` account-data. The ``passphrase`` property has an ``algorithm`` property
-that indicates how to generate the key from the passphrase. Other properties of
-the ``passphrase`` property are defined by the ``algorithm`` specified.
-
-``m.pbkdf2``
-<<<<<<<<<<<<
-
-For the ``m.pbkdf2`` algorithm, the ``passphrase`` property has the following
-properties:
-
-============ =========== ========================================================
-Parameter Type Description
-============ =========== ========================================================
-algorithm string **Required.** Must be ``m.pbkdf2``
-salt string **Required.** The salt used in PBKDF2.
-iterations integer **Required.** The number of iterations to use in PBKDF2.
-bits integer Optional. The number of bits to generate for the key.
- Defaults to 256.
-============ =========== ========================================================
-
-The key is generated using PBKDF2 with SHA-512 as the hash, using the salt
-given in the ``salt`` parameter, and the number of iterations given in the
-``iterations`` parameter.
-
-Example:
-
-.. code:: json
-
- {
- "passphrase": {
- "algorithm": "m.pbkdf2",
- "salt": "MmMsAlty",
- "iterations": 100000,
- "bits": 256
- },
- ...
- }
-
-Sharing
--------
-
-To request a secret from other devices, a client sends an ``m.secret.requests``
-device event with ``action`` set to ``request`` and ``name`` set to the
-identifier of the secret. A device that wishes to share the secret will reply
-with an ``m.secret.send`` event, encrypted using olm. When the original client
-obtains the secret, it sends an ``m.secret.request`` event with ``action`` set
-to ``request_cancellation`` to all devices other than the one that it received
-the secret from. Clients should ignore ``m.secret.send`` events received from
-devices that it did not send an ``m.secret.request`` event to.
-
-Clients must ensure that they only share secrets with other devices that are
-allowed to see them. For example, clients should only share secrets with the
-user’s own devices that are verified and may prompt the user to confirm sharing
-the secret.
-
-Event definitions
-~~~~~~~~~~~~~~~~~
-
-``m.secret.request``
-++++++++++++++++++++
-
-Sent by a client to request a secret from another device or to cancel a
-previous request. It is sent as an unencrypted to-device event.
-
-.. table::
- :widths: auto
-
- ===================== =========== =====================================================
- Parameter Type Description
- ===================== =========== =====================================================
- name string Required if ``action`` is ``request``. The name of
- the secret that is being requested.
- action enum **Required.** One of ["request", "request_cancellation"].
- requesting_device_id string **Required.** The ID of the device requesting the secret.
- request_id string **Required.** A random string uniquely identifying (with
- respect to the requester and the target) the target
- for a secret. If the secret is requested from
- multiple devices at the same time, the same ID may
- be used for every target. The same ID is also used
- in order to cancel a previous request.
- ===================== =========== =====================================================
-
-Example:
-
-.. code:: json
-
- {
- "name": "org.example.some.secret",
- "action": "request",
- "requesting_device_id": "ABCDEFG",
- "request_id": "randomly_generated_id_9573"
- }
-
-``m.secret.send``
-+++++++++++++++++
-
-Sent by a client to share a secret with another device, in response to an
-``m.secret.request`` event. It must be encrypted as an ``m.room.encrypted`` event,
-then sent as a to-device event.
-
-============ =========== ========================================================
-Parameter Type Description
-============ =========== ========================================================
-request_id string **Required.** The ID of the request that this a response to.
-secret string **Required.** The contents of the secret.
-============ =========== ========================================================
-
-Example:
-
-.. code:: json
-
- {
- "request_id": "randomly_generated_id_9573",
- "secret": "ThisIsASecretDon'tTellAnyone"
- }
diff --git a/specification/modules/send_to_device.rst b/specification/modules/send_to_device.rst
deleted file mode 100644
index 7ab622bcf77..00000000000
--- a/specification/modules/send_to_device.rst
+++ /dev/null
@@ -1,150 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-.. _module:to_device:
-.. _`to-device`:
-
-Send-to-Device messaging
-========================
-
-This module provides a means by which clients can exchange signalling messages
-without them being stored permanently as part of a shared communication
-history. A message is delivered exactly once to each client device.
-
-The primary motivation for this API is exchanging data that is meaningless or
-undesirable to persist in the room DAG - for example, one-time authentication
-tokens or key data. It is not intended for conversational data, which should be
-sent using the normal |/rooms//send|_ API for consistency throughout
-Matrix.
-
-Client behaviour
-----------------
-To send a message to other devices, a client should call |/sendToDevice|_.
-Only one message can be sent to each device per transaction, and they must all
-have the same event type. The device ID in the request body can be set to ``*``
-to request that the message be sent to all known devices.
-
-If there are send-to-device messages waiting for a client, they will be
-returned by |/sync|_, as detailed in |Extensions|_. Clients should
-inspect the ``type`` of each returned event, and ignore any they do not
-understand.
-
-.. |Extensions| replace:: Extensions to /sync
-.. _Extensions: `send_to_device_sync`_
-
-Server behaviour
-----------------
-Servers should store pending messages for local users until they are
-successfully delivered to the destination device. When a client calls |/sync|_
-with an access token which corresponds to a device with pending messages, the
-server should list the pending messages, in order of arrival, in the response
-body.
-
-When the client calls ``/sync`` again with the ``next_batch`` token from the
-first response, the server should infer that any send-to-device messages in
-that response have been delivered successfully, and delete them from the store.
-
-If there is a large queue of send-to-device messages, the server should
-limit the number sent in each ``/sync`` response. 100 messages is recommended
-as a reasonable limit.
-
-If the client sends messages to users on remote domains, those messages should
-be sent on to the remote servers via
-`federation`_.
-
-.. _`federation`: ../server_server/%SERVER_RELEASE_LABEL%.html#send-to-device-messaging
-
-.. TODO-spec:
-
- * Is a server allowed to delete undelivered messages? After how long? What
- about if the device is deleted?
-
- * If the destination HS doesn't support the ``m.direct_to_device`` EDU, it
- will just get dumped. Should we indicate that to the client?
-
-
-Protocol definitions
---------------------
-
-{{to_device_cs_http_api}}
-
-.. TODO-spec:
-
- * What should a server do if the user id or device id is unknown? Presumably
- it shouldn't reject the request outright, because some of the destinations
- may be valid. Should we add something to the response?
-
-.. anchor for link from /sync api spec
-.. |send_to_device_sync| replace:: Send-to-Device messaging
-.. _send_to_device_sync:
-
-Extensions to /sync
-~~~~~~~~~~~~~~~~~~~
-
-This module adds the following properties to the |/sync|_ response:
-
-.. todo: generate this from a swagger definition?
-
-========= ========= =======================================================
-Parameter Type Description
-========= ========= =======================================================
-to_device ToDevice Optional. Information on the send-to-device messages
- for the client device.
-========= ========= =======================================================
-
-``ToDevice``
-
-========= ========= =============================================
-Parameter Type Description
-========= ========= =============================================
-events [Event] List of send-to-device messages.
-========= ========= =============================================
-
-``Event``
-
-================ ============ ==================================================
-Parameter Type Description
-================ ============ ==================================================
-content EventContent The content of this event. The fields in this
- object will vary depending on the type of event.
-sender string The Matrix user ID of the user who sent this
- event.
-type string The type of event.
-================ ============ ==================================================
-
-
-Example response:
-
-.. code:: json
-
- {
- "next_batch": "s72595_4483_1934",
- "rooms": {"leave": {}, "join": {}, "invite": {}},
- "to_device": {
- "events": [
- {
- "sender": "@alice:example.com",
- "type": "m.new_device",
- "content": {
- "device_id": "XYZABCDE",
- "rooms": ["!726s6s6q:example.com"]
- }
- }
- ]
- }
- }
-
-
-.. |/sendToDevice| replace:: ``/sendToDevice``
-.. _/sendToDevice: #put-matrix-client-%CLIENT_MAJOR_VERSION%-sendtodevice-eventtype-txnid
diff --git a/specification/modules/server_acls.rst b/specification/modules/server_acls.rst
deleted file mode 100644
index 2b2d8f351ba..00000000000
--- a/specification/modules/server_acls.rst
+++ /dev/null
@@ -1,70 +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.
-
-Server Access Control Lists (ACLs) for rooms
-============================================
-
-.. _module:server-acls:
-
-In some scenarios room operators may wish to prevent a malicious or untrusted
-server from participating in their room. Sending an `m.room.server_acl`_ state
-event into a room is an effective way to prevent the server from participating
-in the room at the federation level.
-
-Server ACLs can also be used to make rooms only federate with a limited set of
-servers, or retroactively make the room no longer federate with any other server,
-similar to setting the ``m.federate`` value on the `m.room.create`_ event.
-
-{{m_room_server_acl_event}}
-
-.. Note::
- Port numbers are not supported because it is unclear to parsers whether a
- port number should be matched or an IP address literal. Additionally, it
- is unlikely that one would trust a server running on a particular domain's
- port but not a different port, especially considering the server host can
- easily change ports.
-
-.. Note::
- CIDR notation is not supported for IP addresses because Matrix does not
- encourage the use of IPs for identifying servers. Instead, a blanket
- ``allow_ip_literals`` is provided to cover banning them.
-
-Client behaviour
-----------------
-Clients are not expected to perform any additional duties beyond sending the
-event. Clients should describe changes to the server ACLs to the user in the
-user interface, such as in the timeline.
-
-Clients may wish to kick affected users from the room prior to denying a server
-access to the room to help prevent those servers from participating and to
-provide feedback to the users that they have been excluded from the room.
-
-Server behaviour
-----------------
-Servers MUST prevent blacklisted servers from sending events or participating
-in the room when an `m.room.server_acl`_ event is present in the room state.
-Which APIs are specifically affected are described in the Server-Server API
-specification.
-
-Servers should still send events to denied servers if they are still residents
-of the room.
-
-
-Security considerations
------------------------
-Server ACLs are only effective if every server in the room honours them. Servers
-that do not honour the ACLs may still permit events sent by denied servers into
-the room, leaking them to other servers in the room. To effectively enforce an
-ACL in a room, the servers that do not honour the ACLs should be denied in the
-room as well.
\ No newline at end of file
diff --git a/specification/modules/server_notices.rst b/specification/modules/server_notices.rst
deleted file mode 100644
index 63b7bfc5e54..00000000000
--- a/specification/modules/server_notices.rst
+++ /dev/null
@@ -1,78 +0,0 @@
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Server Notices
-==============
-
-.. _module:server-notices:
-
-Homeserver hosts often want to send messages to users in an official capacity,
-or have resource limits which affect a user's ability to use the homeserver.
-For example, the homeserver may be limited to a certain number of active users
-per month and has exceeded that limit. To communicate this failure to users,
-the homeserver would use the Server Notices room.
-
-The aesthetics of the room (name, topic, avatar, etc) are left as an implementation
-detail. It is recommended that the homeserver decorate the room such that it looks
-like an official room to users.
-
-Events
-------
-Notices are sent to the client as normal ``m.room.message`` events with a
-``msgtype`` of ``m.server_notice`` in the server notices room. Events with
-a ``m.server_notice`` ``msgtype`` outside of the server notice room must
-be ignored by clients.
-
-The specified values for ``server_notice_type`` are:
-
-:``m.server_notice.usage_limit_reached``:
- The server has exceeded some limit which requires the server administrator
- to intervene. The ``limit_type`` describes the kind of limit reached.
- The specified values for ``limit_type`` are:
-
- :``monthly_active_user``:
- The server's number of active users in the last 30 days has exceeded the
- maximum. New connections are being refused by the server. What defines
- "active" is left as an implementation detail, however servers are encouraged
- to treat syncing users as "active".
-
-
-{{m_room_message_m_server_notice_event}}
-
-Client behaviour
-----------------
-Clients can identify the server notices room by the ``m.server_notice`` tag
-on the room. Active notices are represented by the `pinned events <#m-room-pinned-events>`_
-in the server notices room. Server notice events pinned in that room should
-be shown to the user through special UI and not through the normal pinned
-events interface in the client. For example, clients may show warning banners
-or bring up dialogs to get the user's attention. Events which are not server
-notice events and are pinned in the server notices room should be shown just
-like any other pinned event in a room.
-
-The client must not expect to be able to reject an invite to join the server
-notices room. Attempting to reject the invite must result in a
-``M_CANNOT_LEAVE_SERVER_NOTICE_ROOM`` error. Servers should not prevent the user
-leaving the room after joining the server notices room, however the same error
-code must be used if the server will prevent leaving the room.
-
-Server behaviour
-----------------
-Servers should manage exactly 1 server notices room per user. Servers must
-identify this room to clients with the ``m.server_notice`` tag. Servers should
-invite the target user rather than automatically join them to the server notice
-room.
-
-How servers send notices to clients, and which user they use to send the events,
-is left as an implementation detail for the server.
diff --git a/specification/modules/sso_login.rst b/specification/modules/sso_login.rst
deleted file mode 100644
index 986bed94f44..00000000000
--- a/specification/modules/sso_login.rst
+++ /dev/null
@@ -1,347 +0,0 @@
-.. Copyright 2019-2020 The Matrix.org Foundation C.I.C.
-..
-.. 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.
-
-SSO client login/authentication
-===============================
-
-.. _module:sso_login:
-
-Single Sign-On (SSO) is a generic term which refers to protocols which allow
-users to log into applications via a single web-based authentication portal.
-Examples include OpenID Connect, "Central Authentication Service" (CAS) and SAML.
-
-This module allows a Matrix homeserver to delegate user authentication to an
-external authentication server supporting one of these protocols. In this
-process, there are three systems involved:
-
- * A Matrix client, using the APIs defined this specification, which is seeking
- to authenticate a user to a Matrix homeserver.
-
- * A Matrix homeserver, implementing the APIs defined in this specification, but
- which is delegating user authentication to the authentication server.
-
- * An "authentication server", which is responsible for authenticating the
- user.
-
-This specification is concerned only with communication between the Matrix
-client and the homeserver, and is independent of the SSO protocol used to
-communicate with the authentication server. Different Matrix homeserver
-implementations might support different SSO protocols.
-
-Clients and homeservers implementing the SSO flow will need to consider both login_
-and `user-interactive authentication`_. The flow is
-similar in both cases, but there are slight differences.
-
-Typically, SSO systems require a single "callback" URI to be configured at the
-authentication server. Once the user is authenticated, their browser is
-redirected to that URI. It is up to the Matrix homeserver implementation to
-implement a suitable endpoint. For example, for CAS authentication the
-homeserver should provide a means for the administrator to configure where the
-CAS server is and the REST endpoints which consume the ticket.
-
-Client login via SSO
----------------------
-
-An overview of the process is as follows:
-
-0. The Matrix client calls |GET /login|_ to find the supported login
- types, and the homeserver includes a flow with ``"type": "m.login.sso"`` in the
- response.
-
-1. To initiate the ``m.login.sso`` login type, the Matrix client instructs the
- user's browser to navigate to the |/login/sso/redirect|_ endpoint on the
- user's homeserver.
-
-2. The homeserver responds with an HTTP redirect to the SSO user interface,
- which the browser follows.
-
-3. The authentication server and the homeserver interact to verify the user's
- identity and other authentication information, potentially using a number of
- redirects.
-
-4. The browser is directed to the ``redirectUrl`` provided by the client with
- a ``loginToken`` query parameter for the client to log in with.
-
-5. The client exchanges the login token for an access token by calling the
- |/login|_ endpoint with a ``type`` of ``m.login.token``.
-
-For native applications, typically steps 1 to 4 are carried out by opening an
-embedded web view.
-
-These steps are illustrated as follows::
-
- Matrix Client Matrix Homeserver Auth Server
- | | |
- |-------------(0) GET /login----------->| |
- |<-------------login types--------------| |
- | | |
- | Webview | |
- | | | |
- |----->| | |
- | |--(1) GET /login/sso/redirect-->| |
- | |<---------(2) 302---------------| |
- | | | |
- | |<========(3) Authentication process================>|
- | | | |
- | |<--(4) redirect to redirectUrl--| |
- |<-----| | |
- | | |
- |---(5) POST /login with login token--->| |
- |<-------------access token-------------| |
-
-
-.. Note::
- In the older `r0.4.0 version `_
- of this specification it was possible to authenticate via CAS when the homeserver
- provides a ``m.login.cas`` login flow. This specification deprecates the use
- of ``m.login.cas`` to instead prefer ``m.login.sso``, which is the same process
- with the only change being which redirect endpoint to use: for ``m.login.cas``, use
- ``/cas/redirect`` and for ``m.login.sso`` use ``/sso/redirect`` (described below).
- The endpoints are otherwise the same.
-
-Client behaviour
-~~~~~~~~~~~~~~~~
-
-The client starts the process by instructing the browser to navigate to
-|/login/sso/redirect|_ with an appropriate ``redirectUrl``. Once authentication
-is successful, the browser will be redirected to that ``redirectUrl``.
-
-{{sso_login_redirect_cs_http_api}}
-
-Security considerations
-+++++++++++++++++++++++
-
-1. CSRF attacks via manipulation of parameters on the ``redirectUrl``
-
- Clients should validate any requests to the ``redirectUrl``. In particular, it
- may be possible for attackers to falsify any query parameters, leading to
- cross-site request forgery (CSRF) attacks.
-
- For example, consider a web-based client at ``https://client.example.com``,
- which wants to initiate SSO login on the homeserver at ``server.example.org``.
- It does this by storing the homeserver name in a query parameter for the
- ``redirectUrl``: it redirects to
- ``https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=server.example.org``.
-
- An attacker could trick a victim into following a link to
- ``https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=evil.com``,
- which would result in the client sending a login token for the victim's
- account to the attacker-controlled site ``evil.com``.
-
- To guard against this, clients MUST NOT store state (such as the address of
- the homeserver being logged into) anywhere it can be modified by external
- processes.
-
- Instead, the state could be stored in `localStorage
- `_ or
- in a cookie.
-
-2. For added security, clients SHOULD include a unique identifier in the
- ``redirectUrl`` and reject any callbacks that do not contain a recognised
- identifier, to guard against unsolicited login attempts and replay attacks.
-
-Server behaviour
-~~~~~~~~~~~~~~~~
-
-Redirecting to the Authentication server
-++++++++++++++++++++++++++++++++++++++++
-
-The server should handle
-``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`` as follows:
-
-#. It should build a suitable request for the SSO system.
-
-#. It should store enough state that the flow can be securely resumed after the
- SSO process completes. One way to do this is by storing a cookie which is
- stored in the user's browser, by adding a ``Set-Cookie`` header to the response.
-
-#. It should redirect the user's browser to the SSO login page with the
- appropriate parameters.
-
-See also the "Security considerations" below.
-
-.. TODO-spec:
-
- It might be nice if the server did some validation of the ``redirectUrl``
- parameter, so that we could check that aren't going to redirect to a non-TLS
- endpoint, and to give more meaningful errors in the case of
- faulty/poorly-configured clients.
-
-Handling the callback from the Authentication server
-++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Note that there will normally be a single callback URI which is used for both login
-and user-interactive authentication: it is up to the homeserver implementation
-to distinguish which is taking place.
-
-The homeserver should validate the response from the SSO system: this may
-require additional calls to the authentication server, and/or may require
-checking a signature on the response.
-
-The homeserver then proceeds as follows:
-
-#. The homeserver MUST map the user details received from the authentication
- server to a valid `Matrix user identifier <../appendices.html#user-identifiers>`_.
- The guidance in `Mapping from other character sets
- <../appendices.html#mapping-from-other-character-sets>`_ may be useful.
-
-#. If the generated user identifier represents a new user, it should be
- registered as a new user.
-
-#. The homeserver should generate a short-term login token. This is an opaque
- token, suitable for use with the ``m.login.token`` type of the |/login|_
- API. The lifetime of this token SHOULD be limited to around five
- seconds.
-
-#. The homeserver adds a query parameter of ``loginToken``, with the value of
- the generated login token, to the ``redirectUrl`` given in the
- ``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect``
- request. (Note: ``redirectURL`` may or may not include existing query
- parameters. If it already includes one or more ``loginToken`` parameters,
- they should be removed before adding the new one.)
-
-#. The homeserver redirects the user's browser to the URI thus built.
-
-Security considerations
-~~~~~~~~~~~~~~~~~~~~~~~
-
-1. Homeservers should ensure that login tokens are not sent to malicious
- clients.
-
- For example, consider a homeserver at ``server.example.org``. An attacker tricks
- a victim into following a link to
- ``https://server.example.org/login/sso/redirect?redirectUrl=https://evil.com``,
- resulting in a login token being sent to the attacker-controlled site
- ``evil.com``. This is a form of cross-site request forgery (CSRF).
-
- To mitigate this, Homeservers SHOULD confirm with the user that they are
- happy to grant access to their matrix account to the site named in the
- ``redirectUrl``. This can be done either *before* redirecting to the SSO
- login page when handling the
- ``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`` endpoint, or
- *after* login when handling the callback from the authentication server. (If
- the check is performed before redirecting, it is particularly important that
- the homeserver guards against unsolicited authentication attempts as below).
-
- It may be appropriate to whitelist a set of known-trusted client URLs in
- this process. In particular, the homeserver's own `login fallback`_
- implementation could be excluded.
-
-2. For added security, homeservers SHOULD guard against unsolicited
- authentication attempts by tracking pending requests. One way to do this is
- to set a cookie when handling
- ``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect``, which is
- checked and cleared when handling the callback from the authentication
- server.
-
-SSO during User-Interactive Authentication
-------------------------------------------
-
-`User-interactive authentication`_ is used by client-server
-endpoints which require additional confirmation of the user's identity (beyond
-holding an access token). Typically this means that the user must re-enter
-their password, but for homeservers which delegate to an SSO server, this means
-redirecting to the authentication server during user-interactive auth.
-
-The implemementation of this is based on the `Fallback`_ mechanism for
-user-interactive auth.
-
-Client behaviour
-----------------
-
-Clients do not need to take any particular additional steps beyond ensuring
-that the fallback mechanism has been implemented, and treating the
-``m.login.sso`` authentication type the same as any other unknown type
-(i.e. they should open a browser window for
-``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web?session=``.
-Once the flow has completed, the client retries the request with the session
-only.)
-
-Server behaviour
-----------------
-
-Redirecting to the Authentication server
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The server should handle
-``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web`` in
-much the same way as
-``/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect``, which is to say:
-
-#. It should build a suitable request for the SSO system.
-
-#. It should store enough state that the flow can be securely resumed after the
- SSO process completes. One way to do this is by storing a cookie which is
- stored in the user's browser, by adding a ``Set-Cookie`` header to the response.
-
-#. It should redirect the user's browser to the SSO login page with the
- appropriate parameters.
-
-See also the "Security considerations" below.
-
-Handling the callback from the Authentication server
-++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-Note that there will normally be a single callback URI which is used for both login
-and user-interactive authentication: it is up to the homeserver implementation
-to distinguish which is taking place.
-
-The homeserver should validate the response from the SSO system: this may
-require additional calls to the authentication server, and/or may require
-checking a signature on the response.
-
-The homeserver then returns the `user-interactive authentication fallback
-completion`_ page to the user's browser.
-
-Security considerations
-+++++++++++++++++++++++
-
-1. Confirming the operation
-
- The homeserver SHOULD confirm that the user is happy for the operation to go
- ahead. The goal of the user-interactive authentication operation is to guard
- against a compromised ``access_token`` being used to take over the user's
- account. Simply redirecting the user to the SSO system is insufficient,
- since they may not realise what is being asked of them, or the SSO system
- may even confirm the authentication automatically.
-
- For example, the homeserver might serve a page with words to the effect of:
-
- A client is trying to remove a device from your account. To confirm this
- action, re-authenticate with single sign-on. If you did not expect this, your
- account may be compromised!
-
- This confirmation could take place before redirecting to the SSO
- authentication page (when handling the
- ``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web``
- endpoint), or *after* authentication when handling the callback from the
- authentication server. (If the check is performed before redirecting, it is
- particularly important that the homeserver guards against unsolicited
- authentication attempts as below).
-
-2. For added security, homeservers SHOULD guard against unsolicited
- authentication attempts by tracking pending requests. One way to do this is
- to set a cookie when handling
- ``/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web``,
- which is checked and cleared when handling the callback from the
- authentication server.
-
-
-
-.. |GET /login| replace:: ``GET /login``
-.. _GET /login: #get-matrix-client-%CLIENT_MAJOR_VERSION%-login
-.. |/login| replace:: ``/login``
-.. _/login: #post-matrix-client-%CLIENT_MAJOR_VERSION%-login
-.. |/login/sso/redirect| replace:: ``/login/sso/redirect``
-.. _/login/sso/redirect: #get-matrix-client-%CLIENT_MAJOR_VERSION%-login-sso-redirect
diff --git a/specification/modules/stickers.rst b/specification/modules/stickers.rst
deleted file mode 100644
index 346b0d84fe9..00000000000
--- a/specification/modules/stickers.rst
+++ /dev/null
@@ -1,53 +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.
-
-Sticker Messages
-================
-
-.. _module:stickers:
-
-This module allows users to send sticker messages in to rooms or direct
-messaging sessions.
-
-Sticker messages are specialised image messages that are displayed without
-controls (e.g. no "download" link, or light-box view on click, as would be
-displayed for for `m.image`_ events).
-
-Sticker messages are intended to provide simple "reaction" events in the message
-timeline. The matrix client should provide some mechanism to display the sticker
-"body" e.g. as a tooltip on hover, or in a modal when the sticker image is
-clicked.
-
-Events
-------
-Sticker events are received as a single ``m.sticker`` event in the
-``timeline`` section of a room, in a ``/sync``.
-
-{{m_sticker_event}}
-
-Client behaviour
-----------------
-
-Clients supporting this message type should display the image content from the
-event URL directly in the timeline.
-
-A thumbnail image should be provided in the ``info`` object. This is
-largely intended as a fallback for clients that do not fully support the
-``m.sticker`` event type. In most cases it is fine to set the thumbnail URL to the
-same URL as the main event content.
-
-It is recommended that sticker image content should be 512x512 pixels in size
-or smaller. The dimensions of the image file should be twice the intended
-display size specified in the ``info`` object in order to assist
-rendering sharp images on higher DPI screens.
diff --git a/specification/modules/tags.rst b/specification/modules/tags.rst
deleted file mode 100644
index a5e2377017b..00000000000
--- a/specification/modules/tags.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2018 New Vector Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Tagging
-============
-
-.. _module:tagging:
-
-Users can add tags to rooms. Tags are namespaced strings used to label rooms.
-A room may have multiple tags. Tags are only visible to the user that set them
-but are shared across all their devices.
-
-Events
-------
-
-The tags on a room are received as single ``m.tag`` event in the
-``account_data`` section of a room. The content of the ``m.tag`` event is a
-``tags`` key whose value is an object mapping the name of each tag to another
-object.
-
-The JSON object associated with each tag gives information about the tag, e.g how
-to order the rooms with a given tag.
-
-Ordering information is given under the ``order`` key as a number between 0 and
-1. The numbers are compared such that 0 is displayed first. Therefore a room
-with an ``order`` of ``0.2`` would be displayed before a room with an ``order``
-of ``0.7``. If a room has a tag without an ``order`` key then it should appear
-after the rooms with that tag that have an ``order`` key.
-
-The name of a tag MUST NOT exceed 255 bytes.
-
-The tag namespace is defined as follows:
-
-* The namespace ``m.*`` is reserved for tags defined in the Matrix specification. Clients must ignore
- any tags in this namespace they don't understand.
-* The namespace ``u.*`` is reserved for user-defined tags. The portion of the string after the ``u.``
- is defined to be the display name of this tag. No other semantics should be inferred from tags in
- this namespace.
-* A client or app willing to use special tags for advanced functionality should namespace them similarly to state keys: ``tld.name.*``
-* Any tag in the ``tld.name.*`` form but not matching the namespace of the current client should be ignored
-* Any tag not matching the above rules should be interpreted as a user tag from the ``u.*`` namespace, as if
- the name had already had ``u.`` stripped from the start (ie. the name of the tag is used as the
- display name directly). These non-namespaced tags are supported for historical reasons. New tags should use
- one of the defined namespaces above.
-
-Several special names are listed in the specification:
-The following tags are defined in the ``m.*`` namespace:
-
-* ``m.favourite``: The user's favourite rooms. These should be shown with higher precedence than other rooms.
-* ``m.lowpriority``: These should be shown with lower precedence than others.
-* ``m.server_notice``: Used to identify `Server Notice Rooms <#module-server-notices>`_.
-
-{{m_tag_event}}
-
-Client Behaviour
-----------------
-
-{{tags_cs_http_api}}
diff --git a/specification/modules/third_party_networks.rst b/specification/modules/third_party_networks.rst
deleted file mode 100644
index cd4ce414322..00000000000
--- a/specification/modules/third_party_networks.rst
+++ /dev/null
@@ -1,20 +0,0 @@
-Third Party Networks
-====================
-
-.. _module:third-party-networks:
-
-Application services can provide access to third party networks via bridging.
-This allows Matrix users to communicate with users on other communication
-platforms, with messages ferried back and forth by the application service. A
-single application service may bridge multiple third party networks, and many
-individual locations within those networks. A single third party network
-location may be bridged to multiple Matrix rooms.
-
-Third Party Lookups
--------------------
-
-A client may wish to provide a rich interface for joining third party
-locations and connecting with third party users. Information necessary for
-such an interface is provided by third party lookups.
-
-{{third_party_lookup_cs_http_api}}
\ No newline at end of file
diff --git a/specification/modules/typing_notifications.rst b/specification/modules/typing_notifications.rst
deleted file mode 100644
index 964b804a237..00000000000
--- a/specification/modules/typing_notifications.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Typing Notifications
-====================
-
-.. _module:typing:
-
-Users may wish to be informed when another user is typing in a room. This can be
-achieved using typing notifications. These are ephemeral events scoped to a
-``room_id``. This means they do not form part of the
-`Event Graph `_ but still have a ``room_id`` key.
-
-Events
-------
-
-{{m_typing_event}}
-
-Client behaviour
-----------------
-
-When a client receives an ``m.typing`` event, it MUST use the user ID list to
-**REPLACE** its knowledge of every user who is currently typing. The reason for
-this is that the server *does not remember* users who are not currently typing
-as that list gets big quickly. The client should mark as not typing any user ID
-who is not in that list.
-
-It is recommended that clients store a ``boolean`` indicating whether the user
-is typing or not. Whilst this value is ``true`` a timer should fire periodically
-every N seconds to send a typing HTTP request. The value of N is recommended to
-be no more than 20-30 seconds. This request should be re-sent by the client to
-continue informing the server the user is still typing. As subsequent
-requests will replace older requests, a safety margin of 5 seconds before the
-expected timeout runs out is recommended. When the user stops typing, the
-state change of the ``boolean`` to ``false`` should trigger another HTTP request
-to inform the server that the user has stopped typing.
-
-{{typing_cs_http_api}}
-
-Security considerations
------------------------
-
-Clients may not wish to inform everyone in a room that they are typing and
-instead only specific users in the room.
-
diff --git a/specification/modules/voip_events.rst b/specification/modules/voip_events.rst
deleted file mode 100644
index fab5651702a..00000000000
--- a/specification/modules/voip_events.rst
+++ /dev/null
@@ -1,116 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Voice over IP
-=============
-
-.. _module:voip:
-
-This module outlines how two users in a room can set up a Voice over IP (VoIP)
-call to each other. Voice and video calls are built upon the WebRTC 1.0 standard.
-Call signalling is achieved by sending `message events`_ to the room. In this
-version of the spec, only two-party communication is supported (e.g. between two
-peers, or between a peer and a multi-point conferencing unit).
-This means that clients MUST only send call events to rooms with exactly two
-participants.
-
-.. _message events: `sect:events`_
-
-Events
-------
-
-{{voip_events}}
-
-Client behaviour
-----------------
-
-A call is set up with message events exchanged as follows:
-
-::
-
- Caller Callee
- [Place Call]
- m.call.invite ----------->
- m.call.candidate -------->
- [..candidates..] -------->
- [Answers call]
- <--------------- m.call.answer
- [Call is active and ongoing]
- <--------------- m.call.hangup
-
-Or a rejected call:
-
-::
-
- Caller Callee
- m.call.invite ------------>
- m.call.candidate --------->
- [..candidates..] --------->
- [Rejects call]
- <-------------- m.call.hangup
-
-Calls are negotiated according to the WebRTC specification.
-
-Glare
-~~~~~
-
-"Glare" is a problem which occurs when two users call each other at roughly the
-same time. This results in the call failing to set up as there already is an
-incoming/outgoing call. A glare resolution algorithm can be used to determine
-which call to hangup and which call to answer. If both clients implement the
-same algorithm then they will both select the same call and the call will be
-successfully connected.
-
-
-As calls are "placed" to rooms rather than users, the glare resolution algorithm
-outlined below is only considered for calls which are to the same room. The
-algorithm is as follows:
-
-- If an ``m.call.invite`` to a room is received whilst the client is
- **preparing to send** an ``m.call.invite`` to the same room:
-
- * the client should cancel its outgoing call and instead
- automatically accept the incoming call on behalf of the user.
-
-- If an ``m.call.invite`` to a room is received **after the client has sent**
- an ``m.call.invite`` to the same room and is waiting for a response:
-
- * the client should perform a lexicographical comparison of the call IDs of
- the two calls and use the *lesser* of the two calls, aborting the
- greater. If the incoming call is the lesser, the client should accept
- this call on behalf of the user.
-
-
-The call setup should appear seamless to the user as if they had simply placed
-a call and the other party had accepted. This means any media stream that had been
-setup for use on a call should be transferred and used for the call that
-replaces it.
-
-Server behaviour
-----------------
-
-The homeserver MAY provide a TURN server which clients can use to contact the
-remote party. The following HTTP API endpoints will be used by clients in order
-to get information about the TURN server.
-
-{{voip_cs_http_api}}
-
-
-Security considerations
------------------------
-
-Calls should only be placed to rooms with one other user in them. If they are
-placed to group chat rooms it is possible that another user will intercept and
-answer the call.
-
diff --git a/specification/proposals.rst b/specification/proposals.rst
deleted file mode 100644
index 94878f80497..00000000000
--- a/specification/proposals.rst
+++ /dev/null
@@ -1,6 +0,0 @@
-Tables of Tracked Proposals
----------------------------
-
-This file is generated by an automated process on our build server.
-
-View the current live version `at https://matrix.org/docs/spec/proposals `_.
diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst
deleted file mode 100644
index 6d8dc8a9fa4..00000000000
--- a/specification/proposals_intro.rst
+++ /dev/null
@@ -1,503 +0,0 @@
-.. raw:: html
-
- %proposalscssinjection%
-
-.. title:: Proposals for Spec Changes to Matrix
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Proposals for Spec Changes to Matrix
-------------------------------------
-
-If you are interested in submitting a change to the Matrix Specification,
-please take note of the following guidelines.
-
-Most changes to the Specification require a formal proposal. Bug fixes, typos,
-and clarifications to existing behaviour do not need proposals - see the
-`contributing guide `_
-for more information on what does and does not need a proposal.
-
-The proposal process involves some technical writing, having it reviewed by
-everyone, having the proposal being accepted, then actually having your ideas
-implemented as committed changes to the `Specification repository
-`_.
-
-Meet the `members of the Core Team
-`_, a group of
-individuals tasked with ensuring the spec process is as smooth and painless as
-possible. Members of the Spec Core Team will do their best to participate in
-discussion, summarise when things become long-winded, and generally try to act
-towards the benefit of everyone. As a majority, team members have the ability
-to change the state of a proposal, and individually have the final say in
-proposal discussion.
-
-Guiding Principles
-------------------
-
-Proposals **must** act to the greater benefit of the entire Matrix ecosystem,
-rather than benefiting or privileging any single player or subset of players -
-and must not contain any patent encumbered intellectual property. Members of
-the Core Team pledge to act as a neutral custodian for Matrix on behalf of the
-whole ecosystem.
-
-For clarity: the Matrix ecosystem is anyone who uses the Matrix protocol. That
-includes client users, server admins, client developers, bot developers,
-bridge and application service developers, users and admins who are indirectly
-using Matrix via 3rd party networks which happen to be bridged, server developers,
-room moderators and admins, companies/projects building products or services on
-Matrix, spec contributors, translators, and those who created it in
-the first place.
-
-"Greater benefit" could include maximising:
-
-* the number of end-users reachable on the open Matrix network
-* the number of regular users on the Matrix network (e.g. 30-day retained
- federated users)
-* the number of online servers in the open federation
-* the number of developers building on Matrix
-* the number of independent implementations which use Matrix
-* the number of bridged end-users reachable on the open Matrix network
-* the signal-to-noise ratio of the content on the open Matrix network (i.e. minimising spam)
-* the ability for users to discover content on their terms (empowering them to select what to see and what not to see)
-* the quality and utility of the Matrix spec (as defined by ease and ability
- with which a developer can implement spec-compliant clients, servers, bots,
- bridges, and other integrations without needing to refer to any other
- external material)
-
-In addition, proposal authors are expected to uphold the following values in
-their proposed changes to the Matrix protocol:
-
-* Supporting the whole long-term ecosystem rather than individual stakeholder gain
-* Openness rather than proprietary lock-in
-* Interoperability rather than fragmentation
-* Cross-platform rather than platform-specific
-* Collaboration rather than competition
-* Accessibility rather than elitism
-* Transparency rather than stealth
-* Empathy rather than contrariness
-* Pragmatism rather than perfection
-* Proof rather than conjecture
-
-Please `see MSC1779 `_
-for full details of the project's Guiding Principles.
-
-Technical notes
----------------
-
-Proposals **must** develop Matrix as a layered protocol: with new features
-building on layers of shared abstractions rather than introducing tight vertical
-coupling within the stack. This ensures that new features can evolve rapidly by
-building on existing layers and swapping out old features without impacting the
-rest of the stack or requiring substantial upgrades to the whole ecosystem.
-This is critical for Matrix to rapidly evolve and compete effectively with
-centralised systems, despite being a federated protocol.
-
-For instance, new features should be implemented using the highest layer
-abstractions possible (e.g. new event types, which layer on top of the existing
-room semantics, and so don't even require any API changes). Failing that, the
-next recourse would be backwards-compatible changes to the next layer down (e.g.
-room APIs); failing that, considering changes to the format of events or the
-DAG; etc. It would be a very unusual feature which doesn't build on the
-existing infrastructure provided by the spec and instead created new primitives
-or low level APIs.
-
-Backwards compatibility is very important for Matrix, but not at the expense of
-hindering the protocol's evolution. Backwards incompatible changes to endpoints
-are allowed when no other alternative exists, and must be versioned under a new
-major release of the API. Backwards incompatible changes to the room algorithm
-are also allowed when no other alternative exists, and must be versioned under a
-new version of the room algorithm.
-
-There is sometimes a dilemma over where to include higher level features: for
-instance, should video conferencing be formalised in the spec, or should it be
-implemented via widgets? Should reputation systems be specified? Should search
-engine behaviour be specified?
-
-There is no universal answer to this, but the following guidelines should be
-applied:
-
-1. If the feature would benefit the whole Matrix ecosystem and is aligned with
- the guiding principles above, then it should be supported by the spec.
-2. If the spec already makes the feature possible without changing any of the
- implementations and spec, then it may not need to be added to the spec.
-3. However, if the best user experience for a feature does require custom
- implementation behaviour then the behaviour should be defined in the spec
- such that all implementations may implement it.
-4. However, the spec must never add dependencies on unspecified/nonstandardised
- 3rd party behaviour.
-
-As a worked example:
-
-1. Video conferencing is clearly a feature which would benefit
- the whole ecosystem, and so the spec should find a way to make it happen.
-2. Video conferencing can be achieved by widgets without requiring any
- compulsory changes to changes to clients nor servers to work, and so could be
- omitted from the spec.
-3. A better experience could be achieved by embedding Jitsi natively into clients
- rather than using a widget...
-4. ...except that would add a dependency on unspecified/nonstandardised 3rd party
- behaviour, so must not be added to the spec.
-
-Therefore, our two options in the specific case of video conferencing are
-either to spec SFU conferencing semantics for WebRTC (or refer to an existing spec
-for doing so), or to keep it as a widget-based approach (optionally with widget
-extensions specific for more deeply integrating video conferencing use cases).
-
-As an alternative example: it's very unlikely that "how to visualise Magnetic
-Resonsance Imaging data over Matrix" would ever be added to the Matrix spec
-(other than perhaps a custom event type in a wider standardised Matrix event
-registry) given that the spec's existing primitives of file transfer and
-extensible events (MSC1767) give excellent tools for transfering and
-visualising arbitrary rich data.
-
-Supporting public search engines are likely to not require custom spec features
-(other than possibly better bulk access APIs), given they can be implemented as
-clients using the existing CS API. An exception could be API features required
-by decentralised search infrastructure (avoiding centralisation of power by
-a centralised search engine).
-
-Features such as reactions, threaded messages, editable messages,
-spam/abuse/content filtering (and reputation systems), are all features which
-would clearly benefit the whole Matrix ecosystem, and cannot be implemented in an
-interoperable way using the current spec; so they necessitate a spec change.
-
-Process
--------
-
-The process for submitting a Matrix Spec Change (MSC) Proposal in detail is as
-follows:
-
-- Create a first draft of your proposal using `GitHub-flavored markdown
- `_
-
- - In the document, clearly state the problem being solved, and the possible
- solutions being proposed for solving it and their respective trade-offs.
- - Proposal documents are intended to be as lightweight and flexible as the
- author desires; there is no formal template; the intention is to iterate
- as quickly as possible to get to a good design.
- - However, a `template with suggested headers
- `_
- is available to get you started if necessary.
- - Take care in creating your proposal. Specify your intended changes, and
- give reasoning to back them up. Changes without justification will likely
- be poorly received by the community.
-
-- Fork and make a PR to the `matrix-doc
- `_ repository. The ID of your PR
- will become the MSC ID for the lifetime of your proposal.
-
- - The proposal must live in the ``proposals/`` directory with a filename that
- follows the format ``1234-my-new-proposal.md`` where ``1234`` is the MSC
- ID.
- - Your PR description must include a link to the rendered markdown document
- and a summary of the proposal.
- - It is often very helpful to link any related MSCs or `matrix-doc issues
- `_ to give context
- for the proposal.
- - Additionally, please be sure to sign off your proposal PR as per the
- guidelines listed on `CONTRIBUTING.rst
- `_.
-
-- Gather feedback as widely as possible.
-
- - The aim is to get maximum consensus towards an optimal solution. Sometimes
- trade-offs are required to meet this goal. Decisions should be made to the
- benefit of all major use cases.
- - A good place to ask for feedback on a specific proposal is
- `#matrix-spec:matrix.org `_.
- If preferred, an alternative room can be created and advertised in
- #matrix-spec:matrix.org. Please also link to the room in your PR
- description.
- - For additional discussion areas, know that that #matrix-dev:matrix.org is
- for developers using existing Matrix APIs, #matrix:matrix.org is for users
- trying to run Matrix apps (clients & servers) and
- #matrix-architecture:matrix.org is for cross-cutting discussion of matrix's
- architectural design.
- - The point of the spec proposal process is to be collaborative rather than
- competitive, and to try to solve the problem in question with the optimal
- set of trade-offs. The author should neutrally gather the various
- viewpoints and get consensus, but this can sometimes be time-consuming (or
- the author may be biased), in which case an impartial 'shepherd' can be
- assigned to help guide the proposal through this process instead. A shepherd is
- typically a neutral party from the Spec Core Team or an experienced member of
- the community. There is no formal process for assignment. Simply ask for a
- shepherd to help get your proposal through and one will be assigned based
- on availability. Having a shepherd is not a requirement for proposal
- acceptance.
-
-- Members of the Spec Core Team and community will review and discuss the PR in the
- comments and in relevant rooms on Matrix. Discussion outside of GitHub should
- be summarised in a comment on the PR.
-- When a member of the Spec Core Team believes that no new discussion points are
- being made, and the proposal has suitable evidence of working (see `implementing a
- proposal`_ below), they will propose a motion for a final comment period (FCP),
- along with a *disposition* of either merge, close or postpone. This FCP is
- provided to allow a short period of time for any invested party to provide a
- final objection before a major decision is made. If sufficient reasoning is
- given, an FCP can be cancelled. It is often preceded by a comment summarising
- the current state of the discussion, along with reasoning for its occurrence.
-- A concern can be raised by a Spec Core Team member at any time, which will block
- an FCP from beginning. An FCP will only begin when 75% of the members of the
- Spec Core Team team agree on its outcome, and all existing concerns have been
- resolved.
-- The FCP will then begin and last for 5 days, giving anyone else some time to
- speak up before it concludes. On its conclusion, the disposition of the FCP
- will be carried out. If sufficient reasoning against the disposition is
- raised, the FCP can be cancelled and the MSC will continue to evolve
- accordingly.
-- Once the proposal has been accepted and merged, it is time to submit the
- actual change to the Specification that your proposal reasoned about. This is
- known as a spec PR. However in order for the spec PR to be accepted, an
- implementation **must** be shown to prove that it works well in practice. A
- link to the implementation should be included in the PR description. In
- addition, any significant unforeseen changes to the original idea found
- during this process will warrant another MSC. Any minor, non-fundamental
- changes are allowed but **must** be documented in the original proposal
- document. This ensures that someone reading a proposal in the future doesn't
- assume old information wasn't merged into the spec.
-
- - Similar to the proposal PR, please sign off the spec PR as per the
- guidelines on `CONTRIBUTING.rst
- `_.
-
-- Your PR will then be reviewed and hopefully merged on the grounds it is
- implemented sufficiently. If so, then give yourself a pat on the back knowing
- you've contributed to the Matrix protocol for the benefit of users and
- developers alike :)
-
-The process for handling proposals is shown visually in the following diagram.
-Note that the lifetime of a proposal is tracked through the corresponding
-labels for each stage on the `matrix-doc
-`_ issue and pull request trackers.
-
-::
-
- + +
- Proposals | Spec PRs | Additional States
- +-------+ | +------+ | +---------------+
- | |
- +----------------------+ | +---------+ | +-----------+
- | | | | | | | |
- | Proposal | | +------= Spec PR | | | Postponed |
- | Drafting and Initial | | | | Missing | | | |
- | Feedback Gathering | | | | | | +-----------+
- | | | | +----+----+ |
- +----------+-----------+ | | | | +----------+
- | | | v | | |
- v | | +-----------------+ | | Closed |
- +-------------------+ | | | | | | |
- | | | | | Spec PR Created | | +----------+
- | Proposal PR | | | | and In Review | |
- | In Review | | | | | |
- | | | | +--------+--------+ |
- +---------+---------+ | | | |
- | | | v |
- v | | +-----------+ |
- +----------------------+ | | | | |
- | | | | | Spec PR | |
- | Proposed Final | | | | Merged! | |
- | Comment Period | | | | | |
- | | | | +-----------+ |
- +----------+-----------+ | | |
- | | | |
- v | | |
- +----------------------+ | | |
- | | | | |
- | Final Comment Period | | | |
- | | | | |
- +----------+-----------+ | | |
- | | | |
- v | | |
- +----------------------+ | | |
- | | | | |
- | Final Comment Period | | | |
- | Complete | | | |
- | | | | |
- +----------+-----------+ | | |
- | | | |
- +-----------------+ |
- | |
- + +
-
-Lifetime States
----------------
-
-**Note:** All labels are to be placed on the proposal PR.
-
-=============================== ============================= ====================================
-Name GitHub Label Description
-=============================== ============================= ====================================
-Proposal Drafting and Feedback N/A A proposal document which is still work-in-progress but is being shared to incorporate feedback. Please prefix your proposal's title with ``[WIP]`` to make it easier for reviewers to skim their notifications list.
-Proposal In Review proposal-in-review A proposal document which is now ready and waiting for review by the Spec Core Team and community
-Proposed Final Comment Period proposed-final-comment-period Currently awaiting signoff of a 75% majority of team members in order to enter the final comment period
-Final Comment Period final-comment-period A proposal document which has reached final comment period either for merge, closure or postponement
-Final Commment Period Complete finished-final-comment-period The final comment period has been completed. Waiting for a demonstration implementation
-Spec PR Missing spec-pr-missing The proposal has been agreed, and proven with a demonstration implementation. Waiting for a PR against the Spec
-Spec PR In Review spec-pr-in-review The spec PR has been written, and is currently under review
-Spec PR Merged merged A proposal with a sufficient working implementation and whose Spec PR has been merged!
-Postponed proposal-postponed A proposal that is temporarily blocked or a feature that may not be useful currently but perhaps
- sometime in the future
-Closed proposal-closed A proposal which has been reviewed and deemed unsuitable for acceptance
-Obsolete obsolete A proposal which has been made obsolete by another proposal or decision elsewhere.
-=============================== ============================= ====================================
-
-Categories
-----------
-
-We use category labels on MSCs to place them into a track of work. The Spec Core Team
-decides which of the tracks they are focusing on for the next while and generally makes
-an effort to pull MSCs out of that category when possible.
-
-The current categories are:
-
-============ ================= ======================================
-Name Github Label Description
-============ ================= ======================================
-Core kind:core Important for the protocol's success.
-Feature kind:feature Nice to have additions to the spec.
-Maintenance kind:maintenance Fixes or clarifies existing spec.
-============ ================= ======================================
-
-Some examples of core MSCs would be aggregations, cross-signing, and groups/communities.
-These are the sorts of things that if not implemented could cause the protocol to
-fail or become second-class. Features would be areas like enhanced media APIs,
-new transports, and bookmarks in comparison. Finally, maintenance MSCs would include
-improving error codes, clarifying what is required of an API, and adding properties
-to an API which makes it easier to use.
-
-The Spec Core Team assigns a category to each MSC based on the descriptions above.
-This can mean that new MSCs get categorized into an area the team isn't focused on,
-though that can always change as priorities evolve. We still encourage that MSCs be
-opened, even if not the focus for the time being, as they can still make progress and
-even be merged without the Spec Core Team focusing on them specifically.
-
-Implementing a proposal
------------------------
-
-As part of the proposal process the spec core team will require evidence of the MSC
-working in order for it to move into FCP. This can usually be a branch/pull request
-to whichever implementation of choice that proves the MSC works in practice, though
-in some cases the MSC itself will be small enough to be considered proven. Where it's
-unclear if a MSC will require an implementation proof, ask in `#matrix-spec:matrix.org
-`_.
-
-Early release of a MSC/idea
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To help facilitate early releases of software dependent on a spec release, implementations
-are required to use the following process to ensure that the official Matrix namespace
-is not cluttered with development or testing data.
-
-.. Note::
- Unreleased implementations (including proofs-of-concept demonstrating that a
- particular MSC works) do not have to follow this process.
-
-1. Have an idea for a feature.
-2. Implement the feature using unstable endpoints, vendor prefixes, and unstable
- feature flags as appropriate.
-
- * When using unstable endpoints, they MUST include a vendor prefix. For example:
- ``/_matrix/client/unstable/com.example/login``. Vendor prefixes throughout Matrix
- always use the Java package naming convention. The MSC for the feature should
- identify which preferred vendor prefix is to be used by early adopters.
- * Note that unstable namespaces do not automatically inherit endpoints from stable
- namespaces: for example, the fact that ``/_matrix/client/r0/sync`` exists does
- not imply that ``/_matrix/client/unstable/com.example/sync`` exists.
- * If the client needs to be sure the server supports the feature, an unstable
- feature flag that MUST be vendor prefixed is to be used. This kind of flag shows
- up in the ``unstable_features`` section of ``/versions`` as, for example,
- ``com.example.new_login``. The MSC for the feature should identify which preferred
- feature flag is to be used by early adopters.
- * When using this approach correctly, the implementation can ship/release the
- feature at any time, so long as the implementation is able to accept the technical
- debt that results from needing to provide adequate backwards and forwards
- compatibility. The implementation MUST support the flag (and server-side implementation) disappearing and be
- generally safe for users. Note that implementations early in the MSC review
- process may also be required to provide backwards compatibility with earlier
- editions of the proposal.
- * If the implementation cannot support the technical debt (or if it's impossible
- to provide forwards/backwards compatibility - e.g. a user authentication change
- which can't be safely rolled back), the implementation should not attempt to
- implement the feature and should instead wait for a spec release.
- * If at any point after early release, the idea changes in a backwards-incompatible way, the feature flag should also change so that
- implementations can adapt as needed.
-
-3. In parallel, or ahead of implementation, open an MSC and solicit review per above.
-4. Before FCP can be called, the Spec Core Team will require evidence of the MSC
- working as proposed. A typical example of this is an implementation of the MSC,
- though the implementation does not need to be shipped anywhere and can therefore
- avoid the forwards/backwards compatibility concerns mentioned here.
-5. The FCP process is completed, and assuming nothing is flagged the MSC lands.
-6. A spec PR is written to incorporate the changes into Matrix.
-7. A spec release happens.
-8. Implementations switch to using stable prefixes (e.g.: ``/r0``) if the server
- supports the specification version released. If the server doesn't advertise the
- specification version, but does have the feature flag, unstable prefixes should
- still be used.
-9. A transition period of about 2 months starts immediately after the spec release,
- before implementations start to encourage other implementations to switch
- to stable endpoints. For example, a server implementation should start asking
- client implementations to support the stable endpoints 2 months after the spec
- release, if they haven't already. The same applies in the reverse: if clients
- cannot switch to stable prefixes because server implementations haven't started
- supporting the new spec release, some noise should be raised in the general direction
- of the implementation.
-
-.. Note::
- MSCs MUST still describe what the stable endpoints/feature looks like with a note
- towards the bottom for what the unstable feature flag/prefixes are. For example,
- a MSC would propose `/_matrix/client/r0/new/endpoint`, not `/_matrix/client/unstable/
- com.example/new/endpoint`.
-
-In summary:
-
-* Implementations MUST NOT use stable endpoints before the MSC is in the spec. This
- includes NOT using stable endpoints in the period between completion of FCP and release of the spec.
- passed.
-* Implementations are able to ship features that are exposed to users by default before
- an MSC has been merged to the spec, provided they follow the process above.
-* Implementations SHOULD be wary of the technical debt they are incurring by moving faster
- than the spec.
-* The vendor prefix is chosen by the developer of the feature, using the Java package
- naming convention. The foundation's preferred vendor prefix is `org.matrix`.
-* The vendor prefixes, unstable feature flags, and unstable endpoints should be included
- in the MSC, though the MSC MUST be written in a way that proposes new stable endpoints.
- Typically this is solved by a small table at the bottom mapping the various values
- from stable to unstable.
-
-Proposal Tracking
------------------
-
-This is a living document generated from the list of proposals on the issue and
-pull request trackers of the `matrix-doc
-`_ repo.
-
-We use labels and some metadata in MSC PR descriptions to generate this page.
-Labels are assigned by the Spec Core Team whilst triaging the proposals based on those
-which exist in the `matrix-doc `_
-repo already.
-
-It is worth mentioning that a previous version of the MSC process used a
-mixture of GitHub issues and PRs, leading to some MSC numbers deriving from
-GitHub issue IDs instead. A useful feature of GitHub is that it does
-automatically resolve to an issue, if an issue ID is placed in a pull URL. This
-means that https://github.com/matrix-org/matrix-doc/pull/$MSCID will correctly
-resolve to the desired MSC, whether it started as an issue or a PR.
-
-Other metadata:
-
-- The MSC number is taken from the GitHub Pull Request ID. This is carried for
- the lifetime of the proposal. These IDs do not necessary represent a
- chronological order.
-- The GitHub PR title will act as the MSC's title.
-- Please link to the spec PR (if any) by adding a "PRs: #1234" line in the
- issue description.
-- The creation date is taken from the GitHub PR, but can be overridden by
- adding a "Date: yyyy-mm-dd" line in the PR description.
-- Updated Date is taken from GitHub.
-- Author is the creator of the MSC PR, but can be overridden by adding a
- "Author: @username" line in the body of the issue description. Please make
- sure @username is a GitHub user (include the @!)
-- A shepherd can be assigned by adding a "Shepherd: @username" line in the
- issue description. Again, make sure this is a real GitHub user.
diff --git a/specification/push_gateway.rst b/specification/push_gateway.rst
deleted file mode 100644
index 46c0000d3c2..00000000000
--- a/specification/push_gateway.rst
+++ /dev/null
@@ -1,95 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2018 New Vector Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Push Gateway API
-================
-
-{{unstable_warning_block_PUSH_GATEWAY_RELEASE_LABEL}}
-
-Clients may want to receive push notifications when events are received at
-the homeserver. This is managed by a distinct entity called the Push Gateway.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Changelog
----------
-
-.. topic:: Version: %PUSH_GATEWAY_RELEASE_LABEL%
-{{push_gateway_changelog}}
-
-This version of the specification is generated from
-`matrix-doc `_ as of Git commit
-`{{git_version}} `_.
-
-For the full historical changelog, see
-https://github.com/matrix-org/matrix-doc/blob/master/changelogs/push_gateway.rst
-
-Other versions of this specification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following other versions are also available, in reverse chronological order:
-
-- `HEAD `_: Includes all changes since the latest versioned release.
-- `r0.1.0 `_
-
-Overview
---------
-
-A client's homeserver forwards information about received events to the push
-gateway. The gateway then submits a push notification to the push notification
-provider (e.g. APNS, GCM).
-
-
-::
-
- +--------------------+ +-------------------+
- Matrix HTTP | | | |
- Notification Protocol | App Developer | | Device Vendor |
- | | | |
- +-------------------+ | +----------------+ | | +---------------+ |
- | | | | | | | | | |
- | Matrix homeserver +-----> Push Gateway +------> Push Provider | |
- | | | | | | | | | |
- +-^-----------------+ | +----------------+ | | +----+----------+ |
- | | | | | |
- Matrix | | | | | |
- Client/Server API + | | | | |
- | | +--------------------+ +-------------------+
- | +--+-+ |
- | | <-------------------------------------------+
- +---+ |
- | | Provider Push Protocol
- +----+
-
- Mobile Device or Client
-
-
-Homeserver behaviour
---------------------
-
-This describes the format used by "HTTP" pushers to send notifications of
-events to Push Gateways. If the endpoint returns an HTTP error code, the
-homeserver SHOULD retry for a reasonable amount of time using exponential backoff.
-
-When pushing notifications for events, the homeserver is expected to include all of
-the event-related fields in the ``/notify`` request. When the homeserver is performing
-a push where the ``format`` is ``"event_id_only"``, only the ``event_id``, ``room_id``,
-``counts``, and ``devices`` are required to be populated.
-
-Note that most of the values and behaviour of this endpoint is described by the Client-Server
-API's `Push Module <../client_server/%CLIENT_RELEASE_LABEL%.html#module-push>`_.
-
-{{push_notifier_push_http_api}}
diff --git a/specification/rooms/v1.rst b/specification/rooms/v1.rst
deleted file mode 100644
index a71bdfb4592..00000000000
--- a/specification/rooms/v1.rst
+++ /dev/null
@@ -1,363 +0,0 @@
-.. Copyright 2017,2019 New Vector Ltd
-.. Copyright 2020 The Matrix.org Foundation C.I.C.
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Version 1
-==============
-
-This room version is the first ever version for rooms, and contains the building
-blocks for other room versions.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Client considerations
----------------------
-
-Clients may need to consider some algorithms performed by the server for their own
-implementation.
-
-Redactions
-~~~~~~~~~~
-
-Upon receipt of a redaction event, the server must strip off any keys not in
-the following list:
-
-- ``event_id``
-- ``type``
-- ``room_id``
-- ``sender``
-- ``state_key``
-- ``content``
-- ``hashes``
-- ``signatures``
-- ``depth``
-- ``prev_events``
-- ``prev_state``
-- ``auth_events``
-- ``origin``
-- ``origin_server_ts``
-- ``membership``
-
-.. Note:
- Some of the keys, such as ``hashes``, will appear on the federation-formatted
- event and therefore the client may not be aware of them.
-
-The content object must also be stripped of all keys, unless it is one of
-one of the following event types:
-
-- ``m.room.member`` allows key ``membership``.
-- ``m.room.create`` allows key ``creator``.
-- ``m.room.join_rules`` allows key ``join_rule``.
-- ``m.room.power_levels`` allows keys ``ban``, ``events``, ``events_default``,
- ``kick``, ``redact``, ``state_default``, ``users``, ``users_default``.
-- ``m.room.aliases`` allows key ``aliases``.
-- ``m.room.history_visibility`` allows key ``history_visibility``.
-
-Server implementation components
---------------------------------
-
-.. WARNING::
- The information contained in this section is strictly for server implementors.
- Applications which use the Client-Server API are generally unaffected by the
- intricacies contained here. The section above regarding client considerations
- is the resource that Client-Server API use cases should reference.
-
-
-The algorithms defined here should only apply to version 1 rooms. Other algorithms
-may be used by other room versions, and as such servers should be aware of which
-version room they are dealing with prior to executing a given algorithm.
-
-.. WARNING::
- Although room version 1 is the most popular room version, it is known to have
- undesirable effects. Servers implementing support for room version 1 should be
- aware that restrictions should be generally relaxed and that inconsistencies
- may occur until room version 2 (or later) is ready and adopted.
-
-State resolution
-~~~~~~~~~~~~~~~~
-
-.. WARNING::
- This section documents the state resolution algorithm as implemented by
- Synapse as of December 2017 (and therefore the de-facto Matrix protocol).
- However, this algorithm is known to have some problems.
-
-The room state :math:`S'(E)` after an event :math:`E` is defined in terms of
-the room state :math:`S(E)` before :math:`E`, and depends on whether
-:math:`E` is a state event or a message event:
-
-* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
-
-* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except
- that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key``
- is replaced by :math:`E`'s ``event_id``.
-
-The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of
-states :math:`\{ S'(E'), S'(E''), … \}` consisting of the states after each of
-:math:`E`'s ``prev_event``\s :math:`\{ E', E'', … \}`.
-
-The *resolution* of a set of states is defined as follows. The resolved state
-is built up in a number of passes; here we use :math:`R` to refer to the
-results of the resolution so far.
-
-* Start by setting :math:`R` to the union of the states to be resolved,
- excluding any *conflicting* events.
-
-* First we resolve conflicts between ``m.room.power_levels`` events. If there
- is no conflict, this step is skipped, otherwise:
-
- * Assemble all the ``m.room.power_levels`` events from the states to
- be resolved into a list.
-
- * Sort the list by ascending ``depth`` then descending ``sha1(event_id)``.
-
- * Add the first event in the list to :math:`R`.
-
- * For each subsequent event in the list, check that the event would be
- allowed by the authorization rules for a room in state :math:`R`. If the
- event would be allowed, then update :math:`R` with the event and continue
- with the next event in the list. If it would not be allowed, stop and
- continue below with ``m.room.join_rules`` events.
-
-* Repeat the above process for conflicts between ``m.room.join_rules`` events.
-
-* Repeat the above process for conflicts between ``m.room.member`` events.
-
-* No other events affect the authorization rules, so for all other conflicts,
- just pick the event with the highest depth and lowest ``sha1(event_id)`` that
- passes authentication in :math:`R` and add it to :math:`R`.
-
-A *conflict* occurs between states where those states have different
-``event_ids`` for the same ``(event_type, state_key)``. The events thus
-affected are said to be *conflicting* events.
-
-
-Authorization rules
-~~~~~~~~~~~~~~~~~~~
-
-The types of state events that affect authorization are:
-
-- ``m.room.create``
-- ``m.room.member``
-- ``m.room.join_rules``
-- ``m.room.power_levels``
-- ``m.room.third_party_invite``
-
-.. NOTE::
-
- Power levels are inferred from defaults when not explicitly supplied.
- For example, mentions of the ``sender``'s power level can also refer
- to the default power level for users in the room.
-
-The rules are as follows:
-
-1. If type is ``m.room.create``:
-
- a. If it has any previous events, reject.
- b. If the domain of the ``room_id`` does not match the domain of the
- ``sender``, reject.
- c. If ``content.room_version`` is present and is not a recognised version,
- reject.
- d. If ``content`` has no ``creator`` field, reject.
- e. Otherwise, allow.
-
-#. Reject if event has ``auth_events`` that:
-
- a. have duplicate entries for a given ``type`` and ``state_key`` pair
- #. have entries whose ``type`` and ``state_key`` don't match those
- specified by the `auth events selection`_ algorithm described in the
- server specification.
-
-#. If event does not have a ``m.room.create`` in its ``auth_events``, reject.
-
-#. If type is ``m.room.aliases``:
-
- a. If event has no ``state_key``, reject.
- b. If sender's domain doesn't matches ``state_key``, reject.
- c. Otherwise, allow.
-
-#. If type is ``m.room.member``:
-
- a. If no ``state_key`` key or ``membership`` key in ``content``, reject.
-
- #. If ``membership`` is ``join``:
-
- i. If the only previous event is an ``m.room.create``
- and the ``state_key`` is the creator, allow.
-
- #. If the ``sender`` does not match ``state_key``, reject.
-
- #. If the ``sender`` is banned, reject.
-
- #. If the ``join_rule`` is ``invite`` then allow if membership state
- is ``invite`` or ``join``.
-
- #. If the ``join_rule`` is ``public``, allow.
-
- #. Otherwise, reject.
-
- #. If ``membership`` is ``invite``:
-
- i. If ``content`` has ``third_party_invite`` key:
-
- #. If *target user* is banned, reject.
-
- #. If ``content.third_party_invite`` does not have a
- ``signed`` key, reject.
-
- #. If ``signed`` does not have ``mxid`` and ``token`` keys, reject.
-
- #. If ``mxid`` does not match ``state_key``, reject.
-
- #. If there is no ``m.room.third_party_invite`` event in the
- current room state with ``state_key`` matching ``token``, reject.
-
- #. If ``sender`` does not match ``sender`` of the
- ``m.room.third_party_invite``, reject.
-
- #. If any signature in ``signed`` matches any public key in the
- ``m.room.third_party_invite`` event, allow. The public keys are
- in ``content`` of ``m.room.third_party_invite`` as:
-
- #. A single public key in the ``public_key`` field.
- #. A list of public keys in the ``public_keys`` field.
-
- #. Otherwise, reject.
-
- #. If the ``sender``'s current membership state is not ``join``, reject.
-
- #. If *target user*'s current membership state is ``join`` or ``ban``,
- reject.
-
- #. If the ``sender``'s power level is greater than or equal to the *invite
- level*, allow.
-
- #. Otherwise, reject.
-
- #. If ``membership`` is ``leave``:
-
- i. If the ``sender`` matches ``state_key``, allow if and only if that user's
- current membership state is ``invite`` or ``join``.
-
- #. If the ``sender``'s current membership state is not ``join``, reject.
-
- #. If the *target user*'s current membership state is ``ban``, and the
- ``sender``'s power level is less than the *ban level*, reject.
-
- #. If the ``sender``'s power level is greater than or equal to the *kick
- level*, and the *target user*'s power level is less than the
- ``sender``'s power level, allow.
-
- #. Otherwise, reject.
-
- #. If ``membership`` is ``ban``:
-
- i. If the ``sender``'s current membership state is not ``join``, reject.
-
- #. If the ``sender``'s power level is greater than or equal to the *ban
- level*, and the *target user*'s power level is less than the
- ``sender``'s power level, allow.
-
- #. Otherwise, reject.
-
- #. Otherwise, the membership is unknown. Reject.
-
-#. If the ``sender``'s current membership state is not ``join``, reject.
-
-#. If type is ``m.room.third_party_invite``:
-
- a. Allow if and only if ``sender``'s current power level is greater than
- or equal to the *invite level*.
-
-#. If the event type's *required power level* is greater than the ``sender``'s power
- level, reject.
-
-#. If the event has a ``state_key`` that starts with an ``@`` and does not match
- the ``sender``, reject.
-
-#. If type is ``m.room.power_levels``:
-
- a. If ``users`` key in ``content`` is not a dictionary with keys that are
- valid user IDs with values that are integers (or a string that is an
- integer), reject.
-
- #. If there is no previous ``m.room.power_levels`` event in the room, allow.
-
- #. For the keys ``users_default``, ``events_default``,
- ``state_default``, ``ban``, ``redact``, ``kick``, ``invite`` check if they
- were added, changed or removed. For each found alteration:
-
- i. If the current value is higher than the ``sender``'s current power level,
- reject.
-
- #. If the new value is higher than the ``sender``'s current power level,
- reject.
-
- #. For each entry being added, changed or removed in both the ``events`` and
- ``users`` keys:
-
- i. If the current value is higher than the ``sender``'s current power level,
- reject.
-
- #. If the new value is higher than the ``sender``'s current power level,
- reject.
-
- #. For each entry being changed under the ``users`` key, other than the
- ``sender``'s own entry:
-
- i. If the current value is equal to the ``sender``'s current power level,
- reject.
-
- #. Otherwise, allow.
-
-#. If type is ``m.room.redaction``:
-
- a. If the ``sender``'s power level is greater than or equal to the *redact
- level*, allow.
-
- #. If the domain of the ``event_id`` of the event being redacted is the same
- as the domain of the ``event_id`` of the ``m.room.redaction``, allow.
-
- #. Otherwise, reject.
-
-#. Otherwise, allow.
-
-.. NOTE::
-
- Some consequences of these rules:
-
- * Unless you are a member of the room, the only permitted operations (apart
- from the initial create/join) are: joining a public room; accepting or
- rejecting an invitation to a room.
-
- * To unban somebody, you must have power level greater than or equal to both
- the kick *and* ban levels, *and* greater than the target user's power
- level.
-
-Event format
-~~~~~~~~~~~~
-
-Events in version 1 rooms have the following structure:
-
-{{definition_ss_pdu}}
-
-Canonical JSON
-~~~~~~~~~~~~~~
-
-Servers MUST NOT strictly enforce the JSON format specified in the
-`appendices <../appendices.html#canonical-json>`_ for the reasons described there.
-
-
-.. _`auth events selection`: ../server_server/%SERVER_RELEASE_LABEL%.html#auth-events-selection
-.. _`Signing Events`: ../server_server/%SERVER_RELEASE_LABEL%.html#signing-events
diff --git a/specification/rooms/v2.rst b/specification/rooms/v2.rst
deleted file mode 100644
index afc114f8f59..00000000000
--- a/specification/rooms/v2.rst
+++ /dev/null
@@ -1,204 +0,0 @@
-.. Copyright 2018-2019 New Vector Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Version 2
-==============
-
-This room version builds off of `version 1 `_ with an improved state
-resolution algorithm.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Server implementation components
---------------------------------
-
-.. WARNING::
- The information contained in this section is strictly for server implementors.
- Applications which use the Client-Server API are generally unaffected by the
- details contained here, and can safely ignore their presence.
-
-
-Room version 2 uses the base components of `room version 1 `_, changing
-only the state resolution algorithm.
-
-
-State resolution
-~~~~~~~~~~~~~~~~
-
-The room state :math:`S'(E)` after an event :math:`E` is defined in terms of
-the room state :math:`S(E)` before :math:`E`, and depends on whether
-:math:`E` is a state event or a message event:
-
-* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
-
-* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except
- that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key``
- is replaced by :math:`E`'s ``event_id``.
-
-The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of
-states :math:`\{ S'(E_1), S'(E_2), … \}` consisting of the states after each of
-:math:`E`'s ``prev_event``\s :math:`\{ E_1, E_2, … \}`, where the resolution of
-a set of states is given in the algorithm below.
-
-Definitions
-+++++++++++
-
-The state resolution algorithm for version 2 rooms uses the following
-definitions, given the set of room states :math:`\{ S_1, S_2, \ldots \}`:
-
-Power events
- A *power event* is a state event with type ``m.room.power_levels`` or
- ``m.room.join_rules``, or a state event with type ``m.room.member`` where the
- ``membership`` is ``leave`` or ``ban`` and the ``sender`` does not match the
- ``state_key``. The idea behind this is that power events are events that might
- remove someone's ability to do something in the room.
-
-Unconflicted state map and conflicted state set
- The *unconflicted state map* is the state where the value of each key exists
- and is the same in each state :math:`S_i`. The *conflicted state set* is the
- set of all other state events. Note that the unconflicted state map only has
- one event per ``(event_type, state_key)``, whereas the conflicted state set
- may have multiple events.
-
-Auth difference
- The *auth difference* is calculated by first calculating the full auth chain
- for each state :math:`S_i`, that is the union of the auth chains for each
- event in :math:`S_i`, and then taking every event that doesn't appear in
- every auth chain. If :math:`C_i` is the full auth chain of :math:`S_i`, then
- the auth difference is :math:`\cup C_i - \cap C_i`.
-
-Full conflicted set
- The *full conflicted set* is the union of the conflicted state set and the
- auth difference.
-
-Reverse topological power ordering
- The *reverse topological power ordering* of a set of events is the
- lexicographically smallest topological ordering based on the DAG formed by
- auth events. The reverse topological power ordering is ordered from earliest
- event to latest. For comparing two topological orderings to determine which
- is the lexicographically smallest, the following comparison relation on
- events is used: for events :math:`x` and :math:`y`, :math:`x`_ with an improved event format.
-
-.. note:
- All requirements listed in this room version specification are scoped to rooms
- which actually use this room version. For example, a requirement of "all APIs must
- accept the new event format" does in fact apply to all APIs, but only so much as
- where the contextual room of the request is using this room version. Rooms using
- other room versions should not be affected by these sweeping requirements.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-
-Client considerations
----------------------
-
-This room version changes the format for event IDs sent to clients. Clients should be
-aware that these event IDs may contain slashes and other potentially problematic
-characters. Clients should be treating event IDs as opaque identifiers and should not
-be attempting to parse them into a usable form, just like with other room versions.
-
-Clients should expect to see event IDs changed from the format of ``$randomstring:example.org``
-to something like ``$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk`` (note the lack of
-domain and the potentially problematic slash).
-
-
-Server implementation components
---------------------------------
-
-.. WARNING::
- The information contained in this section is strictly for server implementors.
- Applications which use the Client-Server API are generally unaffected by the
- intricacies contained here. The section above regarding client considerations
- is the resource that Client-Server API use cases should reference.
-
-
-Room version 3 uses the state resolution algorithm defined in `room version 2 `_,
-and the event format defined here.
-
-Event IDs
-~~~~~~~~~
-
-.. admonition:: Rationale
-
- In other room versions (namely version 1 and 2) the event ID is a distinct field
- from the remainder of the event, which must be tracked as such. This leads to
- complications where servers receive multiple events with the same ID in either the
- same or different rooms where the server cannot easily keep track of which event it
- should be using. By removing the use of a dedicated event ID, servers are required
- to track the hashes on an event to determine its ID.
-
-The event ID is the `reference hash`_ of the event encoded using `Unpadded Base64`_,
-prefixed with ``$``. A resulting event ID using this approach should look similar to
-``$CD66HAED5npg6074c6pDtLKalHjVfYb2q4Q3LZgrW6o``.
-
-Event IDs should not be sent over federation to servers when the room uses
-this room version. On the receiving end of an event, the server should compute
-the relevant event ID for itself.
-
-Additionally, the ``auth_events`` and ``prev_events`` have had a format change
-compared to other room versions to make it easier to handle. Instead of a tuple
-of values, they are now plain lists of events.
-
-{{definition_ss_pdu_v3}}
-
-Changes to APIs
-~~~~~~~~~~~~~~~
-
-Due to the event ID being removed from the event, some APIs need to change. All
-APIs which currently accept an event ID must do so with the new format. Servers
-must append the calculated event ID to all events sent to clients where an event
-ID would normally be expected.
-
-Because the format of events has changed, servers must be aware of the room version
-where the event resides so that the server may parse and handle the event. The
-federation API has taken this concern into consideration by ensuring that servers
-are aware of (or can find) the room version during a request.
-
-Authorization rules for events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The authorization rules for a given event have changed in this room version due
-to the change in event format:
-
-* The event no longer needs to be signed by the domain of the event ID (as there
- is no domain in the event ID), but still needs to be signed by the sender's
- domain.
-
-* In past room versions, redactions were only permitted to enter the DAG if the
- sender's domain matched the domain in the event ID being redacted, or the sender
- had appropriate permissions per the power levels. Due to servers now not being
- able to determine where an event came from during event authorization, redaction
- events are always accepted (provided the event is allowed by ``events`` and
- ``events_default`` in the power levels). However, servers should not apply or send
- redactions to clients until both the redaction event and original event have been
- seen, and are valid. Servers should only apply redactions to events where the
- sender's domains match, or the sender of the redaction has the appropriate
- permissions per the power levels.
-
-
-The remaining rules are the same as `room version 1 `_.
-
-
-.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
-.. _`Canonical JSON`: ../appendices.html#canonical-json
-.. _`Signing Events`: ../server_server/%SERVER_RELEASE_LABEL%.html#signing-events
-.. _`reference hash`: ../server_server/%SERVER_RELEASE_LABEL%.html#reference-hashes
diff --git a/specification/rooms/v4.rst b/specification/rooms/v4.rst
deleted file mode 100644
index bcd821cb80c..00000000000
--- a/specification/rooms/v4.rst
+++ /dev/null
@@ -1,76 +0,0 @@
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Version 4
-==============
-
-This room version builds on `version 3 `_ using a different encoding for
-event IDs.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-
-Client considerations
----------------------
-
-This room version changes the format form event IDs sent to clients. Clients should
-already be treating event IDs as opaque identifiers, and should not be concerned with
-the format of them. Clients should still encode the event ID when including it in a
-request path.
-
-Clients should expect to see event IDs changed from the format of ``$randomstring:example.org``
-to something like ``$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg`` (note the lack of domain).
-
-
-Server implementation components
---------------------------------
-
-.. WARNING::
- The information contained in this section is strictly for server implementors.
- Applications which use the Client-Server API are generally unaffected by the
- intricacies contained here. The section above regarding client considerations
- is the resource that Client-Server API use cases should reference.
-
-
-Room version 4 uses the same algorithms defined in `room version 3 `_, however
-using URL-safe base64 to generate the event ID.
-
-Event IDs
-~~~~~~~~~
-
-.. admonition:: Rationale
-
- Room version 3 generated event IDs that were difficult for client implementations
- which were not encoding the event ID to function in those rooms. It additionally
- raised concern due to the ``/`` character being interpretted differently by some
- reverse proxy software, and generally made administration harder.
-
-The event ID is the `reference hash`_ of the event encoded using a variation of
-`Unpadded Base64`_ which replaces the 62nd and 63rd characters with ``-`` and ``_``
-instead of using ``+`` and ``/``. This matches `RFC4648's definition of URL-safe base64
-`_. Event IDs are still prefixed
-with ``$`` and may result in looking like ``$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg``.
-
-Just like in room version 3, event IDs should not be sent over federation to servers
-when the room uses this room version. On the receiving end of an event, the server
-should compute the relevant event ID for itself. Room version 3 also changes the format
-of ``auth_events`` and ``prev_events`` in a PDU.
-
-{{definition_ss_pdu_v4}}
-
-.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
-.. _`Canonical JSON`: ../appendices.html#canonical-json
-.. _`Signing Events`: ../server_server/%SERVER_RELEASE_LABEL%.html#signing-events
-.. _`reference hash`: ../server_server/%SERVER_RELEASE_LABEL%.html#reference-hashes
diff --git a/specification/rooms/v5.rst b/specification/rooms/v5.rst
deleted file mode 100644
index 6d34ec935ac..00000000000
--- a/specification/rooms/v5.rst
+++ /dev/null
@@ -1,59 +0,0 @@
-.. Copyright 2019 The Matrix.org Foundation C.I.C.
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Version 5
-==============
-
-This room version builds on `version 4 `_ while enforcing signing
-key validity periods for events.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-
-Client considerations
----------------------
-
-There are no specific requirements for clients in this room version. Clients should
-be aware of event ID changes in `room version 4 `_, however.
-
-
-Server implementation components
---------------------------------
-
-.. WARNING::
- The information contained in this section is strictly for server implementors.
- Applications which use the Client-Server API are generally unaffected by the
- intricacies contained here. The section above regarding client considerations
- is the resource that Client-Server API use cases should reference.
-
-
-Room version 5 uses the same algorithms defined in `room version 4 `_, ensuring
-that signing key validity is respected.
-
-Signing key validity period
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When validating event signatures, servers MUST enforce the ``valid_until_ts`` property
-from a key request is at least as large as the ``origin_server_ts`` for the event being
-validated. Servers missing a copy of the signing key MUST try to obtain one via the
-`GET /_matrix/key/v2/server <../server_server/%SERVER_RELEASE_LABEL%.html#get-matrix-key-v2-server-keyid>`_
-or `POST /_matrix/key/v2/query <../server_server/%SERVER_RELEASE_LABEL%.html#post-matrix-key-v2-query>`_
-APIs. When using the ``/query`` endpoint, servers MUST set the ``minimum_valid_until_ts``
-property to prompt the notary server to attempt to refresh the key if appropriate.
-
-Servers MUST use the lesser of ``valid_until_ts`` and 7 days into the future when
-determining if a key is valid. This is to avoid a situation where an attacker
-publishes a key which is valid for a significant amount of time without a way for
-the homeserver owner to revoke it.
diff --git a/specification/rooms/v6.rst b/specification/rooms/v6.rst
deleted file mode 100644
index e5378d0ebf7..00000000000
--- a/specification/rooms/v6.rst
+++ /dev/null
@@ -1,100 +0,0 @@
-.. Copyright 2020 The Matrix.org Foundation C.I.C.
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. You may obtain a copy of the License at
-..
-.. http://www.apache.org/licenses/LICENSE-2.0
-..
-.. Unless required by applicable law or agreed to in writing, software
-.. distributed under the License is distributed on an "AS IS" BASIS,
-.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-.. See the License for the specific language governing permissions and
-.. limitations under the License.
-
-Room Version 6
-==============
-
-This room version builds on `version 5 `_ while changing various
-authorization rules performed on events.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-
-Client considerations
----------------------
-
-The redaction algorithm has changed from `room version 1 `_ to remove
-all rules against events of type ``m.room.aliases``. Room versions 2, 3, 4, and
-5 all use v1's redaction algorithm. The algorithm is otherwise unchanged.
-
-
-Server implementation components
---------------------------------
-
-.. WARNING::
- The information contained in this section is strictly for server implementors.
- Applications which use the Client-Server API are generally unaffected by the
- intricacies contained here. The section above regarding client considerations
- is the resource that Client-Server API use cases should reference.
-
-
-Room version 6 makes the following alterations to algorithms described in `room version 5 `_.
-
-Redactions
-~~~~~~~~~~
-
-As mentioned in the client considerations portion of this specification, all
-special meaning has been removed for events of type ``m.room.aliases``. The
-algorithm is otherwise unchanged.
-
-Authorization rules for events
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Like redactions, all rules relating specifically to events of type ``m.room.aliases``
-are removed. They must still pass authorization checks relating to state events.
-
-Additionally, the authorization rules for events of type ``m.room.power_levels``
-now include the content key ``notifications``. This new rule takes the place of the
-rule which checks the ``events`` and ``users`` keys.
-
-For completeness, the changes to the auth rules can be represented as follows:
-
-.. code:: diff
-
- ...
-
- -If type is `m.room.aliases`:
- -
- - a. If event has no `state_key`, reject.
- - b. If sender's domain doesn't matches `state_key`, reject.
- - c. Otherwise, allow.
-
- ...
-
- If type is `m.room.power_levels`:
-
- ...
-
- - * For each entry being added, changed or removed in both the `events` and `users` keys:
- + * For each entry being added, changed or removed in the `events`, `users`, and `notifications` keys:
-
- i. If the current value is higher than the `sender`'s current power level, reject.
-
- ii. If the new value is higher than the `sender`'s current power level, reject.
-
- ...
-
-
-The remaining rules are the same as in `room version 3 `_
-(the last inherited room version to specify the authorization rules).
-
-Canonical JSON
-~~~~~~~~~~~~~~
-
-Servers MUST strictly enforce the JSON format specified in the
-`appendices <../appendices.html#canonical-json>`_. This translates to a 400 ``M_BAD_JSON`` error
-on most endpoints, or discarding of events over federation. For example, the Federation API's
-``/send`` endpoint would discard the event whereas the Client Server API's ``/send/{eventType}``
-endpoint would return a ``M_BAD_JSON`` error.
diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst
deleted file mode 100644
index 9deb267cb80..00000000000
--- a/specification/server_server_api.rst
+++ /dev/null
@@ -1,1267 +0,0 @@
-.. Copyright 2016 OpenMarket Ltd
-.. Copyright 2017-2019 New Vector Ltd
-..
-.. Licensed under the Apache License, Version 2.0 (the "License");
-.. you may not use this file except in compliance with the License.
-.. 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.
-
-Federation API
-==============
-
-{{unstable_warning_block_SERVER_RELEASE_LABEL}}
-
-Matrix homeservers use the Federation APIs (also known as server-server APIs)
-to communicate with each other. Homeservers use these APIs to push messages to
-each other in real-time, to retrieve historic messages from each other, and to
-query profile and presence information about users on each other's servers.
-
-The APIs are implemented using HTTPS requests between each of the servers.
-These HTTPS requests are strongly authenticated using public key signatures
-at the TLS transport layer and using public key signatures in HTTP
-Authorization headers at the HTTP layer.
-
-There are three main kinds of communication that occur between homeservers:
-
-Persisted Data Units (PDUs):
- These events are broadcast from one homeserver to any others that have
- joined the same room (identified by Room ID). They are persisted in
- long-term storage and record the history of messages and state for a
- room.
-
- Like email, it is the responsibility of the originating server of a PDU
- to deliver that event to its recipient servers. However PDUs are signed
- using the originating server's private key so that it is possible to
- deliver them through third-party servers.
-
-Ephemeral Data Units (EDUs):
- These events are pushed between pairs of homeservers. They are not
- persisted and are not part of the history of a room, nor does the
- receiving homeserver have to reply to them.
-
-Queries:
- These are single request/response interactions between a given pair of
- servers, initiated by one side sending an HTTPS GET request to obtain some
- information, and responded by the other. They are not persisted and contain
- no long-term significant history. They simply request a snapshot state at
- the instant the query is made.
-
-
-EDUs and PDUs are further wrapped in an envelope called a Transaction, which is
-transferred from the origin to the destination homeserver using an HTTPS PUT
-request.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-Changelog
----------
-
-.. topic:: Version: %SERVER_RELEASE_LABEL%
-{{server_server_changelog}}
-
-This version of the specification is generated from
-`matrix-doc `_ as of Git commit
-`{{git_version}} `_.
-
-For the full historical changelog, see
-https://github.com/matrix-org/matrix-doc/blob/master/changelogs/server_server.rst
-
-Other versions of this specification
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The following other versions are also available, in reverse chronological order:
-
-- `HEAD `_: Includes all changes since the latest versioned release.
-- `r0.1.4