diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml
index 6e5951207f9..30820495c0f 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"
+ branches: "main"
+
+ - 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/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000000..4ecefbec41b
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,67 @@
+name: "Spec"
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ build-openapi:
+ name: "🐍 Build OpenAPI definitions for matrix.org"
+ runs-on: ubuntu-latest
+ container: "python:3.9"
+ steps:
+ - name: "📥 Source checkout"
+ uses: actions/checkout@v2
+ - name: "📦 Asset creation"
+ run: |
+ python3 -m venv env && . env/bin/activate
+ pip install -r scripts/requirements.txt
+ scripts/generate-matrix-org-assets
+ - name: "📤 Artifact upload"
+ uses: actions/upload-artifact@v2
+ with:
+ name: openapi-artifact
+ path: assets.tar.gz
+
+ build-spec:
+ name: "📖 Build the spec"
+ runs-on: ubuntu-latest
+ steps:
+ - name: "➕ Setup Node"
+ uses: actions/setup-node@v2
+ with:
+ node-version: '14'
+ - name: "➕ Setup Hugo"
+ uses: peaceiris/actions-hugo@c03b5dbed22245418539b65eb9a3b1d5fdd9a0a6
+ with:
+ hugo-version: '0.85.0'
+ extended: true
+ - name: "📥 Source checkout"
+ uses: actions/checkout@v2
+ with:
+ submodules: 'recursive'
+ - name: "⚙️ npm"
+ run: |
+ npm i
+ npm run get-proposals
+ - name: "⚙️ hugo"
+ run: hugo --baseURL "/unstable" -d "spec"
+ - name: "📦 Tarball creation"
+ run: tar -czf spec.tar.gz spec
+ - name: "📤 Artifact upload"
+ uses: actions/upload-artifact@v2
+ with:
+ name: spec-artifact
+ path: spec.tar.gz
+
+ rebuild-matrixdotorg:
+ name: "👷 Rebuild matrix.org"
+ needs: build-openapi
+ runs-on: ubuntu-latest
+ if: ${{ false && github.event_name == 'push' && github.ref == 'refs/head/main' }}
+ steps:
+ - name: "🪄 Triggering rebuild of matrix.org"
+ run: |
+ curl -XPOST -u "${{secrets.TRIGGER_MATRIXORG_REBUILD_TOKEN}}" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/${{ github.repository_owner }}/matrix.org/actions/workflows/build-matrix.org.yml/dispatches --data '{"ref": "${{ github.ref }}" }'
diff --git a/.gitignore b/.gitignore
index 9cc27b85582..cce5848fa87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,21 @@
/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/
\ No newline at end of file
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/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:
![](images/1756-graph2.dot.png)
@@ -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/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,
![Spoiler rendering idea](images/2010-spoiler-example.gif)
+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/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/2582-remove-mimetype-from-encrypted-file.md b/proposals/2582-remove-mimetype-from-encrypted-file.md
new file mode 100644
index 00000000000..3b1f83c7a06
--- /dev/null
+++ b/proposals/2582-remove-mimetype-from-encrypted-file.md
@@ -0,0 +1,12 @@
+# Remove `mimetype` from `EncryptedFile` object
+
+
+## Proposal
+The example in the spec currently lists `mimetype` in the [examples for `EncryptedFile`](https://matrix.org/docs/spec/client_server/r0.6.1#extensions-to-m-message-msgtypes) but not in
+the object definition. As that is duplicate information of the `info` block of file events, the
+mimetype should just be removed alltogether.
+
+
+## Potential issues
+Some clients might depend on this. However, as of August 2021, all known clients have
+been confirmed to not use this.
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/2778-appservice-login.md b/proposals/2778-appservice-login.md
new file mode 100644
index 00000000000..fd509ab8941
--- /dev/null
+++ b/proposals/2778-appservice-login.md
@@ -0,0 +1,135 @@
+# MSC2778: Providing authentication method for appservice users
+
+Appservices within Matrix are increasingly attempting to support End-to-End Encryption. As such, they
+need a way to generate devices for their users so that they can participate in E2E rooms. In order to
+do so, this proposal suggests implementing an appservice extension to the
+[`POST /login` endpoint](https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login).
+
+Appservice users do not usually need to log in as they do not need their own access token, and do not
+traditionally need a "device". However, E2E encryption demands that at least one user in a room has a
+Matrix device which means bridge users need to be able to generate a device on demand. In the past,
+bridge developers have used the bridge bot's device for all bridge users in the room, but this causes
+problems should the bridge wish to only join ghosts to a room (e.g. for DMs).
+
+Another advantage this provides is that an appservice can now be used to generate access tokens for
+any user in its namespace without having to set a password for that user, which may be useful where
+maintaining password(s) in the configuration is undesirable.
+
+## Proposal
+
+A new `type` is to be added to `POST /login`: `m.login.application_service`
+
+The `/login` endpoint may now take an `access_token` in the same way that other
+authenticated endpoints do. No additional parameters should be specified in the request body.
+
+Example request
+
+```json
+{
+ "type": "m.login.application_service",
+ "identifier": {
+ "type": "m.id.user",
+ "user": "_bridge_alice"
+ }
+}
+```
+
+Note: Implementations MUST use the `identifier.type`=`m.id.user` method of specifying the
+localpart. The deprecated top-level `user` field **cannot** use this login flow type. This
+is deliberate so as to coax developers into using the new identifier format when implementing
+new flows.
+
+The response body should be unchanged from the existing `/login` specification.
+
+If one of the following conditions are true:
+
+- The access token is not provided
+- The access token does not correspond to an appservice
+- Or the user has not previously been registered
+
+Then the servers MUST reject with HTTP 403, with an `errcode` of `"M_FORBIDDEN"`.
+
+If the access token DOES correspond to an appservice but the user is not inside its namespace,
+then the `errcode` must be `"M_EXCLUSIVE"`.
+
+Homeservers should ignore the `access_token` parameter if a type other than
+`m.login.application_service` has been provided.
+
+Appservices creating **new** users can still use the `/register` endpoint to generate an `access_token` / `device_id`
+but for existing users, the `/login` endpoint can be used instead.
+
+## Potential issues
+
+This proposal means that there will be more calls to make when setting up a appservice user, when
+using encryption. While this could be done during the registration step, this would prohibit creating
+new devices should the appservice intentionally or inadvertently have lost the client-side device data.
+
+## Alternatives
+
+### 1. Include the token in the `/login` request body
+
+One minor tweak to the current proposal could be to include the token as part of the auth data, rather than
+being part of the header/params to the request. An argument could be made for either, but since the specification
+expects the appservice to pass the token this way in all requests, including `/register`, it seems wise to keep
+it that way.
+
+### 2. Use implementation specific "shared secret" authentication
+
+Some community members have used homeserver implementation details such as a "shared secret" authentication method to
+log into the accounts without having to use the /login process at all. Synapse provides such a function,
+but also means the appservice can now authenticate as any user on the homeserver. This is undesirable from a
+security standpoint.
+
+### 3. Keep using `/register` solely
+
+A third option could be to create a new endpoint that simply creates a new device for an appservice user on demand.
+Given the rest of the matrix eco-system does this with /login, and /login is already extensible with `type`, it would
+create more work for all parties involved for little benefit.
+
+Finally, `POST /register` does already return a `device_id` and `access_token` so appservices
+could store this information rather than calling `POST /login` at all. This does however present a few problems:
+
+- Quite a few appservices which only support unencrypted messaging do not use/store the `device_id`/`access_token` from a register call.
+ In the event that an appservice eventually gains the ability to support encryption, they would be unable to fetch a new `device_id`/
+ `access_token` for any existing users (as `/register` would fail for an existing user).
+- If user tokens were lost or exposed, there is no way to programattically create new access tokens for these users.
+- Finally, if a user was registered externally and the appservice would like to masquerade as it, it would be unable to fetch
+ an access token for that user.
+
+While `POST /register` does work, it is impactical as the sole method of fetching an access token.
+
+## Security considerations
+
+Appservices could use this new functionality to generate devices for any userId that are within its namespace e.g. setting the
+user namespace regex to `@.*:example.com` would allow appservice to control anyone on the homeserver. While this sounds scary, in practice
+this is not a problem because:
+
+- Appservice namespaces are maintained by the homeserver admin. If the namespace were to change, then it's reasonable
+ to assume that the server admin is aware. There is no defense mechanism to stop a malicious server admin from creating new
+ devices for a given user's account as they could also do so by simply modifying the database.
+
+- While an appservice *could* try to masquerade as a user maliciously without the server admin expecting it, it would still
+ be bound by the restrictions of the namespace. Server admins are expected to be aware of the implications of adding new
+ appservices to their server so the burden of responsibility lies with the server admin.
+
+- Appservices already can /sync as any user using the `as_token` and send any messages as any user in the namespace, the only
+ difference is that without a dedicated access token they are unable to receive device messages. While in theory this
+ does make them unable to see encrypted messages, this is not designed to be a security mechanism.
+
+In conclusion this MSC only automates the creation of new devices for users inside an AS namespace, which is something
+a server admin could already do. Appservices should always be treated with care and so with these facts in mind the MSC should
+be considered secure.
+
+## Unstable prefix
+
+Implementations should use `uk.half-shot.msc2778.login.application_service` for `type` given in the
+`POST /login` until this lands in a released version of the specification.
+
+## Implementations
+
+The proposal has been implemented by a homeserver, a bridge SDK and two bridges:
+
+- [synapse](https://github.com/matrix-org/synapse/pull/8320)
+- [mautrix-python](https://github.com/tulir/mautrix-python/commit/12d7c48ca7c15fd3ff61608369af1cf69e289aeb)
+- [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp/commit/ead8a869c84d07fadc7cfcf3d522452c99faaa36)
+- [matrix-appservice-bridge](https://github.com/matrix-org/matrix-appservice-bridge/pull/231/files#diff-5e93f1b51d50a44fcf0ca46ea1793c1cR851-R864)
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:
+
+
+
+![Sequence diagram](https://user-images.githubusercontent.com/2403652/104897523-61fb4b00-5970-11eb-88f7-9fc0956b33a2.png)
+
+### 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:
+
+![login buttons](images/2858-login.png)
+
+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/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/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/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/3375-room-v9.md b/proposals/3375-room-v9.md
new file mode 100644
index 00000000000..0cd1e219a7c
--- /dev/null
+++ b/proposals/3375-room-v9.md
@@ -0,0 +1,26 @@
+# MSC3375: Room Version 9
+
+A new room version, `9`, is proposed using [room version 8](https://spec.matrix.org/unstable/rooms/v8/)
+as a base and incorporating the following:
+
+The redaction rules for room version 9 are updated, such that `m.room.member`
+events allow the `join_authorised_via_users_server` key in addition to the
+`membership` key.
+
+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 `9` upon being added to the specification shall be considered stable.
+No other room versions are affected by this MSC.
+
+## Background
+
+Protecting this key from redaction was missing in [MSC3083](https://github.com/matrix-org/matrix-doc/blob/main/proposals/3083-restricted-rooms.md).
+By redacting it, new servers which join a room are unable to properly authorise
+`m.room.member` events which include the `join_authorised_via_users_server` key
+and have been redacted. This can cause a split-brained room where some servers
+believe a member is joined and other servers do not see the member in the room.
+
+Note that a new room version is necessary since this will change that event ID
+calculation of the `m.room.member` event (and thus is not backwards compatible).
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/dump-swagger.py b/scripts/dump-swagger.py
index 232ca8adcce..4b4922fff39 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)
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..89b7517922d
--- /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 a1f48910ba7..00000000000
--- a/specification/client_server_api.rst
+++ /dev/null
@@ -1,2083 +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}}
-
-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 4c256fc3722..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 6 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 be66897b9f2..00000000000
--- a/specification/modules/end_to_end_encryption.rst
+++ /dev/null
@@ -1,1392 +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.room.message api spec
-.. |encrypted_files| replace:: End-to-end encryption
-.. _encrypted_files:
-
-Extensions to ``m.room.message`` msgtypes
-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
-
-This module adds ``file`` and ``thumbnail_file`` properties, of type
-``EncryptedFile``, to ``m.room.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``.
-
-If a device does not wish to share keys with that device, it can indicate this
-by sending an `m.room_key.withheld`_ to-device message, as described in
-`Reporting that decryption keys are withheld`_.
-
-.. 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
- }
- }
-
-Reporting that decryption keys are withheld
--------------------------------------------
-
-When sending an encrypted event to a room, a client can optionally signal to
-other devices in that room that it is not sending them the keys needed to
-decrypt the event. In this way, the receiving client can indicate to the user
-why it cannot decrypt the event, rather than just showing a generic error
-message.
-
-In the same way, when one device requests keys from another using `Key
-requests`_, the device from which the key is being requested may want to tell
-the requester that it is purposely not sharing the key.
-
-If Alice withholds a megolm session from Bob for some messages in a room, and
-then later on decides to allow Bob to decrypt later messages, she can send Bob
-the megolm session, ratcheted up to the point at which she allows Bob to
-decrypt the messages. If Bob logs into a new device and uses key sharing to
-obtain the decryption keys, the new device will be sent the megolm sessions
-that have been ratcheted up. Bob's old device can include the reason that the
-session was initially not shared by including a ``withheld`` property in the
-``m.forwarded_room_key`` message that is an object with the ``code`` and
-``reason`` properties from the ``m.room_key.withheld`` message.
-
-{{m_room_key_withheld_event}}
-
-
-.. 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 fde38a2a11d..00000000000
--- a/specification/modules/guest_access.rst
+++ /dev/null
@@ -1,103 +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/members <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-members>`_
-* `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 704277a1b1a..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 `MSC1767 `_.
-
-{{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 a7d5383df6f..00000000000
--- a/specification/rooms/v1.rst
+++ /dev/null
@@ -1,368 +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 there are many rooms using room version 1, 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.
-
-State resolution
-~~~~~~~~~~~~~~~~
-
-.. WARNING::
- Room version 1 is known to have bugs that can cause the state of rooms to reset
- to older versions of the room's state. For example this could mean that users
- who had joined the room may be removed from the room, admins and moderators
- could lose their power level, and users who have been banned from the room may
- be able to rejoin. Other state events such as the the room's name or topic could
- also reset to a previous version.
-
- This is fixed in the state resolution algorithm introduced in room version 2.
-
-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 `_
-- `r0.1.3