diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 00000000..659380b0
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,124 @@
+gendoc: &gendoc
+ name: Generate the docs
+ command: |
+ source /env/bin/activate
+ scripts/gendoc.py
+
+genswagger: &genswagger
+ name: Generate the swagger
+ command: |
+ source /env/bin/activate
+ scripts/dump-swagger.py
+
+buildswaggerui: &buildswaggerui
+ name: Build Swagger UI
+ command: |
+ ls scripts/
+ mkdir -p api/client-server
+ git clone https://github.com/matrix-org/swagger-ui swagger-ui
+ cp -r swagger-ui/dist/* api/client-server/
+ mkdir -p api/client-server/json
+ cp scripts/swagger/api-docs.json api/client-server/json/
+ wget https://raw.githubusercontent.com/matrix-org/matrix.org/master/content/swagger.css -O api/client-server/swagger.css
+ wget https://raw.githubusercontent.com/matrix-org/matrix.org/master/scripts/swagger-ui.patch
+ patch api/client-server/index.html swagger-ui.patch
+
+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
+
+validateapi: &validateapi
+ name: Validate OpenAPI specifications
+ command: |
+ cd api
+ npm install
+ node validator.js -s "client-server"
+
+buildspeculator: &buildspeculator
+ name: Build Speculator
+ command: |
+ cd scripts/speculator
+ go build -v
+
+buildcontinuserv: &buildcontinuserv
+ name: Build Continuserv
+ command: |
+ cd scripts/continuserv
+ go build -v
+
+version: 2
+jobs:
+ validate-docs:
+ docker:
+ - image: node:alpine
+ steps:
+ - checkout
+ - run: *validateapi
+ check-docs:
+ docker:
+ - image: uhoreg/matrix-doc-build
+ 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
+ steps:
+ - checkout
+ - run: *gendoc
+ - store_artifacts:
+ path: scripts/gen
+ - 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
+ build-swagger:
+ docker:
+ - image: uhoreg/matrix-doc-build
+ steps:
+ - checkout
+ - run: *genswagger
+ - run: *buildswaggerui
+ - store_artifacts:
+ path: api/client-server/
+ - run:
+ name: "Swagger UI is available at:"
+ command: DOCS_URL="${CIRCLE_BUILD_URL}/artifacts/${CIRCLE_NODE_INDEX}/${CIRCLE_WORKING_DIRECTORY/#\~/$HOME}/api/client-server/index.html"; echo $DOCS_URL
+ build-dev-scripts:
+ docker:
+ - image: golang:1.8
+ steps:
+ - checkout
+ - run:
+ 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:
+ version: 2
+
+ build-spec:
+ jobs:
+ - build-docs
+ - build-swagger
+ - check-docs
+ - validate-docs
+ - build-dev-scripts
+
+notify:
+ webhooks:
+ - url: https://giles.cadair.com/circleci
diff --git a/.gitignore b/.gitignore
index 71648500..a850d2fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,13 @@
-scripts/gen
-scripts/continuserv/continuserv
-scripts/speculator/speculator
+/api/node_modules
+/assets
+/assets.tar.gz
+/env*
+/scripts/gen
+/scripts/continuserv/continuserv
+/scripts/speculator/speculator
/scripts/swagger
-templating/out
+/scripts/tmp
+/templating/out
*.pyc
*.swp
-supporting-docs/_site
-supporting-docs/.sass-cache
-api/node_modules
+_rendered.rst
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index f18263d0..c592cf02 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1,55 +1,35 @@
Contributing to matrix-doc
==========================
-Everyone is welcome to contribute to the ``matrix-doc`` project, provided that they
-are willing to license their contributions under the same license as the
-project itself. We follow a simple 'inbound=outbound' model for contributions:
-the act of submitting an 'inbound' contribution means that the contributor
-agrees to license the code under the same terms as the project's overall
-'outbound' license - in our case, this is Apache Software License
-v2 (see LICENSE).
+Everyone is welcome to contribute to the Matrix specification!
+
+Please ensure that you sign off your contributions. See `Sign off`_ below.
+
+Code style
+----------
+
+The documentation style is described at
+https://github.com/matrix-org/matrix-doc/blob/master/meta/documentation_style.rst.
+
+Python code within the ``matrix-doc`` project should follow the same style as
+synapse, which is documented at
+https://github.com/matrix-org/synapse/tree/master/docs/code_style.rst.
+
+Matrix-doc workflows
+--------------------
Specification changes
~~~~~~~~~~~~~~~~~~~~~
-The Matrix specification documents the APIs which Matrix clients can use. For
-this to be effective, the APIs need to be present and working correctly in a
-server before they can be documented in the specification. This process can
-take some time to complete.
+The Matrix specification documents the APIs which Matrix clients and servers use.
+For this to be effective, the APIs need to be present and working correctly in a
+server before they can be documented in the specification. This process can take
+some time to complete.
For this reason, we have not found the github pull-request model effective for
-discussing changes to the specification. Instead, we have adopted the following
-workflow:
-
-1. Create a discussion document outlining the proposed change. The document
- should include details such as the HTTP endpoint being changed (or the
- suggested URL for a new endpoint), any new or changed parameters and response
- fields, and generally as much detail about edge-cases and error handling as
- is practical at this stage.
-
- The Matrix Core Team's preferred tool for such discussion documents is
- `Google Docs `_ thanks to its support for comment
- threads. Works in progress are kept in a folder at
- https://drive.google.com/drive/folders/0B4wHq8qP86r2ck15MHEwMmlNVUk.
-
-2. Seek feedback on the proposal. `#matrix-dev:matrix.org
- `_ is a good place to reach the
- core team and others who may be interested in your proposal.
-
-3. Implement the changes in servers and clients. Refer to the CONTRIBUTING files
- of the relevant projects for details of how best to do this.
-
- In general we will be unable to publish specification updates until the
- reference server implements them, and they have been proven by a working
- client implementation.
-
-4. Iterate steps 1-3 as necessary.
-
-5. Write the specification for the change, and create a `pull request`_ for
- it. It may be that much of the text of the change can be taken directly from
- the discussion document, though typically some effort will be needed to
- change to the ReST syntax and to ensure that the text is as clear as
- possible.
+discussing changes to the specification. Instead, we have adopted the workflow
+as described at https://matrix.org/docs/spec/proposals - *please read this for
+details on how to contribute spec changes*.
Other changes
@@ -59,43 +39,81 @@ The above process is unnecessary for smaller changes, and those which do not
put new requirements on servers. This category of changes includes the
following:
-* changes to supporting documentation
+* Changes to the scripts used to generate the specification.
-* changes to the scripts used to generate the specification
+* Addition of features which have been in use in practice for some time, but
+ have never made it into the spec (including anything with the `spec-omission
+ `_ label).
-* clarifications to the specification which do not change the behaviour of
+* Likewise, corrections to the specification, to fix situations where, in
+ practice, servers and clients behave differently to the specification,
+ including anything with the `spec-bug
+ `_ label.
+
+ (If there is any doubt about whether it is the spec or the implementations
+ that need fixing, please discuss it with us first in `#matrix-dev:matrix.org
+ `_.)
+
+* Clarifications to the specification which do not change the behaviour of
Matrix servers or clients in a way which might introduce compatibility
- problems for existing deployments. For example, recommendations for UI
- behaviour do not require a proposal document. On the other hand, changes to
- event contents would be best discussed in a proposal document even though no
- changes would be necessary to server implementations.
+ problems for existing deployments. This includes anything with the
+ `clarification `_
+ label.
+
+ For example, recommendations for UI behaviour do not require a proposal
+ document. On the other hand, changes to event contents would be best
+ discussed in a proposal document even though no changes would be necessary to
+ server implementations.
For such changes, please do just open a `pull request`_.
+.. _pull request: https://help.github.com/articles/about-pull-requests
-Pull requests
-~~~~~~~~~~~~~
-.. _pull request: `Pull requests`_
-The preferred and easiest way to contribute changes to the ``matrix-doc`` project
-is to fork it on github, and then create a pull request to ask us to pull your
-changes into our repo (https://help.github.com/articles/using-pull-requests/).
+Adding to the changelog
+~~~~~~~~~~~~~~~~~~~~~~~
-(Note that, unlike most of the other matrix.org projects, pull requests for
-matrix-doc should be based on the ``master`` branch.)
+Currently only changes to the client-server API need to end up in a changelog. The
+other APIs are not yet stable and therefore do not have a changelog. Adding to the
+changelog can only be done after you've opened your pull request, so be sure to do
+that first.
-Code style
-~~~~~~~~~~
+The changelog is managed by Towncrier (https://github.com/hawkowl/towncrier) in the
+form of "news fragments". The news fragments for the client-server API are stored
+under ``changelogs/client_server/newsfragments``.
-The documentation style is described at
-https://github.com/matrix-org/matrix-doc/blob/master/meta/documentation_style.rst.
+To create a changelog entry, create a file named in the format ``prNumber.type`` in
+the ``newsfragments`` directory. The ``type`` can be one of the following:
-Python code within the ``matrix-doc`` project should follow the same style as
-synapse, which is documented at
-https://github.com/matrix-org/synapse/tree/master/docs/code_style.rst.
+* ``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``
+
+* ``feature`` - Used when adding backwards-compatible changes to the API.
+
+* ``clarification`` - Used when an area of the spec is being improved upon and does
+ not change or introduce any functionality.
+
+* ``breaking`` - Used when the change is not backwards compatible.
+
+* ``deprecation`` - Used when deprecating something
+
+All news fragments must have a brief summary explaining the change in the contents
+of the file.
+
+Changes that do not change the spec, such as changes to the build script, formatting,
+CSS, etc should not get a news fragment.
Sign off
-~~~~~~~~
+--------
+
+We ask that everybody who contributes to their project signs off their
+contributions, as explained below.
+
+We follow a simple 'inbound=outbound' model for contributions: the act of
+submitting an 'inbound' contribution means that the contributor agrees to
+license their contribution under the same terms as the project's overall 'outbound'
+license - in our case, this is Apache Software License v2 (see LICENSE).
In order to have a concrete record that your contribution is intentional
and you agree to license it under the same terms as the project's license, we've adopted the
diff --git a/README.rst b/README.rst
index 76e3c1af..b8847bfb 100644
--- a/README.rst
+++ b/README.rst
@@ -1,39 +1,141 @@
-This repository contains the documentation for Matrix.
+This repository contains the Matrix specification.
-Structure
-=========
+If you want to ask more about the specification, join us on
+`#matrix-dev:matrix.org `_.
-- ``api`` : Contains the HTTP API specification.
-- ``attic``: Contains historical sections of specification for reference
+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``: Contains change logs for the various parts of the
+- ``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``: Contains the `JSON Schema`_ for all Matrix events
+- ``event-schemas``: the `JSON Schema`_ for all Matrix events
contained in the specification, along with example JSON files.
-- ``meta``: Contains documents outlining the processes involved when writing
+- ``meta``: documents outlining the processes involved when writing
documents, e.g. documentation style, guidelines.
-- ``scripts``: Contains scripts to generate formatted versions of the
+- ``scripts``: scripts to generate formatted versions of the
documentation, typically HTML.
-- ``specification``: Contains the specification split up into sections.
-- ``supporting-docs``: Contains additional documents which explain design
- decisions, examples, use cases, etc.
-- ``templating``: Contains the templates and templating system used to
- generate the spec.
-
-Contributing
-============
-
-Known issues with the specification are represented as JIRA issues at
-``_.
-
-If you want to ask more about the specification, join us on
-`#matrix-dev:matrix.org `_.
-
-If you would like to contribute to the specification or supporting
-documentation, see ``_.
+- ``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 Synders 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
+~~~~~~~~~~~~~
+
+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 shell 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/labels.rst `_ for notes on what the labels mean.
diff --git a/api/README b/api/README
index 01b0958b..7b971fac 100644
--- a/api/README
+++ b/api/README
@@ -1,3 +1,2 @@
This directory contains swagger-compatible representations of our APIs. See
-scripts/README.md for details on how to make use of them.
-
+the main README.rst for details on how to make use of them.
diff --git a/api/application-service/application_service.yaml b/api/application-service/application_service.yaml
deleted file mode 100644
index d7ad5b19..00000000
--- a/api/application-service/application_service.yaml
+++ /dev/null
@@ -1,213 +0,0 @@
-# Copyright 2016 OpenMarket Ltd
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-swagger: '2.0'
-info:
- title: "Matrix Application Service API"
- version: "1.0.0"
-host: localhost:8008
-schemes:
- - https
- - http
-basePath: "/"
-consumes:
- - application/json
-produces:
- - application/json
-paths:
- "/transactions/{txnId}":
- put:
- summary: Send some events to the application service.
- description: |-
- This API is called by the HS when the HS wants to push an event (or
- batch of events) to the AS.
- parameters:
- - in: path
- name: txnId
- type: string
- description: |-
- The transaction ID for this set of events. Homeservers generate
- these IDs and they are used to ensure idempotency of requests.
- required: true
- x-example: "35"
- - in: body
- name: body
- description: A list of events
- schema:
- type: object
- example: |-
- {
- "events": [
- {
- "age": 32,
- "content": {
- "body": "incoming message",
- "msgtype": "m.text"
- },
- "event_id": "$14328055551tzaee:localhost",
- "origin_server_ts": 1432804485886,
- "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
- "type": "m.room.message",
- "user_id": "@bob:localhost"
- },
- {
- "age": 1984,
- "content": {
- "body": "another incoming message",
- "msgtype": "m.text"
- },
- "event_id": "$1228055551ffsef:localhost",
- "origin_server_ts": 1432804485886,
- "room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
- "type": "m.room.message",
- "user_id": "@bob:localhost"
- }
- ]
- }
- description: "Transaction informations"
- properties:
- events:
- type: array
- description: A list of events
- items:
- type: object
- title: Event
- required: ["events"]
- responses:
- 200:
- description: The transaction was processed successfully.
- examples:
- application/json: |-
- {}
- schema:
- type: object
-
- "/rooms/{roomAlias}":
- get:
- summary: Query if a room alias should exist on the application service.
- description: |-
- This endpoint is invoked by the homeserver on an application service to query
- the existence of a given room alias. The homeserver will only query room
- aliases inside the application service's ``aliases`` namespace. The
- homeserver will send this request when it receives a request to join a
- room alias within the application service's namespace.
- parameters:
- - in: path
- name: roomAlias
- type: string
- description: The room alias being queried.
- required: true
- x-example: "#magicforest:example.com"
- responses:
- 200:
- description: |-
- The application service indicates that this room alias exists. The
- application service MUST have created a room and associated it with
- the queried room alias using the client-server API. Additional
- information about the room such as its name and topic can be set
- before responding.
- examples:
- application/json: |-
- {}
- schema:
- type: object
- 401:
- description: |-
- The homeserver has not supplied credentials to the application service.
- Optional error information can be included in the body of this response.
- examples:
- application/json: |-
- {
- "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
- }
- schema:
- type: object
- 403:
- description: |-
- The credentials supplied by the homeserver were rejected.
- examples:
- application/json: |-
- {
- "errcode": "M_FORBIDDEN"
- }
- schema:
- type: object
- 404:
- description: |-
- The application service indicates that this room alias does not exist.
- Optional error information can be included in the body of this response.
- examples:
- application/json: |-
- {
- "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
- }
- schema:
- type: object
- "/users/{userId}":
- get:
- summary: Query if a user should exist on the application service.
- description: |-
- This endpoint is invoked by the homeserver on an application service to query
- the existence of a given user ID. The homeserver will only query user IDs
- inside the application service's ``users`` namespace. The homeserver will
- send this request when it receives an event for an unknown user ID in
- the application service's namespace.
- parameters:
- - in: path
- name: userId
- type: string
- description: The user ID being queried.
- required: true
- x-example: "@alice:example.com"
- responses:
- 200:
- description: |-
- The application service indicates that this user exists. The application
- service MUST create the user using the client-server API.
- examples:
- application/json: |-
- {}
- schema:
- type: object
- 401:
- description: |-
- The homeserver has not supplied credentials to the application service.
- Optional error information can be included in the body of this response.
- examples:
- application/json: |-
- {
- "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
- }
- schema:
- type: object
- 403:
- description: |-
- The credentials supplied by the homeserver were rejected.
- examples:
- application/json: |-
- {
- "errcode": "M_FORBIDDEN"
- }
- schema:
- type: object
- 404:
- description: |-
- The application service indicates that this user does not exist.
- Optional error information can be included in the body of this response.
- examples:
- application/json: |-
- {
- "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
- }
- schema:
- type: object
diff --git a/api/application-service/definitions/location.yaml b/api/application-service/definitions/location.yaml
new file mode 100644
index 00000000..5a0f92c8
--- /dev/null
+++ b/api/application-service/definitions/location.yaml
@@ -0,0 +1,32 @@
+# 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.
+properties:
+ alias:
+ description: An alias for a matrix room.
+ type: string
+ example: "#freenode_#matrix:matrix.org"
+ protocol:
+ description: The protocol ID that the third party location is a part of.
+ type: string
+ example: "irc"
+ fields:
+ description: Information used to identify this third party location.
+ type: object
+ example: {
+ "network": "freenode",
+ "channel": "#matrix"
+ }
+required: ['alias', 'protocol', 'fields']
+title: Location
+type: object
\ No newline at end of file
diff --git a/api/application-service/definitions/location_batch.yaml b/api/application-service/definitions/location_batch.yaml
new file mode 100644
index 00000000..3f6de9df
--- /dev/null
+++ b/api/application-service/definitions/location_batch.yaml
@@ -0,0 +1,17 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: array
+description: List of matched third party locations.
+items:
+ $ref: location.yaml
diff --git a/api/application-service/definitions/protocol.yaml b/api/application-service/definitions/protocol.yaml
new file mode 100644
index 00000000..851091d6
--- /dev/null
+++ b/api/application-service/definitions/protocol.yaml
@@ -0,0 +1,113 @@
+# 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.
+title: Protocol
+type: object
+properties:
+ user_fields:
+ description: |-
+ Fields which may be used to identify a third party user. These should be
+ ordered to suggest the way that entities may be grouped, where higher
+ groupings are ordered first. For example, the name of a network should be
+ searched before the nickname of a user.
+ type: array
+ items:
+ type: string
+ description: Field used to identify a third party user.
+ example: ["network", "nickname"]
+ location_fields:
+ description: |-
+ Fields which may be used to identify a third party location. These should be
+ ordered to suggest the way that entities may be grouped, where higher
+ groupings are ordered first. For example, the name of a network should be
+ searched before the name of a channel.
+ type: array
+ items:
+ type: string
+ description: Field used to identify a third party location.
+ example: ["network", "channel"]
+ icon:
+ description: A content URI representing an icon for the third party protocol.
+ type: string
+ example: "mxc://example.org/aBcDeFgH"
+ field_types:
+ title: Field Types
+ description: |-
+ The type definitions for the fields defined in the ``user_fields`` and
+ ``location_fields``. Each entry in those arrays MUST have an entry here. The
+ ``string`` key for this object is field name itself.
+
+ May be an empty object if no fields are defined.
+ type: object
+ additionalProperties:
+ title: Field Type
+ description: Definition of valid values for a field.
+ type: object
+ properties:
+ regexp:
+ description: |-
+ A regular expression for validation of a field's value. This may be relatively
+ coarse to verify the value as the application service providing this protocol
+ may apply additional validation or filtering.
+ type: string
+ placeholder:
+ description: An placeholder serving as a valid example of the field value.
+ type: string
+ required: ['regexp', 'placeholder']
+ required: ['fieldname']
+ example: {
+ "network": {
+ "regexp": "([a-z0-9]+\\.)*[a-z0-9]+",
+ "placeholder": "irc.example.org"
+ },
+ "nickname": {
+ "regexp": "[^\\s#]+",
+ "placeholder": "username"
+ },
+ "channel": {
+ "regexp": "#[^\\s]+",
+ "placeholder": "#foobar"
+ }
+ }
+ instances:
+ description: |-
+ A list of objects representing independent instances of configuration.
+ For example, multiple networks on IRC if multiple are provided by the
+ same application service.
+ type: array
+ items:
+ type: object
+ title: Protocol Instance
+ properties:
+ desc:
+ type: string
+ description: A human-readable description for the protocol, such as the name.
+ example: "Freenode"
+ icon:
+ type: string
+ description: |-
+ An optional content URI representing the protocol. Overrides the one provided
+ at the higher level Protocol object.
+ example: "mxc://example.org/JkLmNoPq"
+ fields:
+ type: object
+ description: Preset values for ``fields`` the client may use to search by.
+ example: {
+ "network": "freenode"
+ }
+ network_id:
+ type: string
+ description: A unique identifier across all instances.
+ example: "freenode"
+ required: ['desc', 'fields', 'network_id']
+required: ['user_fields', 'location_fields', 'icon', 'field_types', 'instances']
diff --git a/api/application-service/definitions/protocol_metadata.yaml b/api/application-service/definitions/protocol_metadata.yaml
new file mode 100644
index 00000000..e7bf45da
--- /dev/null
+++ b/api/application-service/definitions/protocol_metadata.yaml
@@ -0,0 +1,70 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: object
+description: Dictionary of supported third party protocols.
+additionalProperties:
+ $ref: protocol.yaml
+example: {
+ "irc": {
+ "user_fields": ["network", "nickname"],
+ "location_fields": ["network", "channel"],
+ "icon": "mxc://example.org/aBcDeFgH",
+ "field_types": {
+ "network": {
+ "regexp": "([a-z0-9]+\\.)*[a-z0-9]+",
+ "placeholder": "irc.example.org"
+ },
+ "nickname": {
+ "regexp": "[^\\s]+",
+ "placeholder": "username"
+ },
+ "channel": {
+ "regexp": "#[^\\s]+",
+ "placeholder": "#foobar"
+ }
+ },
+ "instances": [
+ {
+ "network_id": "freenode",
+ "desc": "Freenode",
+ "icon": "mxc://example.org/JkLmNoPq",
+ "fields": {
+ "network": "freenode.net",
+ }
+ }
+ ]
+ },
+ "gitter": {
+ "user_fields": ["username"],
+ "location_fields": ["room"],
+ "field_types": {
+ "username": {
+ "regexp": "@[^\\s]+",
+ "placeholder": "@username"
+ },
+ "room": {
+ "regexp": "[^\\s]+\\/[^\\s]+",
+ "placeholder": "matrix-org/matrix-doc"
+ }
+ },
+ "instances": [
+ {
+ "network_id": "gitter",
+ "desc": "Gitter",
+ "icon": "mxc://example.org/zXyWvUt",
+ "fields": {}
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/api/application-service/definitions/user.yaml b/api/application-service/definitions/user.yaml
new file mode 100644
index 00000000..258e7c13
--- /dev/null
+++ b/api/application-service/definitions/user.yaml
@@ -0,0 +1,33 @@
+# 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.
+
+# TODO: Change userid to user_id as a breaking change
+properties:
+ userid:
+ description: A Matrix User ID represting a third party user.
+ type: string
+ example: "@_gitter_jim:matrix.org"
+ protocol:
+ description: The protocol ID that the third party location is a part of.
+ type: string
+ example: "gitter"
+ fields:
+ description: Information used to identify this third party location.
+ type: object
+ example: {
+ "user": "jim"
+ }
+required: ['userid', 'protocol', 'fields']
+title: User
+type: object
\ No newline at end of file
diff --git a/api/application-service/definitions/user_batch.yaml b/api/application-service/definitions/user_batch.yaml
new file mode 100644
index 00000000..3653feb4
--- /dev/null
+++ b/api/application-service/definitions/user_batch.yaml
@@ -0,0 +1,17 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: array
+description: List of matched third party users.
+items:
+ $ref: user.yaml
diff --git a/api/application-service/protocols.yaml b/api/application-service/protocols.yaml
new file mode 100644
index 00000000..e6489cc5
--- /dev/null
+++ b/api/application-service/protocols.yaml
@@ -0,0 +1,267 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Application Service API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: "/"
+consumes:
+ - application/json
+produces:
+ - application/json
+paths:
+ "/_matrix/app/unstable/thirdparty/protocol/{protocol}":
+ get:
+ summary: Retrieve metadata about a specific protocol that the application service supports.
+ description: |-
+ This API is called by the homeserver when it wants to present clients
+ with specific information about the various third party networks that
+ an application service supports.
+ operationId: getProtocolMetadata
+ parameters:
+ - in: path
+ name: protocol
+ type: string
+ description: The protocol ID.
+ required: true
+ x-example: "irc"
+ responses:
+ 200:
+ description: The protocol was found and metadata returned.
+ schema:
+ $ref: definitions/protocol_metadata.yaml
+ 401:
+ description: |-
+ The homeserver has not supplied credentials to the application service.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 403:
+ description: |-
+ The credentials supplied by the homeserver were rejected.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 404:
+ description: No protocol was found with the given path.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ "/_matrix/app/unstable/thirdparty/user/{protocol}":
+ get:
+ summary: Retrieve the Matrix User ID of a corresponding third party user.
+ description: |-
+ This API is called by the homeserver in order to retrieve a Matrix
+ User ID linked to a user on the third party network, given a set of
+ user parameters.
+ operationId: queryUserByProtocol
+ parameters:
+ - in: path
+ name: protocol
+ type: string
+ description: The protocol ID.
+ required: true
+ x-example: irc
+ - in: query
+ name: fields...
+ type: string
+ description: |-
+ One or more custom fields that are passed to the application
+ service to help identify the user.
+ responses:
+ 200:
+ description: The Matrix User IDs found with the given parameters.
+ schema:
+ $ref: definitions/user_batch.yaml
+ 401:
+ description: |-
+ The homeserver has not supplied credentials to the application service.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 403:
+ description: |-
+ The credentials supplied by the homeserver were rejected.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 404:
+ description: No users were found with the given parameters.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ "/_matrix/app/unstable/thirdparty/location/{protocol}":
+ get:
+ summary: Retrieve Matrix-side portal rooms leading to a third party location.
+ description: |-
+ Retrieve a list of Matrix portal rooms that lead to the matched third party location.
+ operationId: queryLocationByProtocol
+ parameters:
+ - in: path
+ name: protocol
+ type: string
+ description: The protocol ID.
+ required: true
+ x-example: irc
+ - in: query
+ name: fields...
+ type: string
+ description: |-
+ One or more custom fields that are passed to the application
+ service to help identify the third party location.
+ responses:
+ 200:
+ description: At least one portal room was found.
+ schema:
+ $ref: definitions/location_batch.yaml
+ 401:
+ description: |-
+ The homeserver has not supplied credentials to the application service.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 403:
+ description: |-
+ The credentials supplied by the homeserver were rejected.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 404:
+ description: No mappings were found with the given parameters.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ "/_matrix/app/unstable/thirdparty/location":
+ get:
+ summary: Reverse-lookup third party locations given a Matrix room alias.
+ description: |-
+ Retrieve an array of third party network locations from a Matrix room
+ alias.
+ operationId: queryLocationByAlias
+ parameters:
+ - in: query
+ name: alias
+ type: string
+ description: The Matrix room alias to look up.
+ responses:
+ 200:
+ description: |-
+ All found third party locations.
+ schema:
+ $ref: definitions/location_batch.yaml
+ 401:
+ description: |-
+ The homeserver has not supplied credentials to the application service.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 403:
+ description: |-
+ The credentials supplied by the homeserver were rejected.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 404:
+ description: No mappings were found with the given parameters.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ "/_matrix/app/unstable/thirdparty/user":
+ get:
+ summary: Reverse-lookup third party users given a Matrix User ID.
+ description: |-
+ Retrieve an array of third party users from a Matrix User ID.
+ operationId: queryUserByID
+ parameters:
+ - in: query
+ name: userid
+ type: string
+ description: The Matrix User ID to look up.
+ responses:
+ 200:
+ description: |-
+ An array of third party users.
+ schema:
+ $ref: definitions/user_batch.yaml
+ 401:
+ description: |-
+ The homeserver has not supplied credentials to the application service.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 403:
+ description: |-
+ The credentials supplied by the homeserver were rejected.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 404:
+ description: No mappings were found with the given parameters.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
diff --git a/api/application-service/query_room.yaml b/api/application-service/query_room.yaml
new file mode 100644
index 00000000..b885cb86
--- /dev/null
+++ b/api/application-service/query_room.yaml
@@ -0,0 +1,86 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Application Service API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: "/"
+consumes:
+ - application/json
+produces:
+ - application/json
+paths:
+ "/rooms/{roomAlias}":
+ get:
+ summary: Query if a room alias should exist on the application service.
+ description: |-
+ This endpoint is invoked by the homeserver on an application service to query
+ the existence of a given room alias. The homeserver will only query room
+ aliases inside the application service's ``aliases`` namespace. The
+ homeserver will send this request when it receives a request to join a
+ room alias within the application service's namespace.
+ operationId: queryRoomByAlias
+ parameters:
+ - in: path
+ name: roomAlias
+ type: string
+ description: The room alias being queried.
+ required: true
+ x-example: "#magicforest:example.com"
+ responses:
+ 200:
+ description: |-
+ The application service indicates that this room alias exists. The
+ application service MUST have created a room and associated it with
+ the queried room alias using the client-server API. Additional
+ information about the room such as its name and topic can be set
+ before responding.
+ examples:
+ application/json: {}
+ schema:
+ type: object
+ 401:
+ description: |-
+ The homeserver has not supplied credentials to the application service.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 403:
+ description: |-
+ The credentials supplied by the homeserver were rejected.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 404:
+ description: |-
+ The application service indicates that this room alias does not exist.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
diff --git a/api/application-service/query_user.yaml b/api/application-service/query_user.yaml
new file mode 100644
index 00000000..0431b5e4
--- /dev/null
+++ b/api/application-service/query_user.yaml
@@ -0,0 +1,83 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Application Service API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: "/"
+consumes:
+ - application/json
+produces:
+ - application/json
+paths:
+ "/users/{userId}":
+ get:
+ summary: Query if a user should exist on the application service.
+ description: |-
+ This endpoint is invoked by the homeserver on an application service to query
+ the existence of a given user ID. The homeserver will only query user IDs
+ inside the application service's ``users`` namespace. The homeserver will
+ send this request when it receives an event for an unknown user ID in
+ the application service's namespace, such as a room invite.
+ operationId: queryUserById
+ parameters:
+ - in: path
+ name: userId
+ type: string
+ description: The user ID being queried.
+ required: true
+ x-example: "@alice:example.com"
+ responses:
+ 200:
+ description: |-
+ The application service indicates that this user exists. The application
+ service MUST create the user using the client-server API.
+ examples:
+ application/json: {}
+ schema:
+ type: object
+ 401:
+ description: |-
+ The homeserver has not supplied credentials to the application service.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 403:
+ description: |-
+ The credentials supplied by the homeserver were rejected.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_FORBIDDEN"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
+ 404:
+ description: |-
+ The application service indicates that this user does not exist.
+ Optional error information can be included in the body of this response.
+ examples:
+ application/json: {
+ "errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
+ }
+ schema:
+ $ref: ../client-server/definitions/errors/error.yaml
diff --git a/api/application-service/transactions.yaml b/api/application-service/transactions.yaml
new file mode 100644
index 00000000..8735cc8f
--- /dev/null
+++ b/api/application-service/transactions.yaml
@@ -0,0 +1,74 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Application Service API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: "/"
+produces:
+ - application/json
+paths:
+ "/transactions/{txnId}":
+ put:
+ summary: Send some events to the application service.
+ description: |-
+ This API is called by the homeserver when it wants to push an event
+ (or batch of events) to the application service.
+
+ Note that the application service should distinguish state events
+ from message events via the presence of a ``state_key``, rather than
+ via the event type.
+ operationId: sendTransaction
+ parameters:
+ - in: path
+ name: txnId
+ type: string
+ description: |-
+ The transaction ID for this set of events. Homeservers generate
+ these IDs and they are used to ensure idempotency of requests.
+ required: true
+ x-example: "35"
+ - in: body
+ name: body
+ description: A list of events.
+ schema:
+ type: object
+ example: {
+ "events": [
+ {"$ref": "../../event-schemas/examples/m.room.member"},
+ {"$ref": "../../event-schemas/examples/m.room.message#m.text"}
+ ]
+ }
+ description: Transaction information
+ properties:
+ events:
+ type: array
+ description: |-
+ A list of events, formatted as per the Client-Server API.
+ items:
+ type: object
+ title: Event
+ required: ["events"]
+ responses:
+ 200:
+ description: The transaction was processed successfully.
+ examples:
+ application/json: {}
+ schema:
+ type: object
diff --git a/api/check_examples.py b/api/check_examples.py
index 009055be..0fb275b1 100755
--- a/api/check_examples.py
+++ b/api/check_examples.py
@@ -43,28 +43,23 @@ except ImportError as e:
raise
+def check_schema(filepath, example, schema):
+ example = resolve_references(filepath, example)
+ schema = resolve_references(filepath, schema)
+ resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_file})
+ jsonschema.validate(example, schema, resolver=resolver)
+
+
def check_parameter(filepath, request, parameter):
schema = parameter.get("schema")
- example = None
- try:
- example_json = schema.get('example')
- if example_json and not schema.get("format") == "byte":
- example = json.loads(example_json)
- except Exception as e:
- raise ValueError("Error parsing JSON example request for %r" % (
- request
- ), e)
- fileurl = "file://" + os.path.abspath(filepath)
+ example = schema.get('example')
+
if example and schema:
try:
- print ("Checking request schema for: %r %r" % (
+ print("Checking request schema for: %r %r" % (
filepath, request
))
- # Setting the 'id' tells jsonschema where the file is so that it
- # can correctly resolve relative $ref references in the schema
- schema['id'] = fileurl
- resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml})
- jsonschema.validate(example, schema, resolver=resolver)
+ check_schema(filepath, example, schema)
except Exception as e:
raise ValueError("Error validating JSON schema for %r" % (
request
@@ -72,27 +67,14 @@ def check_parameter(filepath, request, parameter):
def check_response(filepath, request, code, response):
- example = None
- try:
- example_json = response.get('examples', {}).get('application/json')
- if example_json:
- example = json.loads(example_json)
- except Exception as e:
- raise ValueError("Error parsing JSON example response for %r %r" % (
- request, code
- ), e)
+ example = response.get('examples', {}).get('application/json')
schema = response.get('schema')
- fileurl = "file://" + os.path.abspath(filepath)
if example and schema:
try:
print ("Checking response schema for: %r %r %r" % (
filepath, request, code
))
- # Setting the 'id' tells jsonschema where the file is so that it
- # can correctly resolve relative $ref references in the schema
- schema['id'] = fileurl
- resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml})
- jsonschema.validate(example, schema, resolver=resolver)
+ check_schema(filepath, example, schema)
except Exception as e:
raise ValueError("Error validating JSON schema for %r %r" % (
request, code
@@ -119,12 +101,39 @@ def check_swagger_file(filepath):
check_response(filepath, request, code, response)
-def load_yaml(path):
- if not path.startswith("file:///"):
+def resolve_references(path, schema):
+ 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)
+ del schema['$ref']
+ else:
+ result = {}
+
+ for key, value in schema.items():
+ result[key] = resolve_references(path, value)
+ return result
+ elif isinstance(schema, list):
+ return [resolve_references(path, value) for value in schema]
+ else:
+ return schema
+
+
+def load_file(path):
+ print("Loading reference: %s" % path)
+ if not path.startswith("file://"):
raise Exception("Bad ref: %s" % (path,))
path = path[len("file://"):]
with open(path, "r") as f:
- return yaml.load(f)
+ if path.endswith(".json"):
+ return json.load(f)
+ else:
+ # We have to assume it's YAML because some of the YAML examples
+ # do not have file extensions.
+ return yaml.load(f)
if __name__ == '__main__':
diff --git a/api/client-server/account-data.yaml b/api/client-server/account-data.yaml
index 934c59cf..76b2b156 100644
--- a/api/client-server/account-data.yaml
+++ b/api/client-server/account-data.yaml
@@ -34,6 +34,7 @@ paths:
Set some account_data for the client. This config is only visible to the user
that set the account_data. The config will be synced to clients in the
top-level ``account_data``.
+ operationId: setAccountData
security:
- accessToken: []
parameters:
@@ -60,8 +61,8 @@ paths:
The content of the account_data
schema:
type: object
- example: |-
- {"custom_account_data_key": "custom_config_value"}
+ example: {
+ "custom_account_data_key": "custom_config_value"}
responses:
200:
description:
@@ -75,6 +76,7 @@ paths:
Set some account_data for the client on a given room. This config is only
visible to the user that set the account_data. The config will be synced to
clients in the per-room ``account_data``.
+ operationId: setAccountDataPerRoom
security:
- accessToken: []
parameters:
@@ -108,8 +110,8 @@ paths:
The content of the account_data
schema:
type: object
- example: |-
- {"custom_account_data_key": "custom_account_data_value"}
+ example: {
+ "custom_account_data_key": "custom_account_data_value"}
responses:
200:
description:
diff --git a/api/client-server/admin.yaml b/api/client-server/admin.yaml
index a27596a8..09942a10 100644
--- a/api/client-server/admin.yaml
+++ b/api/client-server/admin.yaml
@@ -36,6 +36,7 @@ paths:
This API may be restricted to only be called by the user being looked
up, or by a server admin. Server-local administrator privileges are not
specified in this document.
+ operationId: getWhoIs
security:
- accessToken: []
parameters:
@@ -49,8 +50,7 @@ paths:
200:
description: The lookup was successful.
examples:
- application/json: |-
- {
+ application/json: {
"user_id": "@peter:rabbit.rocks",
"devices": {
"teapot": {
@@ -105,7 +105,8 @@ paths:
type: string
description: Most recently seen IP address of the session.
last_seen:
- type: number
+ type: integer
+ format: int64
description: Unix timestamp that the session was last active.
user_agent:
type: string
diff --git a/api/client-server/administrative_contact.yaml b/api/client-server/administrative_contact.yaml
index 73f2e05c..1cf66fe1 100644
--- a/api/client-server/administrative_contact.yaml
+++ b/api/client-server/administrative_contact.yaml
@@ -39,14 +39,14 @@ paths:
Identifiers in this list may be used by the homeserver as, for example,
identifiers that it will accept to reset the user's account password.
+ operationId: getAccount3PIDs
security:
- accessToken: []
responses:
200:
description: The lookup was successful.
examples:
- application/json: |-
- {
+ application/json: {
"threepids": [
{
"medium": "email",
@@ -66,7 +66,7 @@ paths:
medium:
type: string
description: The medium of the third party identifier.
- enum: ["email"]
+ enum: ["email", "msisdn"]
address:
type: string
description: The third party identifier address.
@@ -75,6 +75,7 @@ paths:
post:
summary: Adds contact information to the user's account.
description: Adds contact information to the user's account.
+ operationId: post3PIDs
security:
- accessToken: []
parameters:
@@ -106,8 +107,7 @@ paths:
server. Default: ``false``.
x-example: true
required: ["three_pid_creds"]
- example: |-
- {
+ example: {
"three_pid_creds": {
"id_server": "matrix.org",
"sid": "abc123987",
@@ -119,17 +119,18 @@ paths:
200:
description: The addition was successful.
examples:
- application/json: "{}"
+ application/json: {}
schema:
type: object
403:
description: The credentials could not be verified with the identity server.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_THREEPID_AUTH_FAILED",
"error": "The third party credentials could not be verified by the identity server."
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- User data
"/account/3pid/email/requestToken":
@@ -142,6 +143,21 @@ paths:
validation tokens when adding an email address to an account. This API's
parameters and response is identical to that of the HS API
|/register/email/requestToken|_ endpoint.
+ operationId: requestTokenTo3PIDEmail
responses:
200:
- description: An email was sent to the given address
+ description: An email was sent to the given address.
+ "/account/3pid/msisdn/requestToken":
+ post:
+ summary: Requests a validation token be sent to the given email address for the purpose of adding a phone number to an account.
+ description: |-
+ Proxies the identity server API ``validate/msisdn/requestToken``, but
+ first checks that the given phone number is **not** already associated
+ with an account on this Home Server. This API should be used to request
+ validation tokens when adding a phone number to an account. This API's
+ parameters and response is identical to that of the HS API
+ |/register/msisdn/requestToken|_ endpoint.
+ operationId: requestTokenTo3PIDMSISDN
+ responses:
+ 200:
+ description: An SMS message was sent to the given phone number.
diff --git a/api/client-server/appservice_room_directory.yaml b/api/client-server/appservice_room_directory.yaml
new file mode 100644
index 00000000..49393cd4
--- /dev/null
+++ b/api/client-server/appservice_room_directory.yaml
@@ -0,0 +1,88 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Client-Server Application Service Room Directory API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ # Note: this is the same access_token definition used elsewhere in the client
+ # server API, however this expects an access token for an application service.
+ $ref: definitions/security.yaml
+paths:
+ "/directory/list/appservice/{networkId}/{roomId}":
+ put:
+ summary: |-
+ Updates a room's visibility in the application service's room directory.
+ description: |-
+ Updates the visibility of a given room on the application service's room
+ directory.
+
+ This API is similar to the room directory visibility API used by clients
+ to update the homeserver's more general room directory.
+
+ This API requires the use of an application service access token (``as_token``)
+ instead of a typical client's access_token. This API cannot be invoked by
+ users who are not identified as application services.
+ operationId: updateAppserviceRoomDirectoryVsibility
+ parameters:
+ - in: path
+ type: string
+ name: networkId
+ description: |-
+ The protocol (network) ID to update the room list for. This would
+ have been provided by the application service as being listed as
+ a supported protocol.
+ required: true
+ x-example: "irc"
+ - in: path
+ type: string
+ name: roomId
+ description: The room ID to add to the directory.
+ required: true
+ x-example: "!somewhere:domain.com"
+ - in: body
+ name: body
+ required: true
+ schema:
+ type: object
+ properties:
+ visibility:
+ type: string
+ enum: ["public", "private"]
+ description: |-
+ Whether the room should be visible (public) in the directory
+ or not (private).
+ example: "public"
+ required: ['visibility']
+ security:
+ # again, this is the appservice's token - not a typical client's
+ - accessToken: []
+ responses:
+ 200:
+ description: The room's directory visibility has been updated.
+ schema:
+ type: object
+ examples:
+ application/json: {}
+ tags:
+ - Application service room directory management
diff --git a/api/client-server/banning.yaml b/api/client-server/banning.yaml
index cdeaaa5d..6ef430df 100644
--- a/api/client-server/banning.yaml
+++ b/api/client-server/banning.yaml
@@ -36,6 +36,7 @@ paths:
When a user is banned from a room, they may not join it or be invited to it until they are unbanned.
The caller must have the required power level in order to perform this operation.
+ operationId: ban
security:
- accessToken: []
parameters:
@@ -50,8 +51,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"reason": "Telling unfunny jokes",
"user_id": "@cheeky_monkey:matrix.org"
}
@@ -61,14 +61,15 @@ paths:
description: The fully qualified user ID of the user being banned.
reason:
type: string
- description: The reason the user has been banned.
+ description: The reason the user has been banned. This will be supplied as the
+ ``reason`` on the target's updated `m.room.member`_ event.
required: ["user_id"]
responses:
200:
description: The user has been kicked and banned from the room.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
403:
@@ -78,11 +79,12 @@ paths:
- The banner is not currently in the room.
- The banner's power level is insufficient to ban users from the room.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_FORBIDDEN",
"error": "You do not have a high enough power level to ban from this room."
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- Room membership
"/rooms/{roomId}/unban":
@@ -93,6 +95,7 @@ paths:
and join if they would otherwise be allowed to join according to its join rules.
The caller must have the required power level in order to perform this operation.
+ operationId: unban
security:
- accessToken: []
parameters:
@@ -107,8 +110,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"user_id": "@cheeky_monkey:matrix.org"
}
properties:
@@ -120,8 +122,8 @@ paths:
200:
description: The user has been unbanned from the room.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
403:
@@ -130,10 +132,11 @@ paths:
- The unbanner's power level is insufficient to unban users from the room.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_FORBIDDEN",
"error": "You do not have a high enough power level to unban from this room."
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- Room membership
diff --git a/api/client-server/cas_login_redirect.yaml b/api/client-server/cas_login_redirect.yaml
index 7a4cec5d..abe9069b 100644
--- a/api/client-server/cas_login_redirect.yaml
+++ b/api/client-server/cas_login_redirect.yaml
@@ -37,6 +37,7 @@ paths:
``redirectUrl=https://client.example.com/?q=p``, it might redirect to
``https://cas.example.com/?service=https%3A%2F%2Fserver.example.com%2F_matrix%2Fclient%2F%CLIENT_MAJOR_VERSION%%2Flogin%2Fcas%2Fticket%3FredirectUrl%3Dhttps%253A%252F%252Fclient.example.com%252F%253Fq%253Dp``.
+ operationId: redirectToCAS
parameters:
- in: query
type: string
diff --git a/api/client-server/cas_login_ticket.yaml b/api/client-server/cas_login_ticket.yaml
index 02469489..a08565a0 100644
--- a/api/client-server/cas_login_ticket.yaml
+++ b/api/client-server/cas_login_ticket.yaml
@@ -39,6 +39,7 @@ paths:
If validation is unsuccessful, the server should respond with a ``401
Unauthorized`` error, the body of which will be displayed to the user.
+ operationId: loginByCASTicket
parameters:
- in: query
type: string
diff --git a/api/client-server/content-repo.yaml b/api/client-server/content-repo.yaml
index f9c92a0d..5f4e9111 100644
--- a/api/client-server/content-repo.yaml
+++ b/api/client-server/content-repo.yaml
@@ -18,15 +18,24 @@ info:
host: localhost:8008
schemes:
- https
+ - http
basePath: /_matrix/media/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+ - "*/*"
produces:
- application/json
- "*/*"
+securityDefinitions:
+ $ref: definitions/security.yaml
paths:
"/upload":
post:
summary: Upload some content to the content repository.
+ operationId: uploadContent
produces: ["application/json"]
+ security:
+ - accessToken: []
parameters:
- in: header
name: Content-Type
@@ -57,15 +66,19 @@ paths:
type: string
description: "The MXC URI to the uploaded content."
examples:
- "application/json": |-
- {
+ application/json: {
"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
}
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Media
"/download/{serverName}/{mediaId}":
get:
summary: "Download content from the content repository."
+ operationId: getContent
produces: ["*/*"]
parameters:
- in: path
@@ -82,6 +95,16 @@ paths:
required: true
description: |
The media ID from the ``mxc://`` URI (the path component)
+ - in: query
+ type: boolean
+ name: allow_remote
+ x-example: false
+ required: false
+ default: true
+ description: |
+ Indicates to the server that it should not attempt to fetch the media if it is deemed
+ remote. This is to prevent routing loops where the server contacts itself. Defaults to
+ true if not provided.
responses:
200:
description: "The content that was previously uploaded."
@@ -94,11 +117,16 @@ paths:
type: "string"
schema:
type: file
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Media
"/download/{serverName}/{mediaId}/{fileName}":
get:
summary: "Download content from the content repository as a given filename."
+ operationId: getContentOverrideName
produces: ["*/*"]
parameters:
- in: path
@@ -122,6 +150,16 @@ paths:
required: true
description: |
The filename to give in the Content-Disposition
+ - in: query
+ type: boolean
+ name: allow_remote
+ x-example: false
+ required: false
+ default: true
+ description: |
+ Indicates to the server that it should not attempt to fetch the media if it is deemed
+ remote. This is to prevent routing loops where the server contacts itself. Defaults to
+ true if not provided.
responses:
200:
description: "The content that was previously uploaded."
@@ -134,11 +172,16 @@ paths:
type: "string"
schema:
type: file
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Media
"/thumbnail/{serverName}/{mediaId}":
get:
summary: "Download a thumbnail of the content from the content repository."
+ operationId: getContentThumbnail
produces: ["image/jpeg", "image/png"]
parameters:
- in: path
@@ -175,6 +218,16 @@ paths:
name: method
x-example: "scale"
description: The desired resizing method.
+ - in: query
+ type: boolean
+ name: allow_remote
+ x-example: false
+ required: false
+ default: true
+ description: |
+ Indicates to the server that it should not attempt to fetch the media if it is deemed
+ remote. This is to prevent routing loops where the server contacts itself. Defaults to
+ true if not provided.
responses:
200:
description: "A thumbnail of the requested content."
@@ -185,5 +238,108 @@ paths:
enum: ["image/jpeg", "image/png"]
schema:
type: file
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
+ tags:
+ - Media
+ "/preview_url":
+ get:
+ summary: "Get information about a URL for a client"
+ operationId: getUrlPreview
+ produces: ["application/json"]
+ security:
+ - accessToken: []
+ parameters:
+ - in: query
+ type: string
+ x-example: "https://matrix.org"
+ name: url
+ description: "The URL to get a preview of"
+ required: true
+ - in: query
+ type: integer
+ format: int64
+ x-example: 1510610716656
+ name: ts
+ description: |-
+ The preferred point in time to return a preview for. The server may
+ return a newer version if it does not have the requested version
+ available.
+ responses:
+ 200:
+ description: |-
+ The OpenGraph data for the URL, which may be empty. Some values are
+ replaced with matrix equivalents if they are provided in the response.
+ The differences from the OpenGraph protocol are described here.
+ schema:
+ type: object
+ properties:
+ "matrix:image:size":
+ type: integer
+ format: int64
+ description: |-
+ The byte-size of the image. Omitted if there is no image attached.
+ "og:image":
+ type: string
+ description: |-
+ An MXC URI to the image. Omitted if there is no image.
+ examples:
+ application/json: {
+ "og:title": "Matrix Blog Post",
+ "og:description": "This is a really cool blog post from matrix.org",
+ "og:image": "mxc://example.com/ascERGshawAWawugaAcauga",
+ "og:image:type": "image/png",
+ "og:image:height": 48,
+ "og:image:width": 48,
+ "matrix:image:size": 102400
+ }
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
+ tags:
+ - Media
+ "/config":
+ get:
+ summary: Get the configuration for the content repository.
+ description: |-
+ This endpoint allows clients to retrieve the configuration of the content
+ repository, such as upload limitations.
+ Clients SHOULD use this as a guide when using content repository endpoints.
+ All values are intentionally left optional. Clients SHOULD follow
+ the advice given in the field description when the field is not available.
+
+ **NOTE:** Both clients and server administrators should be aware that proxies
+ between the client and the server may affect the apparent behaviour of content
+ repository APIs, for example, proxies may enforce a lower upload size limit
+ than is advertised by the server on this endpoint.
+ operationId: getConfig
+ produces: ["application/json"]
+ security:
+ - accessToken: []
+ responses:
+ 200:
+ description: The public content repository configuration for the matrix server.
+ schema:
+ type: object
+ properties:
+ m.upload.size:
+ type: integer
+ format: int64
+ description: |-
+ The maximum size an upload can be in bytes.
+ Clients SHOULD use this as a guide when uploading content.
+ If not listed or null, the size limit should be treated as unknown.
+ examples:
+ application/json: {
+ "m.upload.size": 50000000
+ }
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/error.yaml"
+
tags:
- Media
diff --git a/api/client-server/create_room.yaml b/api/client-server/create_room.yaml
index a321b0f7..ac0c3b16 100644
--- a/api/client-server/create_room.yaml
+++ b/api/client-server/create_room.yaml
@@ -1,4 +1,5 @@
# 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.
@@ -37,15 +38,36 @@ paths:
the new room, including checking power levels for each event. It MUST
apply the events implied by the request in the following order:
- 1. Events set by ``presets``.
+ 0. A default ``m.room.power_levels`` event, giving the room creator
+ (and not other members) permission to send state events. Overridden
+ by the ``power_level_content_override`` parameter.
+
+ 1. Events set by the ``preset``. Currently these are the ``m.room.join_rules``,
+ ``m.room.history_visibility``, and ``m.room.guest_access`` state events.
2. Events listed in ``initial_state``, in the order that they are
listed.
- 3. Events implied by ``name`` and ``topic``.
+ 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and ``m.room.topic``
+ state events).
- 4. Invite events implied by ``invite`` and ``invite_3pid``.
+ 4. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` with
+ ``membership: invite`` and ``m.room.third_party_invite``).
+ The available presets do the following with respect to room state:
+
+ ======================== ============== ====================== ================ =========
+ Preset ``join_rules`` ``history_visibility`` ``guest_access`` Other
+ ======================== ============== ====================== ================ =========
+ ``private_chat`` ``invite`` ``shared`` ``can_join``
+ ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All invitees are given the same power level as the room creator.
+ ``public_chat`` ``public`` ``shared`` ``forbidden``
+ ======================== ============== ====================== ================ =========
+
+ The server will create a ``m.room.create`` event in the room with the
+ requesting user as the creator, alongside other keys provided in the
+ ``creation_content``.
+ operationId: createRoom
security:
- accessToken: []
parameters:
@@ -54,16 +76,15 @@ paths:
description: The desired room configuration.
schema:
type: object
- example: |-
- {
- "preset": "public_chat",
- "room_alias_name": "thepub",
- "name": "The Grand Duke Pub",
- "topic": "All about happy hour",
- "creation_content": {
- "m.federate": false
- }
+ example: {
+ "preset": "public_chat",
+ "room_alias_name": "thepub",
+ "name": "The Grand Duke Pub",
+ "topic": "All about happy hour",
+ "creation_content": {
+ "m.federate": false
}
+ }
properties:
visibility:
type: string
@@ -84,6 +105,9 @@ paths:
created the room. For example, if this was set to "foo" and
sent to the homeserver "example.com" the complete room alias
would be ``#foo:example.com``.
+
+ The complete room alias will become the canonical alias for
+ the room.
name:
type: string
description: |-
@@ -123,14 +147,22 @@ paths:
type: string
description: The invitee's third party identifier.
required: ["id_server", "medium", "address"]
+ room_version:
+ type: string
+ description: |-
+ The room version to set for the room. If not provided, the homeserver is
+ to use its configured default. If provided, the homeserver will return a
+ 400 error with the errcode ``M_UNSUPPORTED_ROOM_VERSION`` if it does not
+ support the room version.
+ example: "1"
creation_content:
title: CreationContent
type: object
description: |-
- Extra keys to be added to the content of the ``m.room.create``.
- The server will clobber the following keys: ``creator``. Future
- versions of the specification may allow the server to clobber
- other keys.
+ Extra keys, such as ``m.federate``, to be added to the content
+ of the `m.room.create`_ event. The server will clobber the following
+ keys: ``creator``, ``room_version``. Future versions of the specification
+ may allow the server to clobber other keys.
initial_state:
type: array
description: |-
@@ -139,7 +171,7 @@ paths:
room. The expected format of the state events are an object
with type, state_key and content keys set.
- Takes precedence over events set by ``presets``, but gets
+ Takes precedence over events set by ``preset``, but gets
overriden by ``name`` and ``topic`` keys.
items:
type: object
@@ -147,35 +179,39 @@ paths:
properties:
type:
type: string
+ description: The type of event to send.
state_key:
type: string
+ description: The state_key of the state event. Defaults to an empty string.
content:
- type: string
+ type: object
+ description: The content of the event.
+ required: ["type", "content"]
preset:
type: string
enum: ["private_chat", "public_chat", "trusted_private_chat"]
description: |-
Convenience parameter for setting various default state events
- based on a preset. Must be either:
+ based on a preset.
- ``private_chat`` =>
- ``join_rules`` is set to ``invite``.
- ``history_visibility`` is set to ``shared``.
-
- ``trusted_private_chat`` =>
- ``join_rules`` is set to ``invite``.
- ``history_visibility`` is set to ``shared``.
- All invitees are given the same power level as the room creator.
-
- ``public_chat``: =>
- ``join_rules`` is set to ``public``.
- ``history_visibility`` is set to ``shared``.
+ If unspecified, the server should use the ``visibility`` to determine
+ which preset to use. A visbility of ``public`` equates to a preset of
+ ``public_chat`` and ``private`` visibility equates to a preset of
+ ``private_chat``.
is_direct:
type: boolean
description: |-
This flag makes the server set the ``is_direct`` flag on the
``m.room.member`` events sent to the users in ``invite`` and
``invite_3pid``. See `Direct Messaging`_ for more information.
+ power_level_content_override:
+ title: Power Level Event Content
+ type: object
+ description: |-
+ The power level content to override in the default power level
+ event. This object is applied on top of the generated `m.room.power_levels`_
+ event content prior to it being sent to the room. Defaults to
+ overriding nothing.
responses:
200:
description: Information about the newly created room.
@@ -187,11 +223,11 @@ paths:
type: string
description: |-
The created room's ID.
+ required: ['room_id']
examples:
- application/json: |-
- {
- "room_id": "!sefiuhWgwghwWgh:example.com"
- }
+ application/json: {
+ "room_id": "!sefiuhWgwghwWgh:example.com"
+ }
400:
description: |-
@@ -208,6 +244,7 @@ paths:
invalid: for example, the user's ``power_level`` is set below
that necessary to set the room name (``errcode`` set to
``M_INVALID_ROOM_STATE``).
-
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- Room creation
diff --git a/api/client-server/definitions/device_keys.yaml b/api/client-server/definitions/device_keys.yaml
new file mode 100644
index 00000000..888c93a4
--- /dev/null
+++ b/api/client-server/definitions/device_keys.yaml
@@ -0,0 +1,68 @@
+# 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.
+type: object
+title: DeviceKeys
+description: Device identity keys
+properties:
+ user_id:
+ type: string
+ description: |-
+ The ID of the user the device belongs to. Must match the user ID used
+ when logging in.
+ example: "@alice:example.com"
+ device_id:
+ type: string
+ description: |-
+ The ID of the device these keys belong to. Must match the device ID used
+ when logging in.
+ example: "JLAFKJWSCS"
+ algorithms:
+ type: array
+ items:
+ type: string
+ description: |-
+ The encryption algorithms supported by this device.
+ example: ["m.olm.curve25519-aes-sha256", "m.megolm.v1.aes-sha"]
+ keys:
+ type: object
+ description: |-
+ Public identity keys. The names of the properties should be in the
+ format ``:``. The keys themselves should be
+ encoded as specified by the key algorithm.
+ additionalProperties:
+ type: string
+ example:
+ "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI"
+ "ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
+ signatures:
+ type: object
+ description: |-
+ Signatures for the device key object. A map from user ID, to a map from
+ ``:`` to the signature.
+
+ The signature is calculated using the process described at `Signing
+ JSON`_.
+ additionalProperties:
+ type: object
+ additionalProperties:
+ type: string
+ example:
+ "@alice:example.com":
+ "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
+required:
+ - user_id
+ - device_id
+ - algorithms
+ - keys
+ - signatures
diff --git a/api/client-server/definitions/error.yaml b/api/client-server/definitions/errors/error.yaml
similarity index 92%
rename from api/client-server/definitions/error.yaml
rename to api/client-server/definitions/errors/error.yaml
index fa5cada7..7471da6f 100644
--- a/api/client-server/definitions/error.yaml
+++ b/api/client-server/definitions/errors/error.yaml
@@ -17,7 +17,9 @@ properties:
errcode:
type: string
description: An error code.
+ example: M_UNKNOWN
error:
type: string
description: A human-readable error message.
+ example: An unknown error occurred
required: ["errcode"]
\ No newline at end of file
diff --git a/api/client-server/definitions/errors/rate_limited.yaml b/api/client-server/definitions/errors/rate_limited.yaml
new file mode 100644
index 00000000..aca82ce7
--- /dev/null
+++ b/api/client-server/definitions/errors/rate_limited.yaml
@@ -0,0 +1,32 @@
+# 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.
+$ref: error.yaml
+type: object
+description: The rate limit was reached for this request
+properties:
+ errcode:
+ type: string
+ description: The M_LIMIT_EXCEEDED error code
+ example: M_LIMIT_EXCEEDED
+ error:
+ type: string
+ description: A human-readable error message.
+ example: Too many requests
+ retry_after_ms:
+ type: integer
+ description: |-
+ The amount of time in milliseconds the client should wait
+ before trying the request again.
+ example: 2000
+required: ["errcode"]
\ No newline at end of file
diff --git a/api/client-server/definitions/event.yaml b/api/client-server/definitions/event.yaml
index aa893fd4..a53b6af7 100644
--- a/api/client-server/definitions/event.yaml
+++ b/api/client-server/definitions/event.yaml
@@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
properties:
+ event_id:
+ description: The ID of this event, if applicable.
+ type: string
content:
description: The content of this event. The fields in this object will vary depending
on the type of event.
diff --git a/api/client-server/definitions/event_filter.yaml b/api/client-server/definitions/event_filter.yaml
index 1cae3ea9..8c96917f 100644
--- a/api/client-server/definitions/event_filter.yaml
+++ b/api/client-server/definitions/event_filter.yaml
@@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-title: Filter
+title: EventFilter
properties:
limit:
description: The maximum number of events to return.
diff --git a/api/client-server/definitions/public_rooms_response.yaml b/api/client-server/definitions/public_rooms_response.yaml
new file mode 100644
index 00000000..ab701051
--- /dev/null
+++ b/api/client-server/definitions/public_rooms_response.yaml
@@ -0,0 +1,105 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+description: A list of the rooms on the server.
+required: ["chunk"]
+properties:
+ chunk:
+ title: "PublicRoomsChunks"
+ type: array
+ description: |-
+ A paginated chunk of public rooms.
+ items:
+ type: object
+ title: "PublicRoomsChunk"
+ required:
+ - room_id
+ - num_joined_members
+ - world_readable
+ - guest_can_join
+ properties:
+ aliases:
+ type: array
+ description: |-
+ Aliases of the room. May be empty.
+ items:
+ type: string
+ canonical_alias:
+ type: string
+ description: |-
+ The canonical alias of the room, if any.
+ name:
+ type: string
+ description: |-
+ The name of the room, if any.
+ num_joined_members:
+ type: integer
+ description: |-
+ The number of members joined to the room.
+ room_id:
+ type: string
+ description: |-
+ The ID of the room.
+ topic:
+ type: string
+ description: |-
+ The topic of the room, if any.
+ world_readable:
+ type: boolean
+ description: |-
+ Whether the room may be viewed by guest users without joining.
+ guest_can_join:
+ type: boolean
+ description: |-
+ Whether guest users may join the room and participate in it.
+ If they can, they will be subject to ordinary power level
+ rules like any other user.
+ avatar_url:
+ type: string
+ description: The URL for the room's avatar, if one is set.
+ next_batch:
+ type: string
+ description: |-
+ A pagination token for the response. The absence of this token
+ means there are no more results to fetch and the client should
+ stop paginating.
+ prev_batch:
+ type: string
+ description: |-
+ A pagination token that allows fetching previous results. The
+ absence of this token means there are no results before this
+ batch, i.e. this is the first batch.
+ total_room_count_estimate:
+ type: integer
+ description: |-
+ An estimate on the total number of public rooms, if the
+ server has an estimate.
+example: {
+ "chunk": [
+ {
+ "aliases": ["#murrays:cheese.bar"],
+ "avatar_url": "mxc://bleeker.street/CHEDDARandBRIE",
+ "guest_can_join": false,
+ "name": "CHEESE",
+ "num_joined_members": 37,
+ "room_id": "!ol19s:bleecker.street",
+ "topic": "Tasty tasty cheese",
+ "world_readable": true
+ }
+ ],
+ "next_batch": "p190q",
+ "prev_batch": "p1902",
+ "total_room_count_estimate": 115
+}
\ No newline at end of file
diff --git a/api/client-server/definitions/room_event_filter.yaml b/api/client-server/definitions/room_event_filter.yaml
index 7d9184b5..c36b3768 100644
--- a/api/client-server/definitions/room_event_filter.yaml
+++ b/api/client-server/definitions/room_event_filter.yaml
@@ -13,23 +13,23 @@
# limitations under the License.
allOf:
- $ref: event_filter.yaml
-title: RoomEventFilter
-properties:
- not_rooms:
- description: A list of room IDs to exclude. If this list is absent then no rooms
- are excluded. A matching room will be excluded even if it is listed in the ``'rooms'``
- filter.
- items:
- type: string
- type: array
- rooms:
- description: A list of room IDs to include. If this list is absent then all rooms
- are included.
- items:
- type: string
- type: array
- contains_url:
- type: boolean
- description: If ``true``, includes only events with a url key in their content. If
- ``false``, excludes those events.
-type: object
+- type: object
+ title: RoomEventFilter
+ properties:
+ not_rooms:
+ description: A list of room IDs to exclude. If this list is absent then no rooms
+ are excluded. A matching room will be excluded even if it is listed in the ``'rooms'``
+ filter.
+ items:
+ type: string
+ type: array
+ rooms:
+ description: A list of room IDs to include. If this list is absent then all rooms
+ are included.
+ items:
+ type: string
+ type: array
+ contains_url:
+ type: boolean
+ description: If ``true``, includes only events with a ``url`` key in their content. If
+ ``false``, excludes those events. Defaults to ``false``.
diff --git a/api/client-server/definitions/sync_filter.yaml b/api/client-server/definitions/sync_filter.yaml
index 69b245a3..33bead26 100644
--- a/api/client-server/definitions/sync_filter.yaml
+++ b/api/client-server/definitions/sync_filter.yaml
@@ -11,6 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+type: object
+title: Filter
properties:
event_fields:
description: List of event fields to include. If this list is absent then all
@@ -40,6 +42,7 @@ properties:
room:
title: RoomFilter
description: Filters to be applied to room data.
+ type: object
properties:
not_rooms:
description: A list of room IDs to exclude. If this list is absent then no rooms
@@ -76,5 +79,3 @@ properties:
allOf:
- $ref: room_event_filter.yaml
description: The per user account data to include for rooms.
- type: object
-type: object
diff --git a/api/client-server/definitions/timeline_batch.yaml b/api/client-server/definitions/timeline_batch.yaml
index 44baa39f..ce613ac4 100644
--- a/api/client-server/definitions/timeline_batch.yaml
+++ b/api/client-server/definitions/timeline_batch.yaml
@@ -19,7 +19,7 @@ properties:
on the filter
type: boolean
prev_batch:
- description: A token that can be supplied to to the ``from`` parameter of the
+ description: A token that can be supplied to the ``from`` parameter of the
rooms/{roomId}/messages endpoint
type: string
type: object
diff --git a/api/client-server/definitions/user_identifier.yaml b/api/client-server/definitions/user_identifier.yaml
new file mode 100644
index 00000000..ce65053d
--- /dev/null
+++ b/api/client-server/definitions/user_identifier.yaml
@@ -0,0 +1,24 @@
+# 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.
+title: User identifier
+description: |-
+ Identification information for a user
+type: object
+properties:
+ type:
+ type: string
+ description: The type of identification. See `Identifier types`_ for supported values and additional property descriptions.
+required:
+ - type
+additionalProperties: true
diff --git a/api/client-server/device_management.yaml b/api/client-server/device_management.yaml
index ddfbad85..75ee9e44 100644
--- a/api/client-server/device_management.yaml
+++ b/api/client-server/device_management.yaml
@@ -33,6 +33,7 @@ paths:
summary: List registered devices for the current user
description: |-
Gets information about all devices for the current user.
+ operationId: getDevices
security:
- accessToken: []
responses:
@@ -49,8 +50,7 @@ paths:
allOf:
- $ref: "definitions/client_device.yaml"
examples:
- application/json: |-
- {
+ application/json: {
"devices": [
{
"device_id": "QBUAZIFURK",
@@ -67,6 +67,7 @@ paths:
summary: Get a single device
description: |-
Gets information on a single device, by device id.
+ operationId: getDevice
security:
- accessToken: []
parameters:
@@ -84,8 +85,7 @@ paths:
allOf:
- $ref: "definitions/client_device.yaml"
examples:
- application/json: |-
- {
+ application/json: {
"device_id": "QBUAZIFURK",
"display_name": "android",
"last_seen_ip": "1.2.3.4",
@@ -99,6 +99,7 @@ paths:
summary: Update a device
description: |-
Updates the metadata on the given device.
+ operationId: updateDevice
security:
- accessToken: []
parameters:
@@ -125,8 +126,8 @@ paths:
200:
description: The device was successfully updated.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
404:
@@ -138,7 +139,8 @@ paths:
description: |-
This API endpoint uses the `User-Interactive Authentication API`_.
- Deletes the given device, and invalidates any access token assoicated with it.
+ Deletes the given device, and invalidates any access token associated with it.
+ operationId: deleteDevice
security:
- accessToken: []
parameters:
@@ -166,8 +168,55 @@ paths:
schema:
type: object
examples:
- application/json: |-
- {}
+ application/json: {
+ }
+ 401:
+ description: |-
+ The homeserver requires additional authentication information.
+ schema:
+ "$ref": "definitions/auth_response.yaml"
+ tags:
+ - Device management
+ "/delete_devices":
+ post:
+ summary: Bulk deletion of devices
+ description: |-
+ This API endpoint uses the `User-Interactive Authentication API`_.
+
+ Deletes the given devices, and invalidates any access token associated with them.
+ operationId: deleteDevices
+ security:
+ - accessToken: []
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ properties:
+ devices:
+ type: array
+ description: The list of device IDs to delete.
+ items:
+ type: string
+ description: A list of device IDs.
+ example: ["QBUAZIFURK", "AUIECTSRND"]
+ auth:
+ description: |-
+ Additional authentication information for the
+ user-interactive authentication API.
+ "$ref": "definitions/auth_data.yaml"
+ required:
+ - devices
+ responses:
+ 200:
+ description: |-
+ The devices were successfully removed, or had been removed
+ previously.
+ schema:
+ type: object
+ examples:
+ application/json: {
+ }
401:
description: |-
The homeserver requires additional authentication information.
diff --git a/api/client-server/directory.yaml b/api/client-server/directory.yaml
index 7a2a2378..78ddfa29 100644
--- a/api/client-server/directory.yaml
+++ b/api/client-server/directory.yaml
@@ -30,6 +30,7 @@ paths:
"/room/{roomAlias}":
put:
summary: Create a new mapping from room alias to room ID.
+ operationId: setRoomAlias
security:
- accessToken: []
parameters:
@@ -40,7 +41,7 @@ paths:
required: true
x-example: "#monkeys:matrix.org"
- in: body
- name: roomInfo
+ name: body
description: Information about this room alias.
required: true
schema:
@@ -49,26 +50,26 @@ paths:
room_id:
type: string
description: The room ID to set.
- example: |-
- {
- "room_id": "!abnjk1jdasj98:capuchins.com"
- }
+ required: ['room_id']
+ example: {
+ "room_id": "!abnjk1jdasj98:capuchins.com"
+ }
responses:
200:
description: The mapping was created.
examples:
- application/json: |-
- {}
+ application/json: {}
schema:
type: object
409:
description: A room alias with that name already exists.
examples:
- application/json: |-
- {
- "errcode": "M_UNKNOWN",
- "error": "Room alias #monkeys:matrix.org already exists."
- }
+ application/json: {
+ "errcode": "M_UNKNOWN",
+ "error": "Room alias #monkeys:matrix.org already exists."
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- Room directory
get:
@@ -80,6 +81,7 @@ paths:
domain part of the alias does not correspond to the server's own
domain.
+ operationId: getRoomIdByAlias
parameters:
- in: path
type: string
@@ -98,13 +100,12 @@ paths:
description: The room ID for this room alias.
servers:
type: array
- description: A list of servers that are aware of this room ID.
+ description: A list of servers that are aware of this room alias.
items:
type: string
- description: A server which is aware of this room ID.
+ description: A server which is aware of this room alias.
examples:
- application/json: |-
- {
+ application/json: {
"room_id": "!abnjk1jdasj98:capuchins.com",
"servers": [
"capuchins.com",
@@ -115,11 +116,12 @@ paths:
404:
description: There is no mapped room ID for this room alias.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_NOT_FOUND",
- "error": "Room ID !abnjk1jdasj98:capuchins.com not found."
+ "error": "Room alias #monkeys:matrix.org not found."
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- Room directory
delete:
@@ -128,6 +130,7 @@ paths:
Remove a mapping of room alias to room ID.
Servers may choose to implement additional access control checks here, for instance that room aliases can only be deleted by their creator or a server administrator.
+ operationId: deleteRoomAlias
security:
- accessToken: []
parameters:
@@ -141,8 +144,8 @@ paths:
200:
description: The mapping was deleted.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
tags:
diff --git a/api/client-server/event_context.yaml b/api/client-server/event_context.yaml
index 8c5567b2..91da3cf4 100644
--- a/api/client-server/event_context.yaml
+++ b/api/client-server/event_context.yaml
@@ -34,6 +34,7 @@ paths:
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.
+ operationId: getEventContext
security:
- accessToken: []
parameters:
@@ -99,8 +100,7 @@ paths:
allOf:
- "$ref": "definitions/event-schemas/schema/core-event-schema/state_event.yaml"
examples:
- application/json: |-
- {
+ application/json: {
"end": "t29-57_2_0_2",
"events_after": [
{
diff --git a/api/client-server/filter.yaml b/api/client-server/filter.yaml
index 1f932e2c..db215196 100644
--- a/api/client-server/filter.yaml
+++ b/api/client-server/filter.yaml
@@ -33,6 +33,7 @@ paths:
Uploads a new filter definition to the homeserver.
Returns a filter ID that may be used in future requests to
restrict which events are returned to the client.
+ operationId: defineFilter
security:
- accessToken: []
parameters:
@@ -52,52 +53,54 @@ paths:
type: object
allOf:
- $ref: "definitions/sync_filter.yaml"
- example: |-
- {
- "room": {
- "state": {
- "types": ["m.room.*"],
- "not_rooms": ["!726s6s6q:example.com"]
- },
- "timeline": {
- "limit": 10,
- "types": ["m.room.message"],
- "not_rooms": ["!726s6s6q:example.com"],
- "not_senders": ["@spam:example.com"]
- },
- "ephemeral": {
- "types": ["m.receipt", "m.typing"],
- "not_rooms": ["!726s6s6q:example.com"],
- "not_senders": ["@spam:example.com"]
- }
+ example: {
+ "room": {
+ "state": {
+ "types": ["m.room.*"],
+ "not_rooms": ["!726s6s6q:example.com"]
},
- "presence": {
- "types": ["m.presence"],
- "not_senders": ["@alice:example.com"]
+ "timeline": {
+ "limit": 10,
+ "types": ["m.room.message"],
+ "not_rooms": ["!726s6s6q:example.com"],
+ "not_senders": ["@spam:example.com"]
},
- "event_format": "client",
- "event_fields": ["type", "content", "sender"]
- }
+ "ephemeral": {
+ "types": ["m.receipt", "m.typing"],
+ "not_rooms": ["!726s6s6q:example.com"],
+ "not_senders": ["@spam:example.com"]
+ }
+ },
+ "presence": {
+ "types": ["m.presence"],
+ "not_senders": ["@alice:example.com"]
+ },
+ "event_format": "client",
+ "event_fields": ["type", "content", "sender"]
+ }
responses:
200:
description: The filter was created.
- examples:
- application/json: |-
- {
- "filter_id": "66696p746572"
- }
schema:
type: object
properties:
filter_id:
type: string
description: |-
- The ID of the filter that was created.
+ The ID of the filter that was created. Cannot start
+ with a ``{`` as this character is used to determine
+ if the filter provided is inline JSON or a previously
+ declared filter by homeservers on some APIs.
+ example: "66696p746572"
+ required: ['filter_id']
tags:
- Room participation
"/user/{userId}/filter/{filterId}":
get:
summary: Download a filter
+ operationId: getFilter
+ security:
+ - accessToken: []
parameters:
- in: path
name: userId
@@ -118,8 +121,7 @@ paths:
description: |-
"The filter defintion"
examples:
- application/json: |-
- {
+ application/json: {
"room": {
"state": {
"types": ["m.room.*"],
@@ -148,5 +150,7 @@ paths:
type: object
allOf:
- $ref: "definitions/sync_filter.yaml"
+ 404:
+ description: "Unknown filter."
tags:
- Room participation
diff --git a/api/client-server/inviting.yaml b/api/client-server/inviting.yaml
index e73d44fc..f312d5ce 100644
--- a/api/client-server/inviting.yaml
+++ b/api/client-server/inviting.yaml
@@ -51,6 +51,7 @@ paths:
``m.room.member`` event to the room.
.. _third party invites section: `invite-by-third-party-id-endpoint`_
+ operationId: inviteUser
security:
- accessToken: []
parameters:
@@ -65,8 +66,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"user_id": "@cheeky_monkey:matrix.org"
}
properties:
@@ -78,8 +78,8 @@ paths:
200:
description: The user has been invited to join the room.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
403:
@@ -91,11 +91,13 @@ paths:
- The inviter is not currently in the room.
- The inviter's power level is insufficient to invite users to the room.
examples:
- application/json: |-
- {"errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
+ application/json: {
+ "errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room membership
diff --git a/api/client-server/joining.yaml b/api/client-server/joining.yaml
index 92454239..7dc1e0a4 100644
--- a/api/client-server/joining.yaml
+++ b/api/client-server/joining.yaml
@@ -44,6 +44,7 @@ paths:
If a ``third_party_signed`` was supplied, the homeserver must verify
that it matches a pending ``m.room.third_party_invite`` event in the
room, and perform key validity checking if required by the event.
+ operationId: joinRoomById
security:
- accessToken: []
parameters:
@@ -57,8 +58,7 @@ paths:
name: third_party_signed
schema:
type: object
- example: |-
- {
+ example: {
"third_party_signed": {
"sender": "@cat:the.hat",
"mxid": "@green:eggs.ham",
@@ -97,8 +97,8 @@ paths:
The joined room ID must be returned in the ``room_id`` field.
examples:
- application/json: |-
- {"room_id": "!d41d8cd:matrix.org"}
+ application/json: {
+ "room_id": "!d41d8cd:matrix.org"}
schema:
type: object
403:
@@ -108,12 +108,14 @@ paths:
- The room is invite-only and the user was not invited.
- The user has been banned from the room.
examples:
- application/json: |-
- {"errcode": "M_FORBIDDEN", "error": "You are not invited to this room."}
+ application/json: {
+ "errcode": "M_FORBIDDEN", "error": "You are not invited to this room."}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room membership
"/join/{roomIdOrAlias}":
@@ -133,6 +135,7 @@ paths:
If a ``third_party_signed`` was supplied, the homeserver must verify
that it matches a pending ``m.room.third_party_invite`` event in the
room, and perform key validity checking if required by the event.
+ operationId: joinRoom
security:
- accessToken: []
parameters:
@@ -142,12 +145,20 @@ paths:
description: The room identifier or alias to join.
required: true
x-example: "#monkeys:matrix.org"
+ - in: query
+ type: array
+ items:
+ type: string
+ name: server_name
+ description: |-
+ The servers to attempt to join the room through. One of the servers
+ must be participating in the room.
+ x-example: ["matrix.org", "elsewhere.ca"]
- in: body
name: third_party_signed
schema:
type: object
- example: |-
- {
+ example: {
"third_party_signed": {
"signed": {
"sender": "@cat:the.hat",
@@ -193,8 +204,8 @@ paths:
The joined room ID must be returned in the ``room_id`` field.
examples:
- application/json: |-
- {"room_id": "!d41d8cd:matrix.org"}
+ application/json: {
+ "room_id": "!d41d8cd:matrix.org"}
schema:
type: object
403:
@@ -204,11 +215,13 @@ paths:
- The room is invite-only and the user was not invited.
- The user has been banned from the room.
examples:
- application/json: |-
- {"errcode": "M_FORBIDDEN", "error": "You are not invited to this room."}
+ application/json: {
+ "errcode": "M_FORBIDDEN", "error": "You are not invited to this room."}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room membership
diff --git a/api/client-server/keys.yaml b/api/client-server/keys.yaml
new file mode 100644
index 00000000..55f8a5a5
--- /dev/null
+++ b/api/client-server/keys.yaml
@@ -0,0 +1,357 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Client-Server Client Config API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/keys/upload":
+ post:
+ summary: Upload end-to-end encryption keys.
+ description: |-
+ Publishes end-to-end encryption keys for the device.
+ operationId: uploadKeys
+ security:
+ - accessToken: []
+ parameters:
+ - in: body
+ name: keys
+ description: |-
+ The keys to be published
+ schema:
+ type: object
+ properties:
+ device_keys:
+ description: |-
+ Identity keys for the device. May be absent if no new
+ identity keys are required.
+ allOf:
+ - $ref: definitions/device_keys.yaml
+ one_time_keys:
+ type: object
+ description: |-
+ One-time public keys for "pre-key" messages. The names of
+ the properties should be in the format
+ ``:``. The format of the key is determined
+ by the key algorithm.
+
+ May be absent if no new one-time keys are required.
+ additionalProperties:
+ type:
+ - string
+ - object
+ example:
+ "curve25519:AAAAAQ": "/qyvZvwjiTxGdGU0RCguDCLeR+nmsb3FfNG3/Ve4vU8"
+ signed_curve25519:AAAAHg:
+ key: "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs"
+ signatures:
+ "@alice:example.com":
+ ed25519:JLAFKJWSCS: "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
+ signed_curve25519:AAAAHQ:
+ key: "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw"
+ signatures:
+ "@alice:example.com":
+ ed25519:JLAFKJWSCS: "IQeCEPb9HFk217cU9kw9EOiusC6kMIkoIRnbnfOh5Oc63S1ghgyjShBGpu34blQomoalCyXWyhaaT3MrLZYQAA"
+ responses:
+ 200:
+ description:
+ The provided keys were sucessfully uploaded.
+ schema:
+ type: object
+ properties:
+ one_time_key_counts:
+ type: object
+ additionalProperties:
+ type: integer
+ description: |-
+ For each key algorithm, the number of unclaimed one-time keys
+ of that type currently held on the server for this device.
+ example:
+ curve25519: 10
+ signed_curve25519: 20
+ required:
+ - one_time_key_counts
+
+ tags:
+ - End-to-end encryption
+ "/keys/query":
+ post:
+ summary: Download device identity keys.
+ description: |-
+ Returns the current devices and identity keys for the given users.
+ operationId: queryKeys
+ security:
+ - accessToken: []
+ parameters:
+ - in: body
+ name: query
+ description: |-
+ Query defining the keys to be downloaded
+ schema:
+ type: object
+ properties:
+ timeout:
+ type: integer
+ description: |-
+ The time (in milliseconds) to wait when downloading keys from
+ remote servers. 10 seconds is the recommended default.
+ example: 10000
+ device_keys:
+ type: object
+ description: |-
+ The keys to be downloaded. A map from user ID, to a list of
+ device IDs, or to an empty list to indicate all devices for the
+ corresponding user.
+ additionalProperties:
+ type: array
+ items:
+ type: string
+ description: "device ID"
+ example:
+ "@alice:example.com": []
+ token:
+ type: string
+ description: |-
+ If the client is fetching keys as a result of a device update received
+ in a sync request, this should be the 'since' token of that sync request,
+ or any later sync token. This allows the server to ensure its response
+ contains the keys advertised by the notification in that sync.
+ required:
+ - device_keys
+
+ responses:
+ 200:
+ description:
+ The device information
+ schema:
+ type: object
+ properties:
+ failures:
+ type: object
+ description: |-
+ If any remote homeservers could not be reached, they are
+ recorded here. The names of the properties are the names of
+ the unreachable servers.
+
+ If the homeserver could be reached, but the user or device
+ was unknown, no failure is recorded. Instead, the corresponding
+ user or device is missing from the ``device_keys`` result.
+ additionalProperties:
+ type: object
+ example: {}
+ device_keys:
+ type: object
+ description: |-
+ Information on the queried devices. A map from user ID, to a
+ map from device ID to device information. For each device,
+ the information returned will be the same as uploaded via
+ ``/keys/upload``, with the addition of an ``unsigned``
+ property.
+ additionalProperties:
+ type: object
+ additionalProperties:
+ allOf:
+ - $ref: definitions/device_keys.yaml
+ properties:
+ unsigned:
+ title: UnsignedDeviceInfo
+ type: object
+ description: |-
+ Additional data added to the device key information
+ by intermediate servers, and not covered by the
+ signatures.
+ properties:
+ device_display_name:
+ type: string
+ description:
+ The display name which the user set on the device.
+ example:
+ "@alice:example.com":
+ JLAFKJWSCS: {
+ "user_id": "@alice:example.com",
+ "device_id": "JLAFKJWSCS",
+ "algorithms": [
+ "m.olm.v1.curve25519-aes-sha256",
+ "m.megolm.v1.aes-sha"
+ ],
+ "keys": {
+ "curve25519:JLAFKJWSCS": "3C5BFWi2Y8MaVvjM8M22DBmh24PmgR0nPvJOIArzgyI",
+ "ed25519:JLAFKJWSCS": "lEuiRJBit0IG6nUf5pUzWTUEsRVVe/HJkoKuEww9ULI"
+ },
+ "signatures": {
+ "@alice:example.com": {
+ "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
+ }
+ },
+ "unsigned": {
+ "device_display_name": "Alice's mobile phone"
+ }
+ }
+
+ tags:
+ - End-to-end encryption
+ "/keys/claim":
+ post:
+ summary: Claim one-time encryption keys.
+ description: |-
+ Claims one-time keys for use in pre-key messages.
+ operationId: claimKeys
+ security:
+ - accessToken: []
+ parameters:
+ - in: body
+ name: query
+ description: |-
+ Query defining the keys to be claimed
+ schema:
+ type: object
+ properties:
+ timeout:
+ type: integer
+ description: |-
+ The time (in milliseconds) to wait when downloading keys from
+ remote servers. 10 seconds is the recommended default.
+ example: 10000
+ one_time_keys:
+ type: object
+ description: |-
+ The keys to be claimed. A map from user ID, to a map from
+ device ID to algorithm name.
+ additionalProperties:
+ type: object
+ additionalProperties:
+ type: string
+ description: algorithm
+ example: "signed_curve25519"
+ example:
+ "@alice:example.com": { "JLAFKJWSCS": "signed_curve25519" }
+ required:
+ - one_time_keys
+ responses:
+ 200:
+ description:
+ The claimed keys
+ schema:
+ type: object
+ properties:
+ failures:
+ type: object
+ description: |-
+ If any remote homeservers could not be reached, they are
+ recorded here. The names of the properties are the names of
+ the unreachable servers.
+
+ If the homeserver could be reached, but the user or device
+ was unknown, no failure is recorded. Instead, the corresponding
+ user or device is missing from the ``one_time_keys`` result.
+ additionalProperties:
+ type: object
+ example: {}
+ one_time_keys:
+ type: object
+ description: |-
+ One-time keys for the queried devices. A map from user ID, to a
+ map from devices to a map from ``:`` to the key object.
+ additionalProperties:
+ type: object
+ additionalProperties:
+ type:
+ - string
+ - object
+ example:
+ "@alice:example.com":
+ JLAFKJWSCS:
+ signed_curve25519:AAAAHg:
+ key: "zKbLg+NrIjpnagy+pIY6uPL4ZwEG2v+8F9lmgsnlZzs"
+ signatures:
+ "@alice:example.com":
+ ed25519:JLAFKJWSCS: "FLWxXqGbwrb8SM3Y795eB6OA8bwBcoMZFXBqnTn58AYWZSqiD45tlBVcDa2L7RwdKXebW/VzDlnfVJ+9jok1Bw"
+ tags:
+ - End-to-end encryption
+ "/keys/changes":
+ get:
+ summary: Query users with recent device key updates.
+ description: |-
+ Gets a list of users who have updated their device identity keys since a
+ previous sync token.
+
+ The server should include in the results any users who:
+
+ * currently share a room with the calling user (ie, both users have
+ membership state ``join``); *and*
+ * added new device identity keys or removed an existing device with
+ identity keys, between ``from`` and ``to``.
+ operationId: getKeysChanges
+ security:
+ - accessToken: []
+ parameters:
+ - in: query
+ name: from
+ type: string
+ description: |-
+ The desired start point of the list. Should be the ``next_batch`` field
+ from a response to an earlier call to |/sync|. Users who have not
+ uploaded new device identity keys since this point, nor deleted
+ existing devices with identity keys since then, will be excluded
+ from the results.
+ required: true
+ x-example: "s72594_4483_1934"
+ - in: query
+ name: to
+ type: string
+ description: |-
+ The desired end point of the list. Should be the ``next_batch``
+ field from a recent call to |/sync| - typically the most recent
+ such call. This may be used by the server as a hint to check its
+ caches are up to date.
+ required: true
+ x-example: "s75689_5632_2435"
+ responses:
+ 200:
+ description:
+ The list of users who updated their devices.
+ schema:
+ type: object
+ properties:
+ changed:
+ type: array
+ items:
+ type: string
+ description: |-
+ The Matrix User IDs of all users who updated their device
+ identity keys.
+ example: ["@alice:example.com", "@bob:example.org"]
+ left:
+ type: array
+ items:
+ type: string
+ description: |-
+ The Matrix User IDs of all users who may have left all
+ the end-to-end encrypted rooms they previously shared
+ with the user.
+ example: ["@clara:example.com", "@doug:example.org"]
+ tags:
+ - End-to-end encryption
diff --git a/api/client-server/kicking.yaml b/api/client-server/kicking.yaml
index 137e72cf..7fbee38b 100644
--- a/api/client-server/kicking.yaml
+++ b/api/client-server/kicking.yaml
@@ -34,6 +34,11 @@ paths:
Kick a user from the room.
The caller must have the required power level in order to perform this operation.
+
+ Kicking a user adjusts the target member's membership state to be ``leave`` with an
+ optional ``reason``. 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/``.
+ operationId: kick
security:
- accessToken: []
parameters:
@@ -48,8 +53,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"reason": "Telling unfunny jokes",
"user_id": "@cheeky_monkey:matrix.org"
}
@@ -59,14 +63,16 @@ paths:
description: The fully qualified user ID of the user being kicked.
reason:
type: string
- description: The reason the user has been kicked.
+ description: |-
+ The reason the user has been kicked. This will be supplied as the
+ ``reason`` on the target's updated `m.room.member`_ event.
required: ["user_id"]
responses:
200:
description: The user has been kicked from the room.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
403:
@@ -77,10 +83,11 @@ paths:
- The kickee is not currently in the room.
- The kicker's power level is insufficient to kick users from the room.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_FORBIDDEN",
"error": "You do not have a high enough power level to kick from this room."
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- Room membership
diff --git a/api/client-server/leaving.yaml b/api/client-server/leaving.yaml
index 655308d2..513b5b4d 100644
--- a/api/client-server/leaving.yaml
+++ b/api/client-server/leaving.yaml
@@ -42,6 +42,7 @@ paths:
The user will still be allowed to retrieve history from the room which
they were previously allowed to see.
+ operationId: leaveRoom
security:
- accessToken: []
parameters:
@@ -56,14 +57,14 @@ paths:
description: |-
The room has been left.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room membership
"/rooms/{roomId}/forget":
@@ -77,8 +78,9 @@ paths:
for this room. If all users on a homeserver forget a room, the room is
eligible for deletion from that homeserver.
- If the user is currently joined to the room, they will implicitly leave
- the room as part of this API call.
+ If the user is currently joined to the room, they must leave the room
+ before calling this API.
+ operationId: forgetRoom
security:
- accessToken: []
parameters:
@@ -93,13 +95,22 @@ paths:
description: |-
The room has been forgotten.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
+ 400:
+ description: The user has not left the room
+ examples:
+ application/json: {
+ "errcode": "M_UNKNOWN",
+ "error": "User @example:matrix.org is in room !au1ba7o:matrix.org"
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room membership
diff --git a/api/client-server/list_joined_rooms.yaml b/api/client-server/list_joined_rooms.yaml
new file mode 100644
index 00000000..19ad8649
--- /dev/null
+++ b/api/client-server/list_joined_rooms.yaml
@@ -0,0 +1,58 @@
+# Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+swagger: '2.0'
+info:
+ title: "Matrix Client-Server Room Listing API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/joined_rooms":
+ get:
+ summary: Lists the user's current rooms.
+ description: |-
+ This API returns a list of the user's current rooms.
+ operationId: getJoinedRooms
+ security:
+ - accessToken: []
+ responses:
+ 200:
+ description: A list of the rooms the user is in.
+ schema:
+ type: object
+ required: ["joined_rooms"]
+ properties:
+ joined_rooms:
+ type: array
+ description: |-
+ The ID of each room in which the user has ``joined`` membership.
+ items:
+ type: string
+ examples:
+ application/json: {
+ "joined_rooms": [
+ "!foo:example.com"
+ ]
+ }
+ tags:
+ - Room membership
diff --git a/api/client-server/list_public_rooms.yaml b/api/client-server/list_public_rooms.yaml
index 3aa449d3..43239b5c 100644
--- a/api/client-server/list_public_rooms.yaml
+++ b/api/client-server/list_public_rooms.yaml
@@ -13,7 +13,7 @@
# limitations under the License.
swagger: '2.0'
info:
- title: "Matrix Client-Server Room Creation API"
+ title: "Matrix Client-Server Room Directory API"
version: "1.0.0"
host: localhost:8008
schemes:
@@ -25,6 +25,92 @@ consumes:
produces:
- application/json
paths:
+ "/directory/list/room/{roomId}":
+ get:
+ summary: Gets the visibility of a room in the directory
+ description: |-
+ Gets the visibility of a given room on the server's public room directory.
+ operationId: getRoomVisibilityOnDirectory
+ parameters:
+ - in: path
+ type: string
+ name: roomId
+ description: The room ID.
+ required: true
+ x-example: "!curbf:matrix.org"
+ responses:
+ 200:
+ description: The visibility of the room in the directory
+ schema:
+ type: object
+ properties:
+ visibility:
+ type: string
+ enum: ['private', 'public']
+ description: The visibility of the room in the directory.
+ examples:
+ application/json: {
+ "visibility": "public"
+ }
+ 404:
+ description: The room is not known to the server
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND",
+ "error": "Room not found"
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
+ put:
+ summary: Sets the visibility of a room in the room directory
+ description: |-
+ Sets the visibility of a given room in the server's public room
+ directory.
+
+ Servers may choose to implement additional access control checks
+ here, for instance that room visibility can only be changed by
+ the room creator or a server administrator.
+ operationId: setRoomVisibilityOnDirectory
+ security:
+ - accessToken: []
+ parameters:
+ - in: path
+ type: string
+ name: roomId
+ description: The room ID.
+ required: true
+ x-example: "!curbf:matrix.org"
+ - in: body
+ name: body
+ required: true
+ description: |-
+ The new visibility for the room on the room directory.
+ schema:
+ type: object
+ properties:
+ visibility:
+ type: string
+ enum: ["private", "public"]
+ description: |-
+ The new visibility setting for the room.
+ Defaults to 'public'.
+ example: {
+ "visibility": "public"
+ }
+ responses:
+ 200:
+ description: The visibility was updated, or no change was needed.
+ examples:
+ application/json: {}
+ 404:
+ description: The room is not known to the server
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND",
+ "error": "Room not found"
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
"/publicRooms":
get:
summary: Lists the public rooms on the server.
@@ -33,10 +119,11 @@ paths:
This API returns paginated responses. The rooms are ordered by the number
of joined members, with the largest rooms first.
+ operationId: getPublicRooms
parameters:
- in: query
name: limit
- type: number
+ type: integer
description: |-
Limit the number of results returned.
- in: query
@@ -57,99 +144,7 @@ paths:
200:
description: A list of the rooms on the server.
schema:
- type: object
- description: A list of the rooms on the server.
- required: ["chunk"]
- properties:
- chunk:
- title: "PublicRoomsChunks"
- type: array
- description: |-
- A paginated chunk of public rooms.
- items:
- type: object
- title: "PublicRoomsChunk"
- required:
- - room_id
- - num_joined_members
- - world_readable
- - guest_can_join
- properties:
- aliases:
- type: array
- description: |-
- Aliases of the room. May be empty.
- items:
- type: string
- canonical_alias:
- type: string
- description: |-
- The canonical alias of the room, if any.
- name:
- type: string
- description: |-
- The name of the room, if any.
- num_joined_members:
- type: number
- description: |-
- The number of members joined to the room.
- room_id:
- type: string
- description: |-
- The ID of the room.
- topic:
- type: string
- description: |-
- The topic of the room, if any.
- world_readable:
- type: boolean
- description: |-
- Whether the room may be viewed by guest users without joining.
- guest_can_join:
- type: boolean
- description: |-
- Whether guest users may join the room and participate in it.
- If they can, they will be subject to ordinary power level
- rules like any other user.
- avatar_url:
- type: string
- description: The URL for the room's avatar, if one is set.
- next_batch:
- type: string
- description: |-
- A pagination token for the response. The absence of this token
- means there are no more results to fetch and the client should
- stop paginating.
- prev_batch:
- type: string
- description: |-
- A pagination token that allows fetching previous results. The
- absence of this token means there are no results before this
- batch, i.e. this is the first batch.
- total_room_count_estimate:
- type: number
- description: |-
- An estimate on the total number of public rooms, if the
- server has an estimate.
- examples:
- application/json: |-
- {
- "chunk": [
- {
- "aliases": ["#murrays:cheese.bar"],
- "avatar_url": "mxc://bleeker.street/CHEDDARandBRIE",
- "guest_can_join": false,
- "name": "CHEESE",
- "num_joined_members": 37,
- "room_id": "!ol19s:bleecker.street",
- "topic": "Tasty tasty cheese",
- "world_readable": true
- }
- ],
- "next_batch": "p190q",
- "prev_batch": "p1902",
- "total_room_count_estimate": 115
- }
+ $ref: "definitions/public_rooms_response.yaml"
tags:
- Room discovery
post:
@@ -159,6 +154,7 @@ paths:
This API returns paginated responses. The rooms are ordered by the number
of joined members, with the largest rooms first.
+ operationId: queryPublicRooms
security:
- accessToken: []
parameters:
@@ -177,7 +173,7 @@ paths:
type: object
properties:
limit:
- type: number
+ type: integer
description: |-
Limit the number of results returned.
since:
@@ -198,8 +194,26 @@ paths:
description: |-
A string to search for in the room metadata, e.g. name,
topic, canonical alias etc. (Optional).
- example: |-
- {"limit": 10, "filter": {"generic_search_term": "foo"}}
+ include_all_networks:
+ type: boolean
+ description: |-
+ Whether or not to include all known networks/protocols from
+ application services on the homeserver. Defaults to false.
+ example: false
+ third_party_instance_id:
+ type: string
+ description: |-
+ The specific third party network/protocol to request from the
+ homeserver. Can only be used if ``include_all_networks`` is false.
+ example: "irc"
+ example: {
+ "limit": 10,
+ "filter": {
+ "generic_search_term": "foo"
+ },
+ "include_all_networks": false,
+ "third_party_instance_id": "irc"
+ }
responses:
200:
description: A list of the rooms on the server.
@@ -237,7 +251,7 @@ paths:
description: |-
The name of the room, if any.
num_joined_members:
- type: number
+ type: integer
description: |-
The number of members joined to the room.
room_id:
@@ -274,13 +288,12 @@ paths:
absence of this token means there are no results before this
batch, i.e. this is the first batch.
total_room_count_estimate:
- type: number
+ type: integer
description: |-
An estimate on the total number of public rooms, if the
server has an estimate.
examples:
- application/json: |-
- {
+ application/json: {
"chunk": [
{
"aliases": ["#murrays:cheese.bar"],
diff --git a/api/client-server/login.yaml b/api/client-server/login.yaml
index 8c0282be..43aae5df 100644
--- a/api/client-server/login.yaml
+++ b/api/client-server/login.yaml
@@ -1,4 +1,5 @@
# 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.
@@ -28,6 +29,42 @@ securityDefinitions:
$ref: definitions/security.yaml
paths:
"/login":
+ get:
+ summary: Get the supported login types to authenticate users
+ description: |-
+ Gets the homeserver's supported login types to authenticate users. Clients
+ should pick one of these and supply it as the ``type`` when logging in.
+ operationId: getLoginFlows
+ responses:
+ 200:
+ description: The login types the homeserver supports
+ examples:
+ application/json: {
+ "flows": [
+ {"type": "m.login.password"}
+ ]
+ }
+ schema:
+ type: object
+ properties:
+ flows:
+ type: array
+ description: The homeserver's supported login types
+ items:
+ type: object
+ title: LoginFlow
+ properties:
+ type:
+ description: |-
+ The login type. This is supplied as the ``type`` when
+ logging in.
+ type: string
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
+ tags:
+ - Session management
post:
summary: Authenticates the user.
description: |-
@@ -41,15 +78,18 @@ paths:
supplied by the client or generated by the server. The server may
invalidate any access token previously associated with that device. See
`Relationship between access tokens and devices`_.
+ operationId: login
parameters:
- in: body
name: body
schema:
type: object
- example: |-
- {
+ example: {
"type": "m.login.password",
- "user": "cheeky_monkey",
+ "identifier": {
+ "type": "m.id.user",
+ "user": "cheeky_monkey"
+ },
"password": "ilovebananas",
"initial_device_display_name": "Jungle Phone"
}
@@ -58,15 +98,18 @@ paths:
type: string
enum: ["m.login.password", "m.login.token"]
description: The login type being used.
+ identifier:
+ description: Identification information for the user.
+ "$ref": "definitions/user_identifier.yaml"
user:
type: string
- description: The fully qualified user ID or just local part of the user ID, to log in.
+ description: The fully qualified user ID or just local part of the user ID, to log in. Deprecated in favour of ``identifier``.
medium:
type: string
- description: When logging in using a third party identifier, the medium of the identifier. Must be 'email'.
+ description: When logging in using a third party identifier, the medium of the identifier. Must be 'email'. Deprecated in favour of ``identifier``.
address:
type: string
- description: Third party identifier for the user.
+ description: Third party identifier for the user. Deprecated in favour of ``identifier``.
password:
type: string
description: |-
@@ -75,7 +118,7 @@ paths:
token:
type: string
description: |-
- Required when ``type`` is ``m.login.token``. The login token.
+ Required when ``type`` is ``m.login.token``. Part of `Token-based`_ login.
device_id:
type: string
description: |-
@@ -93,11 +136,9 @@ paths:
200:
description: The user has been authenticated.
examples:
- application/json: |-
- {
+ application/json: {
"user_id": "@cheeky_monkey:matrix.org",
"access_token": "abc123",
- "home_server": "matrix.org",
"device_id": "GHTYAJCE"
}
schema:
@@ -113,7 +154,13 @@ paths:
This access token can then be used to authorize other requests.
home_server:
type: string
- description: The hostname of the homeserver on which the account has been registered.
+ description: |-
+ The server_name of the homeserver on which the account has
+ been registered.
+
+ **Deprecated**. Clients should extract the server_name from
+ ``user_id`` (by splitting at the first colon) if they require
+ it. Note also that ``homeserver`` is not spelt this way.
device_id:
type: string
description: |-
@@ -123,20 +170,23 @@ paths:
description: |-
Part of the request was invalid. For example, the login type may not be recognised.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_UNKNOWN",
"error": "Bad login type."
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
403:
description: |-
The login attempt failed. For example, the password may have been incorrect.
examples:
- application/json: |-
- {"errcode": "M_FORBIDDEN"}
+ application/json: {
+ "errcode": "M_FORBIDDEN"}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Session management
diff --git a/api/client-server/logout.yaml b/api/client-server/logout.yaml
index eed4ee44..2dfd6d97 100644
--- a/api/client-server/logout.yaml
+++ b/api/client-server/logout.yaml
@@ -33,6 +33,7 @@ paths:
description: |-
Invalidates an existing access token, so that it can no longer be used for
authorization.
+ operationId: logout
security:
- accessToken: []
responses:
@@ -43,3 +44,26 @@ paths:
properties: {}
tags:
- Session management
+ "/logout/all":
+ post:
+ summary: Invalidates all access tokens for a user
+ description: |-
+ Invalidates all access tokens for a user, so that they can no longer be used for
+ authorization. This includes the access token that made this request.
+
+ This endpoint does not require UI authorization because UI authorization is
+ designed to protect against attacks where the someone gets hold of a single access
+ token then takes over the account. This endpoint invalidates all access tokens for
+ the user, including the token used in the request, and therefore the attacker is
+ unable to take over the account in this way.
+ operationId: logout_all
+ security:
+ - accessToken: []
+ responses:
+ 200:
+ description: The user's access tokens were succesfully invalidated.
+ schema:
+ type: object
+ properties: {}
+ tags:
+ - Session management
diff --git a/api/client-server/message_pagination.yaml b/api/client-server/message_pagination.yaml
index 005b8fb3..941e61fb 100644
--- a/api/client-server/message_pagination.yaml
+++ b/api/client-server/message_pagination.yaml
@@ -33,6 +33,7 @@ paths:
description: |-
This API returns a list of message and state events for a room. It uses
pagination query parameters to paginate history in the room.
+ operationId: getRoomEvents
security:
- accessToken: []
parameters:
@@ -106,9 +107,9 @@ paths:
items:
type: object
title: RoomEvent
+ "$ref": "definitions/event-schemas/schema/core-event-schema/room_event.yaml"
examples:
- application/json: |-
- {
+ application/json: {
"start": "t47429-4392820_219380_26003_2265",
"end": "t47409-4357353_219380_26003_2265",
"chunk": [
diff --git a/api/client-server/notifications.yaml b/api/client-server/notifications.yaml
index 8a5e9553..b450885b 100644
--- a/api/client-server/notifications.yaml
+++ b/api/client-server/notifications.yaml
@@ -34,6 +34,7 @@ paths:
description: |-
This API is used to paginate through the list of events that the
user has been, or would have been notified about.
+ operationId: getNotifications
security:
- accessToken: []
parameters:
@@ -44,7 +45,7 @@ paths:
required: false
x-example: "xxxxx"
- in: query
- type: number
+ type: integer
name: limit
description: Limit on the number of events to return in this request.
required: false
@@ -62,8 +63,7 @@ paths:
200:
description: A batch of events is being returned
examples:
- application/json: |-
- {
+ application/json: {
"next_token": "abcdef",
"notifications": [
{
diff --git a/api/client-server/old_sync.yaml b/api/client-server/old_sync.yaml
index e3e1ea8a..c502c239 100644
--- a/api/client-server/old_sync.yaml
+++ b/api/client-server/old_sync.yaml
@@ -38,6 +38,7 @@ paths:
should instead call the |/sync|_ API with a ``since`` parameter. See
the `migration guide
`_.
+ operationId: getEvents
security:
- accessToken: []
parameters:
@@ -59,8 +60,7 @@ paths:
200:
description: "The events received, which may be none."
examples:
- application/json: |-
- {
+ application/json: {
"start": "s3456_9_0",
"end": "s3457_9_0",
"chunk": [
@@ -115,6 +115,7 @@ paths:
should instead call the |/sync|_ API with no ``since`` parameter. See
the `migration guide
`_.
+ operationId: initialSync
security:
- accessToken: []
parameters:
@@ -138,8 +139,7 @@ paths:
200:
description: The user's current state.
examples:
- application/json: |-
- {
+ application/json: {
"end": "s3456_9_0",
"presence": [
{
@@ -407,7 +407,9 @@ paths:
retrieve this event e.g. by being a member in the room for this event.
This endpoint was deprecated in r0 of this specification. Clients
- should instead call the |/rooms/{roomId}/context/{eventId}|_ API.
+ should instead call the |/rooms/{roomId}/event/{eventId}|_ API
+ or the |/rooms/{roomId}/context/{eventId}|_ API.
+ operationId: getOneEvent
security:
- accessToken: []
parameters:
@@ -421,8 +423,7 @@ paths:
200:
description: The full event.
examples:
- application/json: |-
- {
+ application/json: {
"content": {
"body": "Hello world!",
"msgtype": "m.text"
diff --git a/api/client-server/openid.yaml b/api/client-server/openid.yaml
new file mode 100644
index 00000000..cb982fb3
--- /dev/null
+++ b/api/client-server/openid.yaml
@@ -0,0 +1,103 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Client-Server OpenID API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/user/{userId}/openid/request_token":
+ post:
+ summary: Get an OpenID token object to verify the requester's identity.
+ description: |-
+ Gets an OpenID token object that the requester may supply to another
+ service to verify their identity in Matrix. The generated token is only
+ valid for exchanging for user information from the federation API for
+ OpenID.
+
+ The access token generated is only valid for the OpenID API. It cannot
+ be used to request another OpenID access token or call ``/sync``, for
+ example.
+ operationId: requestOpenIdToken
+ security:
+ - accessToken: []
+ parameters:
+ - in: path
+ type: string
+ name: userId
+ description: |-
+ The user to request and OpenID token for. Should be the user who
+ is authenticated for the request.
+ required: true
+ x-example: "@alice:example.com"
+ - in: body
+ name: body
+ description: An empty object. Reserved for future expansion.
+ required: true
+ schema:
+ type: object
+ example: {}
+ responses:
+ 200:
+ description: |-
+ OpenID token information. This response is nearly compatible with the
+ response documented in the `OpenID 1.0 Specification `_
+ with the only difference being the lack of an ``id_token``. Instead,
+ the Matrix homeserver's name is provided.
+ examples:
+ application/json: {
+ "access_token": "SomeT0kenHere",
+ "token_type": "Bearer",
+ "matrix_server_name": "example.com",
+ "expires_in": 3600,
+ }
+ schema:
+ type: object
+ properties:
+ access_token:
+ type: string
+ description: |-
+ An access token the consumer may use to verify the identity of
+ the person who generated the token. This is given to the federation
+ API ``GET /openid/userinfo``.
+ token_type:
+ type: string
+ description: The string ``Bearer``.
+ matrix_server_name:
+ type: string
+ description: |-
+ The homeserver domain the consumer should use when attempting to
+ verify the user's identity.
+ expires_in:
+ type: integer
+ description: |-
+ The number of seconds before this token expires and a new one must
+ be generated.
+ required: ['access_token', 'token_type', 'matrix_server_name', 'expires_in']
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
+ tags:
+ - OpenID
diff --git a/api/client-server/peeking_events.yaml b/api/client-server/peeking_events.yaml
index e33a4ce3..2f66bae7 100644
--- a/api/client-server/peeking_events.yaml
+++ b/api/client-server/peeking_events.yaml
@@ -41,6 +41,7 @@ paths:
Note that the normal ``/events`` endpoint has been deprecated. This
API will also be deprecated at some point, but its replacement is not
yet known.
+ operationId: peekEvents
security:
- accessToken: []
parameters:
@@ -69,8 +70,7 @@ paths:
200:
description: "The events received, which may be none."
examples:
- application/json: |-
- {
+ application/json: {
"start": "s3456_9_0",
"end": "s3457_9_0",
"chunk": [
diff --git a/api/client-server/presence.yaml b/api/client-server/presence.yaml
index 7a963067..91b75c6a 100644
--- a/api/client-server/presence.yaml
+++ b/api/client-server/presence.yaml
@@ -35,6 +35,7 @@ paths:
the activity time is updated to reflect that activity; the client does
not need to specify the ``last_active_ago`` field. You cannot set the
presence state of another user.
+ operationId: setPresence
security:
- accessToken: []
parameters:
@@ -50,8 +51,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"presence": "online",
"status_msg": "I am here."
}
@@ -68,20 +68,23 @@ paths:
200:
description: The new presence state was set.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Presence
get:
summary: Get this user's presence state.
description: |-
Get the given user's presence state.
+ operationId: getPresence
+ security:
+ - accessToken: []
parameters:
- in: path
type: string
@@ -93,8 +96,7 @@ paths:
200:
description: The presence state for this user.
examples:
- application/json: |-
- {
+ application/json: {
"presence": "unavailable",
"last_active_ago": 420845
}
@@ -121,6 +123,17 @@ paths:
description: |-
There is no presence state for this user. This user may not exist or
isn't exposing presence information to you.
+ schema:
+ "$ref": "definitions/errors/error.yaml"
+ 403:
+ description: You are not allowed to see this user's presence status.
+ examples:
+ application/json: {
+ "errcode": "M_FORBIDDEN",
+ "error": "You are not allowed to see their presence"
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
tags:
- Presence
"/presence/list/{userId}":
@@ -128,6 +141,7 @@ paths:
summary: Add or remove users from this presence list.
description: |-
Adds or removes users from this presence list.
+ operationId: modifyPresenceList
security:
- accessToken: []
parameters:
@@ -143,8 +157,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"invite": [
"@bob:matrix.org"
],
@@ -169,20 +182,21 @@ paths:
200:
description: The list was updated.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Presence
get:
summary: Get presence events for this presence list.
description: |-
Retrieve a list of presence events for every user on this list.
+ operationId: getPresenceForList
parameters:
- in: path
type: string
@@ -194,8 +208,7 @@ paths:
200:
description: A list of presence events for this list.
examples:
- application/json: |-
- [
+ application/json: [
{
"content": {
"last_active_ago": 395,
diff --git a/api/client-server/profile.yaml b/api/client-server/profile.yaml
index 89ebdc2a..c8dc4056 100644
--- a/api/client-server/profile.yaml
+++ b/api/client-server/profile.yaml
@@ -33,6 +33,7 @@ paths:
description: |-
This API sets the given user's display name. You must have permission to
set this user's display name, e.g. you need to have their ``access_token``.
+ operationId: setDisplayName
security:
- accessToken: []
parameters:
@@ -48,8 +49,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"displayname": "Alice Margatroid"
}
properties:
@@ -60,14 +60,14 @@ paths:
200:
description: The display name was set.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
get:
@@ -76,6 +76,7 @@ paths:
Get the user's display name. This API may be used to fetch the user's
own displayname or to query the name of other users; either locally or
on remote homeservers.
+ operationId: getDisplayName
parameters:
- in: path
type: string
@@ -87,8 +88,7 @@ paths:
200:
description: The display name for this user.
examples:
- application/json: |-
- {
+ application/json: {
"displayname": "Alice Margatroid"
}
schema:
@@ -107,6 +107,7 @@ paths:
description: |-
This API sets the given user's avatar URL. You must have permission to
set this user's avatar URL, e.g. you need to have their ``access_token``.
+ operationId: setAvatarUrl
security:
- accessToken: []
parameters:
@@ -122,8 +123,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"avatar_url": "mxc://matrix.org/wefh34uihSDRGhw34"
}
properties:
@@ -134,14 +134,14 @@ paths:
200:
description: The avatar URL was set.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
get:
@@ -150,6 +150,7 @@ paths:
Get the user's avatar URL. This API may be used to fetch the user's
own avatar URL or to query the URL of other users; either locally or
on remote homeservers.
+ operationId: getAvatarUrl
parameters:
- in: path
type: string
@@ -161,8 +162,7 @@ paths:
200:
description: The avatar URL for this user.
examples:
- application/json: |-
- {
+ application/json: {
"avatar_url": "mxc://matrix.org/SDGdghriugerRg"
}
schema:
@@ -183,6 +183,7 @@ paths:
to fetch the user's own profile information or other users; either
locally or on remote homeservers. This API may return keys which are not
limited to ``displayname`` or ``avatar_url``.
+ operationId: getUserProfile
parameters:
- in: path
type: string
@@ -194,8 +195,7 @@ paths:
200:
description: The avatar URL for this user.
examples:
- application/json: |-
- {
+ application/json: {
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
"displayname": "Alice Margatroid"
}
diff --git a/api/client-server/pusher.yaml b/api/client-server/pusher.yaml
index 5807b007..d232baf9 100644
--- a/api/client-server/pusher.yaml
+++ b/api/client-server/pusher.yaml
@@ -1,4 +1,5 @@
# 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.
@@ -31,30 +32,30 @@ paths:
get:
summary: Gets the current pushers for the authenticated user
description: |-
- Gets all currently active pushers for the authenticated user
+ Gets all currently active pushers for the authenticated user.
+ operationId: getPushers
security:
- accessToken: []
responses:
200:
- description: The pushers for this user
+ description: The pushers for this user.
examples:
- application/json: |-
- {
- "pushers": [
- {
- "pushkey": "Xp/MzCt8/9DcSNE9cuiaoT5Ac55job3TdLSSmtmYl4A=",
- "kind": "http",
- "app_id": "face.mcapp.appy.prod",
- "app_display_name": "Appy McAppface",
- "device_display_name": "Alice's Phone",
- "profile_tag": "xyz",
- "lang": "en-US",
- "data": {
- "url": "https://example.com/_matrix/push/v1/notify"
- }
+ application/json: {
+ "pushers": [
+ {
+ "pushkey": "Xp/MzCt8/9DcSNE9cuiaoT5Ac55job3TdLSSmtmYl4A=",
+ "kind": "http",
+ "app_id": "face.mcapp.appy.prod",
+ "app_display_name": "Appy McAppface",
+ "device_display_name": "Alice's Phone",
+ "profile_tag": "xyz",
+ "lang": "en-US",
+ "data": {
+ "url": "https://example.com/_matrix/push/v1/notify"
}
- ]
- }
+ }
+ ]
+ }
schema:
type: object
properties:
@@ -70,7 +71,7 @@ paths:
pushkey:
type: string
description: |-
- This is a unique identifier for this pusher. See `/set` for
+ This is a unique identifier for this pusher. See ``/set`` for
more detail.
Max length, 512 bytes.
kind:
@@ -115,6 +116,19 @@ paths:
description: |-
Required if ``kind`` is ``http``. The URL to use to send
notifications to.
+ format:
+ type: string
+ description: |-
+ The format to use when sending notifications to the Push
+ Gateway.
+ required:
+ - pushkey
+ - app_id
+ - kind
+ - app_display_name
+ - device_display_name
+ - lang
+ - data
tags:
- Push notifications
"/pushers/set":
@@ -124,29 +138,30 @@ paths:
This endpoint allows the creation, modification and deletion of `pushers`_
for this user ID. The behaviour of this endpoint varies depending on the
values in the JSON body.
+ operationId: postPusher
security:
- accessToken: []
parameters:
- in: body
name: pusher
- description: The pusher information
+ description: The pusher information.
required: true
schema:
type: object
- example: |-
- {
- "lang": "en",
- "kind": "http",
- "app_display_name": "Mat Rix",
- "device_display_name": "iPhone 9",
- "profile_tag": "xxyyzz",
- "app_id": "com.example.app.ios",
- "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ",
- "data": {
- "url": "https://push-gateway.location.here"
- },
- "append": false
- }
+ example: {
+ "lang": "en",
+ "kind": "http",
+ "app_display_name": "Mat Rix",
+ "device_display_name": "iPhone 9",
+ "profile_tag": "xxyyzz",
+ "app_id": "com.example.app.ios",
+ "pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ",
+ "data": {
+ "url": "https://push-gateway.location.here/_matrix/push/v1/notify",
+ "format": "event_id_only"
+ },
+ "append": false
+ }
properties:
pushkey:
type: string
@@ -157,11 +172,15 @@ paths:
for APNS or the Registration ID for GCM. If your notification
client has no such concept, use any unique identifier.
Max length, 512 bytes.
+
+ If the ``kind`` is ``"email"``, this is the email address to
+ send notifications to.
kind:
type: string
description: |-
The kind of pusher to configure. ``"http"`` makes a pusher that
- sends HTTP pokes. ``null`` deletes the pusher.
+ sends HTTP pokes. ``"email"`` makes a pusher that emails the
+ user with unread notifications. ``null`` deletes the pusher.
app_id:
type: string
description: |-
@@ -169,6 +188,8 @@ paths:
It is recommended that this end with the platform, such that
different platform versions get different app identifiers.
Max length, 64 chars.
+
+ If the ``kind`` is ``"email"``, this is ``"m.email"``.
app_display_name:
type: string
description: |-
@@ -188,7 +209,7 @@ paths:
type: string
description: |-
The preferred language for receiving notifications (e.g. 'en'
- or 'en-US')
+ or 'en-US').
data:
type: object
description: |-
@@ -201,7 +222,17 @@ paths:
type: string
description: |-
Required if ``kind`` is ``http``. The URL to use to send
- notifications to.
+ notifications to. MUST be an HTTPS URL with a path of
+ ``/_matrix/push/v1/notify``.
+ example: "https://push-gateway.location.here/_matrix/push/v1/notify"
+ format:
+ type: string
+ description: |-
+ The format to send notifications in to Push Gateways if the
+ ``kind`` is ``http``. The details about what fields the
+ homeserver should send to the push gateway are defined in the
+ `Push Gateway Specification`_. Currently the only format
+ available is 'event_id_only'.
append:
type: boolean
description: |-
@@ -216,23 +247,22 @@ paths:
200:
description: The pusher was set.
examples:
- application/json: |-
- {}
+ application/json: {}
schema:
- type: object # empty json object
+ type: object
+ description: An empty object.
400:
description: One or more of the pusher values were invalid.
examples:
- application/json: |-
- {
- "error": "Missing parameters: lang, data",
- "errcode": "M_MISSING_PARAM"
- }
+ application/json: {
+ "error": "Missing parameters: lang, data",
+ "errcode": "M_MISSING_PARAM"
+ }
schema:
- type: object
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Push notifications
diff --git a/api/client-server/pushrules.yaml b/api/client-server/pushrules.yaml
index 0ba8d481..e23c9189 100644
--- a/api/client-server/pushrules.yaml
+++ b/api/client-server/pushrules.yaml
@@ -35,6 +35,7 @@ paths:
the rulesets by suffixing a ``scope`` to this path e.g.
``/pushrules/global/``. This will return a subset of this data under the
specified key e.g. the ``global`` key.
+ operationId: getPushRules
security:
- accessToken: []
responses:
@@ -52,8 +53,7 @@ paths:
"$ref": "definitions/push_ruleset.yaml"
]
examples:
- application/json: |-
- {
+ application/json: {
"global": {
"content": [
{
@@ -248,6 +248,7 @@ paths:
summary: Retrieve a push rule.
description: |-
Retrieve a single specified push rule.
+ operationId: getPushRule
security:
- accessToken: []
parameters:
@@ -279,8 +280,7 @@ paths:
The specific push rule. This will also include keys specific to the
rule itself such as the rule's ``actions`` and ``conditions`` if set.
examples:
- application/json: |-
- {
+ application/json: {
"actions": [
"dont_notify"
],
@@ -301,6 +301,7 @@ paths:
summary: Delete a push rule.
description: |-
This endpoint removes the push rule defined in the path.
+ operationId: deletePushRule
security:
- accessToken: []
parameters:
@@ -330,8 +331,8 @@ paths:
200:
description: The push rule was deleted.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
tags:
@@ -342,6 +343,9 @@ paths:
This endpoint allows the creation, modification and deletion of pushers
for this user ID. The behaviour of this endpoint varies depending on the
values in the JSON body.
+
+ When creating push rules, they MUST be enabled by default.
+ operationId: setPushRule
security:
- accessToken: []
parameters:
@@ -393,8 +397,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"pattern": "cake*lie",
"actions": ["notify"]
}
@@ -423,26 +426,25 @@ paths:
required: ["actions"]
responses:
200:
- description: The pusher was set.
+ description: The push rule was created/updated.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
400:
description: There was a problem configuring this push rule.
examples:
- application/json: |-
- {
+ application/json: {
"error": "before/after rule not found: someRuleId",
"errcode": "M_UNKNOWN"
}
schema:
- type: object
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Push notifications
"/pushrules/{scope}/{kind}/{ruleId}/enabled":
@@ -450,6 +452,7 @@ paths:
summary: "Get whether a push rule is enabled"
description:
This endpoint gets whether the specified push rule is enabled.
+ operationId: isPushRuleEnabled
security:
- accessToken: []
parameters:
@@ -480,8 +483,7 @@ paths:
200:
description: Whether the push rule is enabled.
examples:
- application/json: |-
- {
+ application/json: {
"enabled": true
}
schema:
@@ -495,6 +497,7 @@ paths:
summary: "Enable or disable a push rule."
description: |-
This endpoint allows clients to enable or disable the specified push rule.
+ operationId: setPushRuleEnabled
security:
- accessToken: []
parameters:
@@ -532,16 +535,15 @@ paths:
type: boolean
description: Whether the push rule is enabled or not.
required: ["enabled"]
- example: |-
- {
+ example: {
"enabled": true
}
responses:
200:
description: The push rule was enabled or disabled.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
tags:
@@ -551,6 +553,7 @@ paths:
summary: "The actions for a push rule"
description:
This endpoint get the actions for the specified push rule.
+ operationId: getPushRuleActions
security:
- accessToken: []
parameters:
@@ -581,8 +584,7 @@ paths:
200:
description: The actions for this push rule.
examples:
- application/json: |-
- {
+ application/json: {
"actions": ["notify"]
}
schema:
@@ -599,6 +601,7 @@ paths:
description: |-
This endpoint allows clients to change the actions of a push rule.
This can be used to change the actions of builtin rules.
+ operationId: setPushRuleActions
security:
- accessToken: []
parameters:
@@ -640,16 +643,15 @@ paths:
enum: ["notify", "dont_notify", "coalesce", "set_tweak"]
# TODO: type: object e.g. {"set_sound":"beeroclock.wav"} :/
required: ["actions"]
- example: |-
- {
+ example: {
"actions": ["notify"]
}
responses:
200:
description: The actions for the push rule were set.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
tags:
diff --git a/api/client-server/receipts.yaml b/api/client-server/receipts.yaml
index 9bd7f56c..a3e9789e 100644
--- a/api/client-server/receipts.yaml
+++ b/api/client-server/receipts.yaml
@@ -33,6 +33,7 @@ paths:
description: |-
This API updates the marker for the given receipt type to the event ID
specified.
+ operationId: postReceipt
security:
- accessToken: []
parameters:
@@ -62,19 +63,19 @@ paths:
server will automatically set the ``ts`` field.
schema:
type: object
- example: |-
- {}
+ example: {
+ }
responses:
200:
description: The receipt was sent.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room participation
diff --git a/api/client-server/redaction.yaml b/api/client-server/redaction.yaml
index df60f817..58141049 100644
--- a/api/client-server/redaction.yaml
+++ b/api/client-server/redaction.yaml
@@ -39,6 +39,7 @@ paths:
Users may redact their own events, and any user with a power level
greater than or equal to the `redact` power level of the room may
redact events there.
+ operationId: redactEvent
security:
- accessToken: []
parameters:
@@ -66,8 +67,7 @@ paths:
name: body
schema:
type: object
- example: |-
- {
+ example: {
"reason": "Indecent material"
}
properties:
@@ -78,9 +78,8 @@ paths:
200:
description: "An ID for the redaction event."
examples:
- application/json: |-
- {
- "event_id": "YUwQidLecu"
+ application/json: {
+ "event_id": "$YUwQidLecu:example.com"
}
schema:
type: object
diff --git a/api/client-server/registration.yaml b/api/client-server/registration.yaml
index d09cd431..e4b05629 100644
--- a/api/client-server/registration.yaml
+++ b/api/client-server/registration.yaml
@@ -45,10 +45,21 @@ paths:
If the client does not supply a ``device_id``, the server must
auto-generate one.
+ The server SHOULD register an account with a User ID based on the
+ ``username`` provided, if any. Note that the grammar of Matrix User ID
+ localparts is restricted, so the server MUST either map the provided
+ ``username`` onto a ``user_id`` in a logical manner, or reject
+ ``username``\s which do not comply to the grammar, with
+ ``M_INVALID_USERNAME``.
+
+ Matrix clients MUST NOT assume that localpart of the registered
+ ``user_id`` matches the provided ``username``.
+
The returned access token must be associated with the ``device_id``
supplied by the client or generated by the server. The server may
invalidate any access token previously associated with that device. See
`Relationship between access tokens and devices`_.
+ operationId: register
parameters:
- in: query
name: kind
@@ -86,7 +97,7 @@ paths:
username:
type: string
description: |-
- The local part of the desired Matrix ID. If omitted,
+ The basis for the localpart of the desired Matrix ID. If omitted,
the homeserver MUST generate a Matrix ID local part.
example: cheeky_monkey
password:
@@ -110,11 +121,9 @@ paths:
200:
description: The account has been registered.
examples:
- application/json: |-
- {
+ application/json: {
"user_id": "@cheeky_monkey:matrix.org",
"access_token": "abc123",
- "home_server": "matrix.org",
"device_id": "GHTYAJCE"
}
schema:
@@ -122,7 +131,11 @@ paths:
properties:
user_id:
type: string
- description: The fully-qualified Matrix ID that has been registered.
+ description: |-
+ The fully-qualified Matrix user ID (MXID) that has been registered.
+
+ Any user ID returned by this API must conform to the grammar given in the
+ `Matrix specification `_.
access_token:
type: string
description: |-
@@ -130,7 +143,13 @@ paths:
This access token can then be used to authorize other requests.
home_server:
type: string
- description: The hostname of the homeserver on which the account has been registered.
+ description: |-
+ The server_name of the homeserver on which the account has
+ been registered.
+
+ **Deprecated**. Clients should extract the server_name from
+ ``user_id`` (by splitting at the first colon) if they require
+ it. Note also that ``homeserver`` is not spelt this way.
device_id:
type: string
description: |-
@@ -154,11 +173,12 @@ paths:
them after authentication is completed if, for example, the requested user ID
was registered whilst the client was performing authentication.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_USER_IN_USE",
"error": "Desired user ID is already taken."
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
401:
description: |-
The homeserver requires additional authentication information.
@@ -167,7 +187,7 @@ paths:
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
"/register/email/requestToken":
@@ -176,10 +196,9 @@ paths:
description: |-
Proxies the identity server API ``validate/email/requestToken``, but
first checks that the given email address is not already associated
- with an account on this Home Server. Note that, for consistency,
- this API takes JSON objects, though the Identity Server API takes
- ``x-www-form-urlencoded`` parameters. See the Identity Server API for
+ with an account on this Home Server. See the Identity Server API for
further information.
+ operationId: requestTokenToRegisterEmail
parameters:
- in: body
name: body
@@ -199,9 +218,9 @@ paths:
description: The email address
example: "example@example.com"
send_attempt:
- type: number
+ type: integer
description: Used to distinguish protocol level retries from requests to re-send the email.
- example: "1"
+ example: 1
required: ["client_secret", "email", "send_attempt"]
responses:
200:
@@ -210,7 +229,7 @@ paths:
Note that this may be an email containing the validation token or it may be informing
the user of an error.
examples:
- application/json: "{}"
+ application/json: {}
schema:
type: object
400:
@@ -225,13 +244,77 @@ paths:
* ``M_SERVER_NOT_TRUSTED`` : The ``id_server`` parameter refers to an ID server
that is not trusted by this Home Server.
examples:
- application/json: |-
- {
+ application/json: {
"errcode": "M_THREEPID_IN_USE",
"error": "The specified address is already in use"
}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
+ "/register/msisdn/requestToken":
+ post:
+ summary: Requests a validation token be sent to the given phone number for the purpose of registering an account
+ description: |-
+ Proxies the identity server API ``validate/msisdn/requestToken``, but
+ first checks that the given phone number is not already associated
+ with an account on this Home Server. See the Identity Server API for
+ further information.
+ operationId: requestTokenToRegisterMSISDN
+ parameters:
+ - in: body
+ name: body
schema:
type: object
+ properties:
+ id_server:
+ type: string
+ description: The ID server to send the onward request to as a hostname with an appended colon and port number if the port is not the default.
+ example: "id.matrix.org"
+ client_secret:
+ type: string
+ description: Client-generated secret string used to protect this session.
+ example: "this_is_my_secret_string"
+ country:
+ type: string
+ description: |-
+ The two-letter uppercase ISO country code that the number in
+ ``phone_number`` should be parsed as if it were dialled from.
+ phone_number:
+ type: string
+ description: The phone number.
+ example: "example@example.com"
+ send_attempt:
+ type: integer
+ description: Used to distinguish protocol level retries from requests to re-send the SMS message.
+ example: 1
+ required: ["client_secret", "country", "phone_number", "send_attempt"]
+ responses:
+ 200:
+ description: |-
+ An SMS message has been sent to the specified phone number.
+ Note that this may be an SMS message containing the validation token or it may be informing
+ the user of an error.
+ examples:
+ application/json: {}
+ schema:
+ type: object
+ 400:
+ description: |-
+ Part of the request was invalid. This may include one of the following error codes:
+
+ * ``M_THREEPID_IN_USE`` : The phone number is already registered to an account on this server.
+ However, if the home server has the ability to send SMS message, it is recommended that the server
+ instead send an SMS message to the user with instructions on how to reset their password.
+ This prevents malicious parties from being able to determine if a given phone number
+ has an account on the Home Server in question.
+ * ``M_SERVER_NOT_TRUSTED`` : The ``id_server`` parameter refers to an ID server
+ that is not trusted by this Home Server.
+ examples:
+ application/json: {
+ "errcode": "M_THREEPID_IN_USE",
+ "error": "The specified address is already in use"
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
"/account/password":
post:
summary: "Changes a user's password."
@@ -247,6 +330,7 @@ paths:
valid access token is provided.
security:
- accessToken: []
+ operationId: changePassword
parameters:
- in: body
name: body
@@ -266,7 +350,7 @@ paths:
200:
description: The password has been changed.
examples:
- application/json: "{}"
+ application/json: {}
schema:
type: object
401:
@@ -277,7 +361,7 @@ paths:
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
"/account/password/email/requestToken":
@@ -298,9 +382,32 @@ paths:
.. |/register/email/requestToken| replace:: ``/register/email/requestToken``
.. _/register/email/requestToken: #post-matrix-client-%CLIENT_MAJOR_VERSION%-register-email-requesttoken
+ operationId: requestTokenToResetPasswordEmail
responses:
200:
description: An email was sent to the given address
+ "/account/password/msisdn/requestToken":
+ post:
+ summary: Requests a validation token be sent to the given phone number for the purpose of resetting a user's password.
+ description: |-
+ Proxies the identity server API ``validate/msisdn/requestToken``, but
+ first checks that the given phone number **is** associated with an account
+ on this Home Server. This API should be used to request
+ validation tokens when authenticating for the
+ `account/password` endpoint. This API's parameters and response are
+ identical to that of the HS API |/register/msisdn/requestToken|_ except that
+ `M_THREEPID_NOT_FOUND` may be returned if no account matching the
+ given email address could be found. The server may instead send an
+ SMS message to the given address prompting the user to create an account.
+ `M_THREEPID_IN_USE` may not be returned.
+
+ .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken``
+
+ .. _/register/msisdn/requestToken: #post-matrix-client-%CLIENT_MAJOR_VERSION%-register-email-requesttoken
+ operationId: requestTokenToResetPasswordMSISDN
+ responses:
+ 200:
+ description: An SMS message was sent to the given phone number.
"/account/deactivate":
post:
summary: "Deactivate a user's account."
@@ -317,6 +424,7 @@ paths:
valid access token is provided.
security:
- accessToken: []
+ operationId: deactivateAccount
parameters:
- in: body
name: body
@@ -331,7 +439,7 @@ paths:
200:
description: The account has been deactivated.
examples:
- application/json: "{}"
+ application/json: {}
schema:
type: object
401:
@@ -342,6 +450,68 @@ paths:
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
+ tags:
+ - User data
+ "/register/available":
+ get:
+ summary: Checks to see if a username is available on the server.
+ description: |-
+ Checks to see if a username is available, and valid, for the server.
+
+ The server should check to ensure that, at the time of the request, the
+ username requested is available for use. This includes verifying that an
+ application service has not claimed the username and that the username
+ fits the server's desired requirements (for example, a server could dictate
+ that it does not permit usernames with underscores).
+
+ Matrix clients may wish to use this API prior to attempting registration,
+ however the clients must also be aware that using this API does not normally
+ reserve the username. This can mean that the username becomes unavailable
+ between checking its availability and attempting to register it.
+ operationId: checkUsernameAvailability
+ parameters:
+ - in: query
+ name: username
+ type: string
+ x-example: my_cool_localpart
+ required: true
+ default: my_cool_localpart
+ description: The username to check the availability of.
+ responses:
+ 200:
+ description: The username is available
+ examples:
+ application/json: {
+ "available": true
+ }
+ schema:
+ type: object
+ properties:
+ available:
+ type: boolean
+ description: |-
+ A flag to indicate that the username is available. This should always
+ be ``true`` when the server replies with 200 OK.
+ 400:
+ description: |-
+ Part of the request was invalid or the username is not available. This may
+ include one of the following error codes:
+
+ * ``M_USER_IN_USE`` : The desired username is already taken.
+ * ``M_INVALID_USERNAME`` : The desired username is not a valid user name.
+ * ``M_EXCLUSIVE`` : The desired username is in the exclusive namespace
+ claimed by an application service.
+ examples:
+ application/json: {
+ "errcode": "M_USER_IN_USE",
+ "error": "Desired user ID is already taken."
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- User data
diff --git a/api/client-server/report_content.yaml b/api/client-server/report_content.yaml
new file mode 100644
index 00000000..f702151d
--- /dev/null
+++ b/api/client-server/report_content.yaml
@@ -0,0 +1,78 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Client-Server Report Content API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/rooms/{roomId}/report/{eventId}":
+ post:
+ summary: Reports an event as inappropriate.
+ description: |-
+ Reports an event as inappropriate to the server, which may then notify
+ the appropriate people.
+ operationId: reportContent
+ parameters:
+ - in: path
+ type: string
+ name: roomId
+ description: The room in which the event being reported is located.
+ required: true
+ x-example: "!637q39766251:example.com"
+ - in: path
+ type: string
+ name: eventId
+ description: The event to report.
+ required: true
+ x-example: "$something:domain.com"
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "score": -100,
+ "reason": "this makes me sad"
+ }
+ required: ['score', 'reason']
+ properties:
+ score:
+ type: integer
+ description: |-
+ The score to rate this content as where -100 is most offensive
+ and 0 is inoffensive.
+ reason:
+ type: string
+ description: The reason the content is being reported. May be blank.
+ security:
+ - accessToken: []
+ responses:
+ 200:
+ description: The event has been reported successfully.
+ schema:
+ type: object
+ examples:
+ application/json: {}
+ tags:
+ - Reporting content
diff --git a/api/client-server/room_initial_sync.yaml b/api/client-server/room_initial_sync.yaml
index bf403606..c27f0f24 100644
--- a/api/client-server/room_initial_sync.yaml
+++ b/api/client-server/room_initial_sync.yaml
@@ -24,6 +24,7 @@ paths:
direct replacement; the relevant information is returned by the
|/sync|_ API. See the `migration guide
`_.
+ operationId: roomInitialSync
security:
- accessToken: []
parameters:
@@ -37,8 +38,7 @@ paths:
200:
description: The current state of the room
examples:
- application/json: |-
- {
+ application/json: {
"membership": "join",
"messages": {
"chunk": [
diff --git a/api/client-server/room_send.yaml b/api/client-server/room_send.yaml
index 64dbf4e7..6963f76c 100644
--- a/api/client-server/room_send.yaml
+++ b/api/client-server/room_send.yaml
@@ -38,6 +38,7 @@ paths:
The body of the request should be the content object of the event; the
fields in this object will vary depending on the type of event. See
`Room Events`_ for the m. event specification.
+ operationId: sendMessage
security:
- accessToken: []
parameters:
@@ -66,8 +67,7 @@ paths:
name: body
schema:
type: object
- example: |-
- {
+ example: {
"msgtype": "m.text",
"body": "hello"
}
@@ -75,9 +75,8 @@ paths:
200:
description: "An ID for the sent event."
examples:
- application/json: |-
- {
- "event_id": "YUwRidLecu"
+ application/json: {
+ "event_id": "$YUwRidLecu:example.com"
}
schema:
type: object
diff --git a/api/client-server/room_state.yaml b/api/client-server/room_state.yaml
index d9064f0a..c04fb803 100644
--- a/api/client-server/room_state.yaml
+++ b/api/client-server/room_state.yaml
@@ -42,6 +42,7 @@ paths:
The body of the request should be the content object of the event; the
fields in this object will vary depending on the type of event. See
`Room Events`_ for the ``m.`` event specification.
+ operationId: setRoomStateWithKey
security:
- accessToken: []
parameters:
@@ -56,28 +57,28 @@ paths:
name: eventType
description: The type of event to send.
required: true
- x-example: "m.room.name"
+ x-example: "m.room.member"
- in: path
type: string
name: stateKey
description: The state_key for the state to send. Defaults to the empty string.
required: true
- x-example: ""
+ x-example: "@alice:example.com"
- in: body
name: body
schema:
type: object
- example: |-
- {
- "name": "New name for the room"
+ example: {
+ "membership": "join",
+ "avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto",
+ "displayname": "Alice Margatroid"
}
responses:
200:
description: "An ID for the sent event."
examples:
- application/json: |-
- {
- "event_id": "YUwRidLecu"
+ application/json: {
+ "event_id": "$YUwRidLecu:example.com"
}
schema:
type: object
@@ -104,6 +105,7 @@ paths:
The body of the request should be the content object of the event; the
fields in this object will vary depending on the type of event. See
`Room Events`_ for the ``m.`` event specification.
+ operationId: setRoomState
security:
- accessToken: []
parameters:
@@ -123,17 +125,15 @@ paths:
name: body
schema:
type: object
- example: |-
- {
+ example: {
"name": "New name for the room"
}
responses:
200:
description: "An ID for the sent event."
examples:
- application/json: |-
- {
- "event_id": "YUwRidLecu"
+ application/json: {
+ "event_id": "$YUwRidLecu:example.com"
}
schema:
type: object
diff --git a/api/client-server/rooms.yaml b/api/client-server/rooms.yaml
index 0bb268dd..cc1f2bf7 100644
--- a/api/client-server/rooms.yaml
+++ b/api/client-server/rooms.yaml
@@ -27,6 +27,49 @@ produces:
securityDefinitions:
$ref: definitions/security.yaml
paths:
+ "/rooms/{roomId}/event/{eventId}":
+ get:
+ summary: Get a single event by event ID.
+ description: |-
+ Get a single event based on ``roomId/eventId``. You must have permission to
+ retrieve this event e.g. by being a member in the room for this event.
+ operationId: getOneRoomEvent
+ security:
+ - accessToken: []
+ parameters:
+ - in: path
+ type: string
+ name: roomId
+ description: The ID of the room the event is in.
+ required: true
+ x-example: "!asfDuShaf7Gafaw:matrix.org"
+ - in: path
+ type: string
+ name: eventId
+ description: The event ID to get.
+ required: true
+ x-example: "$asfDuShaf7Gafaw:matrix.org"
+ responses:
+ 200:
+ description: The full event.
+ examples:
+ application/json: {
+ "content": {
+ "body": "Hello world!",
+ "msgtype": "m.text"
+ },
+ "room_id": "!wfgy43Sg4a:matrix.org",
+ "sender": "@bob:matrix.org",
+ "event_id": "$asfDuShaf7Gafaw:matrix.org",
+ "type": "m.room.message"
+ }
+ schema:
+ allOf:
+ - "$ref": "definitions/event-schemas/schema/core-event-schema/event.yaml"
+ 404:
+ description: The event was not found or you do not have permission to read this event.
+ tags:
+ - Room participation
"/rooms/{roomId}/state/{eventType}/{stateKey}":
get:
summary: Get the state identified by the type and key.
@@ -35,6 +78,7 @@ paths:
joined to the room then the state is taken from the current
state of the room. If the user has left the room then the state is
taken from the state of the room when they left.
+ operationId: getRoomStateWithKey
security:
- accessToken: []
parameters:
@@ -60,8 +104,8 @@ paths:
200:
description: The content of the state event.
examples:
- application/json: |-
- {"name": "Example room name"}
+ application/json: {
+ "name": "Example room name"}
schema:
type: object
404:
@@ -82,6 +126,7 @@ paths:
taken from the state of the room when they left.
This looks up the state event with the empty state key.
+ operationId: getRoomStateByType
security:
- accessToken: []
parameters:
@@ -101,8 +146,8 @@ paths:
200:
description: The content of the state event.
examples:
- application/json: |-
- {"name": "Example room name"}
+ application/json: {
+ "name": "Example room name"}
schema:
type: object
404:
@@ -118,6 +163,7 @@ paths:
summary: Get all state events in the current state of a room.
description: |-
Get the state events for the current state of a room.
+ operationId: getRoomState
security:
- accessToken: []
parameters:
@@ -131,8 +177,7 @@ paths:
200:
description: The current state of the room
examples:
- application/json: |-
- [
+ application/json: [
{
"age": 7148266897,
"content": {
@@ -235,6 +280,7 @@ paths:
summary: Get the m.room.member events for the room.
description:
Get the list of members for this room.
+ operationId: getMembersByRoom
parameters:
- in: path
type: string
@@ -242,6 +288,8 @@ paths:
description: The room to get the member events for.
required: true
x-example: "!636q39766251:example.com"
+ security:
+ - accessToken: []
responses:
200:
description: |-
@@ -249,8 +297,7 @@ paths:
this will be the current members of the room. If you have left the
room then this will be the members of the room when you left.
examples:
- application/json: |-
- {
+ application/json: {
"chunk": [
{
"age": 6547561012,
@@ -299,3 +346,53 @@ paths:
member of the room.
tags:
- Room participation
+ "/rooms/{roomId}/joined_members":
+ get:
+ summary: Gets the list of currently joined users and their profile data.
+ description:
+ This API returns a map of MXIDs to member info objects for members of the room. The current user must be in the room for it to work, unless it is an Application Service in which case any of the AS's users must be in the room.
+ This API is primarily for Application Services and should be faster to respond than ``/members`` as it can be implemented more efficiently on the server.
+ operationId: getJoinedMembersByRoom
+ parameters:
+ - in: path
+ type: string
+ name: roomId
+ description: The room to get the members of.
+ required: true
+ x-example: "!636q39766251:example.com"
+ security:
+ - accessToken: []
+ responses:
+ 200:
+ description: |-
+ A map of MXID to room member objects.
+ examples:
+ application/json: {
+ "joined": {
+ "@bar:example.com": {
+ "display_name": "Bar",
+ "avatar_url": "mxc://riot.ovh/printErCATzZijQsSDWorRaK"
+ }
+ }
+ }
+ schema:
+ type: object
+ properties:
+ joined:
+ additionalProperties:
+ title: RoomMember
+ type: object
+ properties:
+ display_name:
+ type: string
+ description: The display name of the user this object is representing.
+ avatar_url:
+ type: string
+ description: The mxc avatar url of the user this object is representing.
+ description: A map from user ID to a RoomMember object.
+ type: object
+ 403:
+ description: >
+ You aren't a member of the room.
+ tags:
+ - Room participation
diff --git a/api/client-server/search.yaml b/api/client-server/search.yaml
index a1c7b9fe..4a5f4515 100644
--- a/api/client-server/search.yaml
+++ b/api/client-server/search.yaml
@@ -32,6 +32,7 @@ paths:
summary: Perform a server-side search.
description: |-
Performs a full text search across different categories.
+ operationId: search
security:
- accessToken: []
parameters:
@@ -40,29 +41,28 @@ paths:
type: string
description: |-
The point to return events from. If given, this should be a
- `next_batch` result from a previous call to this endpoint.
+ ``next_batch`` result from a previous call to this endpoint.
x-example: "YWxsCgpOb25lLDM1ODcwOA"
- in: body
name: body
schema:
type: object
- example: |-
- {
+ example: {
"search_categories": {
"room_events": {
"keys": [
"content.body"
],
- "search_term": "martians and men"
- }
- },
- "order_by": "recent",
- "groupings": {
- "group_by": [
- {
- "key": "room_id"
+ "search_term": "martians and men",
+ "order_by": "recent",
+ "groupings": {
+ "group_by": [
+ {
+ "key": "room_id"
+ }
+ ]
}
- ]
+ }
}
}
properties:
@@ -74,7 +74,7 @@ paths:
properties:
room_events:
type: object
- title: "Room Events"
+ title: Room Events Criteria
description: Mapping of category name to search criteria.
properties:
search_term:
@@ -89,15 +89,22 @@ paths:
filter:
type: object
title: Filter
+ # Within the C-S spec document, `filter`_ is picked up
+ # as a link to the filtering section. In OpenAPI 3.0,
+ # we could use the link feature, but we're still on 2.0
+ # for now :/
description: |-
- This takes a `filter `_.
+ This takes a `filter`_.
+ $ref: "definitions/room_event_filter.yaml"
order_by:
title: "Ordering"
type: string
enum: ["recent", "rank"]
- description: "The order in which to search for results."
+ description: |-
+ The order in which to search for results.
+ By default, this is ``"rank"``.
event_context:
- title: "Event Context"
+ title: Include Event Context
type: object
description: |-
Configures whether any context for the events
@@ -108,13 +115,13 @@ paths:
title: "Before limit"
description: |-
How many events before the result are
- returned.
+ returned. By default, this is ``5``.
after_limit:
type: integer
title: "After limit"
description: |-
How many events after the result are
- returned.
+ returned. By default, this is ``5``.
include_profile:
type: boolean
title: "Return profile information"
@@ -122,6 +129,7 @@ paths:
Requests that the server returns the
historic profile information for the users
that sent the events that were returned.
+ By default, this is ``false``.
include_state:
type: boolean
title: Include current state
@@ -162,18 +170,24 @@ paths:
properties:
search_categories:
type: object
- title: Categories
+ title: Result Categories
description: Describes which categories to search in and
their criteria.
properties:
room_events:
type: object
- title: Room Event Results
+ title: Result Room Events
description: Mapping of category name to search criteria.
properties:
count:
- type: number
+ type: integer
description: An approximate count of the total number of results found.
+ highlights:
+ type: array
+ title: Highlights
+ description: List of words which should be highlighted, useful for stemming which may change the query terms.
+ items:
+ type: string
results:
type: array
title: Results
@@ -214,6 +228,9 @@ paths:
description: |-
The historic profile information of the
users that sent the events returned.
+
+ The ``string`` key is the user ID for which
+ the profile belongs to.
additionalProperties:
type: object
title: User Profile
@@ -247,15 +264,24 @@ paths:
The current state for every room in the results.
This is included if the request had the
``include_state`` key set with a value of ``true``.
+
+ The ``string`` key is the room ID for which the ``State
+ Event`` array belongs to.
additionalProperties:
type: array
title: Room State
items:
+ type: object
"$ref": "definitions/event-schemas/schema/core-event-schema/state_event.yaml"
groups:
type: object
title: Groups
- description: Any groups that were requested.
+ description: |-
+ Any groups that were requested.
+
+ The outer ``string`` key is the group key requested (eg: ``room_id``
+ or ``sender``). The inner ``string`` key is the grouped value (eg:
+ a room's ID or a user's ID).
additionalProperties:
type: object
title: Group Key
@@ -297,8 +323,7 @@ paths:
the next call. If this field is absent, there are no
more results.
examples:
- application/json: |-
- {
+ application/json: {
"search_categories": {
"room_events": {
"groups": {
@@ -312,6 +337,10 @@ paths:
}
}
},
+ "highlights": [
+ "martians",
+ "men"
+ ],
"next_batch": "5FdgFsd234dfgsdfFD",
"count": 1224,
"results": [
@@ -339,6 +368,6 @@ paths:
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Search
diff --git a/api/client-server/sync.yaml b/api/client-server/sync.yaml
index b7965da9..f20798c0 100644
--- a/api/client-server/sync.yaml
+++ b/api/client-server/sync.yaml
@@ -34,6 +34,7 @@ paths:
Clients use this API when they first log in to get an initial snapshot
of the state on the server, and then continue to call this API to get
incremental deltas to the state, and to receive new messages.
+ operationId: sync
security:
- accessToken: []
parameters:
@@ -88,8 +89,12 @@ paths:
name: timeout
type: integer
description: |-
- The maximum time to poll in milliseconds before returning this
- request.
+ The maximum time to wait, in milliseconds, before returning this
+ request. If no events (or other data) become available before this
+ time elapses, the server will return a response with empty fields.
+
+ By default, this is ``0``, so the server will return immediately
+ even if the response is empty.
x-example: 30000
responses:
200:
@@ -222,6 +227,14 @@ paths:
room up to the point when the user left.
allOf:
- $ref: "definitions/timeline_batch.yaml"
+ account_data:
+ title: Account Data
+ type: object
+ description: |-
+ The private data that this user has attached to
+ this room.
+ allOf:
+ - $ref: "definitions/event_batch.yaml"
presence:
title: Presence
type: object
@@ -242,9 +255,22 @@ paths:
description: |-
Information on the send-to-device messages for the client
device, as defined in |send_to_device_sync|_.
+ device_lists:
+ title: DeviceLists
+ type: object
+ description: |-
+ Information on end-to-end device updates, as specified in
+ |device_lists_sync|_.
+ device_one_time_keys_count:
+ title: One-time keys count
+ type: object
+ additionalProperties:
+ type: integer
+ description: |-
+ Information on end-to-end encryption keys, as specified
+ in |device_lists_sync|_.
examples:
- application/json: |-
- {
+ application/json: {
"next_batch": "s72595_4483_1934",
"presence": {
"events": [
diff --git a/api/client-server/tags.yaml b/api/client-server/tags.yaml
index 7add8479..b7bafab6 100644
--- a/api/client-server/tags.yaml
+++ b/api/client-server/tags.yaml
@@ -1,4 +1,5 @@
# 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.
@@ -32,6 +33,7 @@ paths:
summary: List the tags for a room.
description: |-
List the tags set by a user on a room.
+ operationId: getRoomTags
security:
- accessToken: []
parameters:
@@ -61,11 +63,11 @@ paths:
title: Tags
type: object
examples:
- application/json: |-
- {
+ application/json: {
"tags": {
- "work": {"order": "1"},
- "pinned": {}
+ "m.favourite": {},
+ "u.Work": {"order": "1"},
+ "u.Customers": {}
}
}
tags:
@@ -75,6 +77,7 @@ paths:
summary: Add a tag to a room.
description: |-
Add a tag to the room.
+ operationId: setRoomTag
security:
- accessToken: []
parameters:
@@ -107,8 +110,8 @@ paths:
Extra data for the tag, e.g. ordering.
schema:
type: object
- example: |-
- {"order": "1"}
+ example: {
+ "order": "1"}
responses:
200:
description:
@@ -116,14 +119,15 @@ paths:
schema:
type: object
examples:
- application/json: |-
- {}
+ application/json: {
+ }
tags:
- User data
delete:
summary: Remove a tag from the room.
description: |-
Remove a tag from the room.
+ operationId: deleteRoomTag
security:
- accessToken: []
parameters:
@@ -156,7 +160,7 @@ paths:
schema:
type: object
examples:
- application/json: |-
- {}
+ application/json: {
+ }
tags:
- User data
diff --git a/api/client-server/third_party_lookup.yaml b/api/client-server/third_party_lookup.yaml
new file mode 100644
index 00000000..3d348df2
--- /dev/null
+++ b/api/client-server/third_party_lookup.yaml
@@ -0,0 +1,208 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Client-Server Third Party Lookup API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/thirdparty/protocols":
+ get:
+ summary: Retrieve metadata about all protocols that a homeserver supports.
+ description: |-
+ Fetches the overall metadata about protocols supported by the
+ homeserver. Includes both the available protocols and all fields
+ required for queries against each protocol.
+ operationId: getProtocols
+ security:
+ - accessToken: []
+ responses:
+ 200:
+ description: The protocols supported by the homeserver.
+ schema:
+ $ref: ../application-service/definitions/protocol_metadata.yaml
+ "/thirdparty/protocol/{protocol}":
+ get:
+ summary: Retrieve metadata about a specific protocol that the homeserver supports.
+ description: |-
+ Fetches the metadata from the homeserver about a particular third party protocol.
+ operationId: getProtocolMetadata
+ security:
+ - accessToken: []
+ parameters:
+ - in: path
+ name: protocol
+ type: string
+ description: |-
+ The name of the protocol.
+ required: true
+ x-example: "irc"
+ responses:
+ 200:
+ description: The protocol was found and metadata returned.
+ schema:
+ $ref: ../application-service/definitions/protocol.yaml
+ 404:
+ description: The protocol is unknown.
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND"
+ }
+ schema:
+ $ref: definitions/errors/error.yaml
+ "/thirdparty/location/{protocol}":
+ get:
+ summary: Retrieve Matrix-side portals rooms leading to a third party location.
+ description: |-
+ Requesting this endpoint with a valid protocol name results in a list
+ of successful mapping results in a JSON array. Each result contains
+ objects to represent the Matrix room or rooms that represent a portal
+ to this third party network. Each has the Matrix room alias string,
+ an identifier for the particular third party network protocol, and an
+ object containing the network-specific fields that comprise this
+ identifier. It should attempt to canonicalise the identifier as much
+ as reasonably possible given the network type.
+ operationId: queryLocationByProtocol
+ security:
+ - accessToken: []
+ parameters:
+ - in: path
+ name: protocol
+ type: string
+ description: The protocol used to communicate to the third party network.
+ required: true
+ x-example: irc
+ - in: query
+ name: searchFields
+ type: string
+ description: |-
+ One or more custom fields to help identify the third party
+ location.
+ responses:
+ 200:
+ description: At least one portal room was found.
+ schema:
+ $ref: ../application-service/definitions/location_batch.yaml
+ 404:
+ description: No portal rooms were found.
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND"
+ }
+ schema:
+ $ref: definitions/errors/error.yaml
+ "/thirdparty/user/{protocol}":
+ get:
+ summary: Retrieve the Matrix User ID of a corresponding third party user.
+ description: |-
+ Retrieve a Matrix User ID linked to a user on the third party service, given
+ a set of user parameters.
+ operationId: queryUserByProtocol
+ security:
+ - accessToken: []
+ parameters:
+ - in: path
+ name: protocol
+ type: string
+ description: |-
+ The name of the protocol.
+ required: true
+ x-example: irc
+ - in: query
+ name: fields...
+ type: string
+ description: |-
+ One or more custom fields that are passed to the AS to help identify the user.
+ responses:
+ 200:
+ description: The Matrix User IDs found with the given parameters.
+ schema:
+ $ref: ../application-service/definitions/user_batch.yaml
+ 404:
+ description: The Matrix User ID was not found
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND"
+ }
+ schema:
+ $ref: definitions/errors/error.yaml
+ "/thirdparty/location":
+ get:
+ summary: Reverse-lookup third party locations given a Matrix room alias.
+ description: |-
+ Retrieve an array of third party network locations from a Matrix room
+ alias.
+ operationId: queryLocationByAlias
+ security:
+ - accessToken: []
+ parameters:
+ - in: query
+ name: alias
+ type: string
+ description: The Matrix room alias to look up.
+ required: true
+ x-example: "#matrix:matrix.org"
+ responses:
+ 200:
+ description: |-
+ All found third party locations.
+ schema:
+ $ref: ../application-service/definitions/location_batch.yaml
+ 404:
+ description: The Matrix room alias was not found
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND"
+ }
+ schema:
+ $ref: definitions/errors/error.yaml
+ "/thirdparty/user":
+ get:
+ summary: Reverse-lookup third party users given a Matrix User ID.
+ description: |-
+ Retrieve an array of third party users from a Matrix User ID.
+ operationId: queryUserByID
+ security:
+ - accessToken: []
+ parameters:
+ - in: query
+ name: userid
+ type: string
+ description: The Matrix User ID to look up.
+ required: true
+ x-example: "@bob:matrix.org"
+ responses:
+ 200:
+ description: |-
+ An array of third party users.
+ schema:
+ $ref: ../application-service/definitions/user_batch.yaml
+ 404:
+ description: The Matrix User ID was not found
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND"
+ }
+ schema:
+ $ref: definitions/errors/error.yaml
diff --git a/api/client-server/third_party_membership.yaml b/api/client-server/third_party_membership.yaml
index a2e40372..66c14c4d 100644
--- a/api/client-server/third_party_membership.yaml
+++ b/api/client-server/third_party_membership.yaml
@@ -75,6 +75,7 @@ paths:
append a ``m.room.third_party_invite`` event to the room.
.. _joining rooms section: `invite-by-user-id-endpoint`_
+ operationId: inviteBy3PID
security:
- accessToken: []
parameters:
@@ -89,8 +90,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"id_server": "matrix.org",
"medium": "email",
"address": "cheeky@monkey.com"
@@ -111,8 +111,8 @@ paths:
200:
description: The user has been invited to join the room.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object
403:
@@ -124,11 +124,13 @@ paths:
- The inviter is not currently in the room.
- The inviter's power level is insufficient to invite users to the room.
examples:
- application/json: |-
- {"errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
+ application/json: {
+ "errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
+ schema:
+ "$ref": "definitions/errors/error.yaml"
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room membership
diff --git a/api/client-server/to_device.yaml b/api/client-server/to_device.yaml
index 16af1182..739d9a4f 100644
--- a/api/client-server/to_device.yaml
+++ b/api/client-server/to_device.yaml
@@ -34,6 +34,7 @@ paths:
description: |-
This endpoint is used to send send-to-device events to a set of
client devices.
+ operationId: sendToDevice
security:
- accessToken: []
parameters:
@@ -83,7 +84,7 @@ paths:
description:
The message was successfully sent.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
tags:
- Send-to-Device messaging
diff --git a/api/client-server/typing.yaml b/api/client-server/typing.yaml
index fea94108..e7cbe2d7 100644
--- a/api/client-server/typing.yaml
+++ b/api/client-server/typing.yaml
@@ -35,6 +35,7 @@ paths:
milliseconds where N is the value specified in the ``timeout`` key.
Alternatively, if ``typing`` is ``false``, it tells the server that the
user has stopped typing.
+ operationId: setTyping
security:
- accessToken: []
parameters:
@@ -56,8 +57,7 @@ paths:
required: true
schema:
type: object
- example: |-
- {
+ example: {
"typing": true,
"timeout": 30000
}
@@ -75,13 +75,13 @@ paths:
200:
description: The new typing state was set.
examples:
- application/json: |-
- {}
+ application/json: {
+ }
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- Room participation
diff --git a/api/client-server/users.yaml b/api/client-server/users.yaml
new file mode 100644
index 00000000..fc6d233b
--- /dev/null
+++ b/api/client-server/users.yaml
@@ -0,0 +1,100 @@
+# Copyright 2017 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 User Directory API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/user_directory/search":
+ post:
+ summary: Searches the user directory.
+ description: |-
+ This API performs a server-side search over all users registered on the server.
+ It searches user ID and displayname case-insensitively for users that you share a room with or that are in public rooms.
+ operationId: searchUserDirectory
+ security:
+ - accessToken: []
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ properties:
+ search_term:
+ type: string
+ description: The term to search for
+ example: "foo"
+ limit:
+ type: integer
+ description: The maximum number of results to return (Defaults to 10).
+ example: 10
+ required: ["search_term"]
+ responses:
+ 200:
+ description: The results of the search.
+ examples:
+ application/json: {
+ "results": [
+ {
+ "user_id": "@foo:bar.com",
+ "display_name": "Foo",
+ "avatar_url": "mxc://bar.com/foo"
+ }
+ ],
+ "limited": false
+ }
+ schema:
+ type: object
+ required: ["results", "limited"]
+ properties:
+ results:
+ type: array
+ description: Ordered by rank and then whether or not profile info is available.
+ items:
+ title: User
+ type: object
+ required: ["user_id"]
+ properties:
+ user_id:
+ type: string
+ example: "@foo:bar.com"
+ description: The user's matrix user ID.
+ display_name:
+ type: string
+ example: "Foo"
+ description: The display name of the user, if one exists.
+ avatar_url:
+ type: string
+ example: "mxc://bar.com/foo"
+ description: The avatar url, as an MXC, if one exists.
+ limited:
+ type: boolean
+ description: Indicates if the result list has been truncated by the limit.
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
+ tags:
+ - User data
diff --git a/api/client-server/versions.yaml b/api/client-server/versions.yaml
index 1352279a..d3aa0359 100644
--- a/api/client-server/versions.yaml
+++ b/api/client-server/versions.yaml
@@ -33,12 +33,12 @@ paths:
Only the latest ``Z`` value will be reported for each supported ``X.Y`` value.
i.e. if the server implements ``r0.0.0``, ``r0.0.1``, and ``r1.2.0``, it will report ``r0.0.1`` and ``r1.2.0``.
+ operationId: getVersions
responses:
200:
description: The versions supported by the server.
examples:
- application/json: |-
- {
+ application/json: {
"versions": ["r0.0.1"]
}
schema:
diff --git a/api/client-server/voip.yaml b/api/client-server/voip.yaml
index 30000540..75ace4c3 100644
--- a/api/client-server/voip.yaml
+++ b/api/client-server/voip.yaml
@@ -33,14 +33,14 @@ paths:
description: |-
This API provides credentials for the client to use when initiating
calls.
+ operationId: getTurnServer
security:
- accessToken: []
responses:
200:
description: The TURN server credentials.
examples:
- application/json: |-
- {
+ application/json: {
"username":"1443779631:@user:example.com",
"password":"JlKfBy1QwLrO20385QyAtEyIv0=",
"uris":[
@@ -73,6 +73,6 @@ paths:
429:
description: This request was rate-limited.
schema:
- "$ref": "definitions/error.yaml"
+ "$ref": "definitions/errors/rate_limited.yaml"
tags:
- VOIP
diff --git a/api/client-server/whoami.yaml b/api/client-server/whoami.yaml
new file mode 100644
index 00000000..ad40eb86
--- /dev/null
+++ b/api/client-server/whoami.yaml
@@ -0,0 +1,84 @@
+# Copyright 2017 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.
+swagger: '2.0'
+info:
+ title: "Matrix Client-Server Account Identification API"
+ version: "1.0.0"
+host: localhost:8008
+schemes:
+ - https
+ - http
+basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/account/whoami":
+ get:
+ summary: Gets information about the owner of an access token.
+ description: |-
+ Gets information about the owner of a given access token.
+
+ Note that, as with the rest of the Client-Server API,
+ Application Services may masquerade as users within their
+ namespace by giving a ``user_id`` query parameter. In this
+ situation, the server should verify that the given ``user_id``
+ is registered by the appservice, and return it in the response
+ body.
+ operationId: getTokenOwner
+ security:
+ - accessToken: []
+ parameters: []
+ responses:
+ 200:
+ description:
+ The token belongs to a known user.
+ examples:
+ application/json: {
+ "user_id": "@joe:example.org"
+ }
+ schema:
+ type: object
+ required: ["user_id"]
+ properties:
+ user_id:
+ type: string
+ description: The user id that owns the access token.
+ 401:
+ description:
+ The token is not recognised
+ examples:
+ application/json: {
+ "errcode": "M_UNKNOWN_TOKEN",
+ "error": "Unrecognised access token."
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
+ 403:
+ description:
+ The appservice cannot masquerade as the user or has not registered them.
+ examples:
+ application/json: {
+ "errcode": "M_FORBIDDEN",
+ "error": "Application service has not registered this user."
+ }
+ schema:
+ "$ref": "definitions/errors/error.yaml"
+ 429:
+ description: This request was rate-limited.
+ schema:
+ "$ref": "definitions/errors/rate_limited.yaml"
+ tags:
+ - User data
diff --git a/api/identity/associations.yaml b/api/identity/associations.yaml
new file mode 100644
index 00000000..784bb5d6
--- /dev/null
+++ b/api/identity/associations.yaml
@@ -0,0 +1,179 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Identity Service Establishing Associations API"
+ version: "1.0.0"
+host: localhost:8090
+schemes:
+ - https
+ - http
+basePath: /_matrix/identity/api/v1
+produces:
+ - application/json
+paths:
+ "/3pid/getValidated3pid":
+ get:
+ summary: Check whether ownership of a 3pid was validated.
+ description: A client can check whether ownership of a 3pid was validated
+ operationId: getValidated3pid
+ parameters:
+ - in: query
+ type: string
+ name: sid
+ description: The Session ID generated by the ``requestToken`` call.
+ required: true
+ x-example: 1234
+ - in: query
+ type: string
+ name: client_secret
+ description: The client secret passed to the ``requestToken`` call.
+ required: true
+ x-example: monkeys_are_GREAT
+ responses:
+ 200:
+ description: Validation information for the session.
+ examples:
+ application/json: {
+ "medium": "email",
+ "validated_at": 1457622739026,
+ "address": "louise@bobs.burgers"
+ }
+ schema:
+ type: object
+ properties:
+ medium:
+ type: string
+ description: The medium type of the 3pid.
+ address:
+ type: string
+ description: The address of the 3pid being looked up.
+ validated_at:
+ type: integer
+ description: Timestamp indicating the time that the 3pid was validated.
+ 400:
+ description: |-
+ The session has not been validated.
+
+ If the session has not been validated, then ``errcode`` will be
+ ``M_SESSION_NOT_VALIDATED``. If the session has timed out, then
+ ``errcode`` will be ``M_SESSION_EXPIRED``.
+ examples:
+ application/json: {
+ "errcode": "M_SESSION_NOT_VALIDATED",
+ "error": "This validation session has not yet been completed"
+ }
+ 404:
+ description: The Session ID or client secret were not found
+ examples:
+ application/json: {
+ "errcode": "M_NO_VALID_SESSION",
+ "error": "No valid session was found matching that sid and client secret"
+ }
+ "/bind":
+ post:
+ summary: Publish an association between a session and a Matrix user ID.
+ description: |-
+ Publish an association between a session and a Matrix user ID.
+
+ Future calls to ``/lookup`` for any of the session\'s 3pids will return
+ this association.
+
+ Note: for backwards compatibility with older versions of this
+ specification, the parameters may also be specified as
+ ``application/x-form-www-urlencoded`` data. However, this usage is
+ deprecated.
+ operationId: bind
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "sid": "1234",
+ "client_secret": "monkeys_are_GREAT",
+ "mxid": "@ears:matrix.org"
+ }
+ properties:
+ sid:
+ type: string
+ description: The Session ID generated by the ``requestToken`` call.
+ client_secret:
+ type: string
+ description: The client secret passed to the ``requestToken`` call.
+ mxid:
+ type: string
+ description: The Matrix user ID to associate with the 3pids.
+ required: ["sid", "client_secret", "mxid"]
+ responses:
+ 200:
+ description: The association was published.
+ examples:
+ application/json: {
+ "address": "louise@bobs.burgers",
+ "medium": "email",
+ "mxid": "@ears:matrix.org",
+ "not_before": 1428825849161,
+ "not_after": 4582425849161,
+ "ts": 1428825849161,
+
+ "signatures": {
+ "matrix.org": {
+ "ed25519:0": "ENiU2YORYUJgE6WBMitU0mppbQjidDLanAusj8XS2nVRHPu+0t42OKA/r6zV6i2MzUbNQ3c3MiLScJuSsOiVDQ"
+ }
+ }
+ }
+ schema:
+ type: object
+ properties:
+ address:
+ type: string
+ description: The 3pid address of the user being looked up.
+ medium:
+ type: string
+ description: The medium type of the 3pid.
+ mxid:
+ type: string
+ description: The Matrix user ID associated with the 3pid.
+ not_before:
+ type: integer
+ description: A unix timestamp before which the association is not known to be valid.
+ not_after:
+ type: integer
+ description: A unix timestamp after which the association is not known to be valid.
+ ts:
+ type: integer
+ description: The unix timestamp at which the association was verified.
+ signatures:
+ type: object
+ description: The signatures of the verifying identity services which show that the association should be trusted, if you trust the verifying identity services.
+ 400:
+ description: |-
+ The association was not published.
+
+ If the session has not been validated, then ``errcode`` will be
+ ``M_SESSION_NOT_VALIDATED``. If the session has timed out, then
+ ``errcode`` will be ``M_SESSION_EXPIRED``.
+ examples:
+ application/json: {
+ "errcode": "M_SESSION_NOT_VALIDATED",
+ "error": "This validation session has not yet been completed"
+ }
+ 404:
+ description: The Session ID or client secret were not found
+ examples:
+ application/json: {
+ "errcode": "M_NO_VALID_SESSION",
+ "error": "No valid session was found matching that sid and client secret"
+ }
diff --git a/api/identity/email_associations.yaml b/api/identity/email_associations.yaml
new file mode 100644
index 00000000..8431c9e8
--- /dev/null
+++ b/api/identity/email_associations.yaml
@@ -0,0 +1,197 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Identity Service Email Associations API"
+ version: "1.0.0"
+host: localhost:8090
+schemes:
+ - https
+ - http
+basePath: /_matrix/identity/api/v1
+produces:
+ - application/json
+paths:
+ "/validate/email/requestToken":
+ post:
+ summary: Request a token for validating an email address.
+ description: |-
+ Create a session for validating an email address.
+
+ The identity service will send an email containing a token. If that
+ token is presented to the identity service in the future, it indicates
+ that that user was able to read the email for that email address, and
+ so we validate ownership of the email address.
+
+ Note that Home Servers offer APIs that proxy this API, adding
+ additional behaviour on top, for example,
+ ``/register/email/requestToken`` is designed specifically for use when
+ registering an account and therefore will inform the user if the email
+ address given is already registered on the server.
+
+ Note: for backwards compatibility with older versions of this
+ specification, the parameters may also be specified as
+ ``application/x-form-www-urlencoded`` data. However, this usage is
+ deprecated.
+ operationId: emailRequestToken
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "client_secret": "monkeys_are_GREAT",
+ "email": "foo@example.com",
+ "send_attempt": 1
+ }
+ properties:
+ client_secret:
+ type: string
+ description: A unique string used to identify the validation attempt
+ email:
+ type: string
+ description: The email address to validate.
+ send_attempt:
+ type: integer
+ description: |-
+ Optional. If specified, the server will only send an email if
+ the ``send_attempt`` is a number greater than the most recent
+ one which it has seen (or if it has never seen one), scoped
+ to that ``email`` + ``client_secret`` pair. This is to avoid
+ repeatedly sending the same email in the case of request
+ retries between the POSTing user and the identity
+ service. The client should increment this value if they
+ desire a new email (e.g. a reminder) to be sent.
+ next_link:
+ type: string
+ description: |-
+ Optional. When the validation is completed, the identity
+ service will redirect the user to this URL.
+ required: ["client_secret", "email"]
+ responses:
+ 200:
+ description:
+ Session created.
+ examples:
+ application/json: {
+ "sid": "1234"
+ }
+ schema:
+ type: object
+ properties:
+ sid:
+ type: string
+ description: The session ID.
+ 400:
+ description: |
+ An error ocurred. Some possible errors are:
+
+ - ``M_INVALID_EMAIL``: The email address provided was invalid.
+ - ``M_EMAIL_SEND_ERROR``: The validation email could not be sent.
+ "/validate/email/submitToken":
+ post:
+ summary: Validate ownership of an email address.
+ description: |-
+ Validate ownership of an email address.
+
+ If the three parameters are consistent with a set generated by a
+ ``requestToken`` call, ownership of the email address is considered to
+ have been validated. This does not publish any information publicly, or
+ associate the email address with any Matrix user ID. Specifically,
+ calls to ``/lookup`` will not show a binding.
+
+ Note: for backwards compatibility with older versions of this
+ specification, the parameters may also be specified as
+ ``application/x-form-www-urlencoded`` data. However, this usage is
+ deprecated.
+ operationId: emailSubmitTokenPost
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "sid": "1234",
+ "client_secret": "monkeys_are_GREAT",
+ "token": "atoken"
+ }
+ properties:
+ sid:
+ type: string
+ description: The session ID, generated by the ``requestToken`` call.
+ client_secret:
+ type: string
+ description: The client secret that was supplied to the ``requestToken`` call.
+ token:
+ type: string
+ description: The token generated by the ``requestToken`` call and emailed to the user.
+ required: ["sid", "client_secret", "token"]
+ responses:
+ 200:
+ description:
+ The success of the validation.
+ examples:
+ application/json: {
+ "success": true
+ }
+ schema:
+ type: object
+ properties:
+ success:
+ type: boolean
+ description: Whether the validation was successful or not.
+ get:
+ summary: Validate ownership of an email address.
+ description: |-
+ Validate ownership of an email address.
+
+ If the three parameters are consistent with a set generated by a
+ ``requestToken`` call, ownership of the email address is considered to
+ have been validated. This does not publish any information publicly, or
+ associate the email address with any Matrix user ID. Specifically,
+ calls to ``/lookup`` will not show a binding.
+
+ Note that, in contrast with the POST version, this endpoint will be
+ used by end-users, and so the response should be human-readable.
+ operationId: emailSubmitTokenGet
+ parameters:
+ - in: query
+ type: string
+ name: sid
+ required: true
+ description: The session ID, generated by the ``requestToken`` call.
+ x-example: 1234
+ - in: query
+ type: string
+ name: client_secret
+ required: true
+ description: The client secret that was supplied to the ``requestToken`` call.
+ x-example: monkeys_are_GREAT
+ - in: query
+ type: string
+ name: token
+ required: true
+ description: The token generated by the ``requestToken`` call and emailed to the user.
+ x-example: atoken
+ responses:
+ "200":
+ description: Email address is validated.
+ "3xx":
+ description: |-
+ Email address is validated, and the ``next_link`` parameter was
+ provided to the ``requestToken`` call. The user must be redirected
+ to the URL provided by the ``next_link`` parameter.
+ "4xx":
+ description:
+ Validation failed.
diff --git a/api/identity/invitation_signing.yaml b/api/identity/invitation_signing.yaml
new file mode 100644
index 00000000..982dbff7
--- /dev/null
+++ b/api/identity/invitation_signing.yaml
@@ -0,0 +1,90 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Identity Service Ephemeral Invitation Signing API"
+ version: "1.0.0"
+host: localhost:8090
+schemes:
+ - https
+ - http
+basePath: /_matrix/identity/api/v1
+produces:
+ - application/json
+paths:
+ "/sign-ed25519":
+ post:
+ summary: Sign invitation details
+ description: |-
+ Sign invitation details.
+
+ The identity server will look up ``token`` which was stored in a call
+ to ``store-invite``, and fetch the sender of the invite.
+ operationId: blindlySignStuff
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "mxid": "@foo:bar.com",
+ "token": "sometoken",
+ "private_key": "base64encodedkey"
+ }
+ properties:
+ mxid:
+ type: string
+ description: The Matrix user ID of the user accepting the invitation.
+ token:
+ type: string
+ description: Token from the call to ``store-invite``
+ private_key:
+ type: string
+ description: The private key, encoded as `Unpadded base64`_.
+ required: ["mxid", "token", "private_key"]
+ responses:
+ 200:
+ description: The signedjson of the mxid, sender, and token.
+ schema:
+ type: object
+ properties:
+ mxid:
+ type: string
+ description: The Matrix user ID of the user accepting the invitation.
+ sender:
+ type: string
+ description: The Matrix user ID of the user who sent the invitation.
+ signatures:
+ type: object
+ description: The signature of the mxid, sender, and token.
+ token:
+ type: string
+ description: The token for the invitation.
+ examples:
+ application/json: {
+ "mxid": "@foo:bar.com",
+ "sender": "@baz:bar.com",
+ "signatures": {
+ "my.id.server": {
+ "ed25519:0": "def987"
+ }
+ },
+ "token": "abc123"
+ }
+ 404:
+ description: Token was not found.
+ example: {
+ "errcode": "M_UNRECOGNIZED",
+ "error": "Didn't recognize token"
+ }
diff --git a/api/identity/lookup.yaml b/api/identity/lookup.yaml
index 83c3b661..bfd2153e 100644
--- a/api/identity/lookup.yaml
+++ b/api/identity/lookup.yaml
@@ -1,4 +1,6 @@
# Copyright 2016 OpenMarket Ltd
+# Copyright 2017 Kamax.io
+# Copyright 2017 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.
@@ -27,26 +29,26 @@ paths:
get:
summary: Look up the Matrix user ID for a 3pid.
description: Look up the Matrix user ID for a 3pid.
+ operationId: lookupUser
parameters:
- in: query
type: string
name: medium
required: true
- description: The literal string "email".
+ description: The medium type of the 3pid. See the `3PID Types`_ Appendix.
x-example: "email"
- in: query
type: string
name: address
required: true
- description: The email address being looked up.
+ description: The address of the 3pid being looked up. See the `3PID Types`_ Appendix.
x-example: "louise@bobs.burgers"
responses:
200:
description:
The association for that 3pid, or the empty object if no association is known.
examples:
- application/json: |-
- {
+ application/json: {
"address": "louise@bobs.burgers",
"medium": "email",
"mxid": "@ears:matrix.org",
@@ -83,4 +85,58 @@ paths:
description: The unix timestamp at which the association was verified.
signatures:
type: object
- description: The signatures of the verifying identity service which show that the association should be trusted, if you trust the verifying identity service.
+ description: The signatures of the verifying identity services which show that the association should be trusted, if you trust the verifying identity services.
+ "/bulk_lookup":
+ post:
+ summary: Lookup Matrix user IDs for a list of 3pids.
+ description: Lookup Matrix user IDs for a list of 3pids.
+ operationId: lookupUsers
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "threepids":
+ [
+ ["email","user@example.org"],
+ ["msisdn", "123456789"],
+ ["email","user2@example.org"]
+ ]
+ }
+ properties:
+ threepids:
+ type: array
+ items:
+ type: array
+ title: 3PID mappings
+ items:
+ type: string
+ title: 3PID medium or address
+ description: an array of arrays containing the `3PID Types`_ with the ``medium`` in first position and the ``address`` in second position.
+ required:
+ - "threepids"
+ responses:
+ 200:
+ description: A list of known 3PID mappings for the supplied 3PIDs.
+ examples:
+ application/json: {
+ "threepids": [
+ ["email","user@example.org", "@bla:example.org"],
+ ["msisdn", "123456789", "@blah2:example.com"]
+ ]
+ }
+ schema:
+ type: object
+ properties:
+ threepids:
+ type: array
+ items:
+ type: array
+ title: 3PID mappings
+ items:
+ type: string
+ title: 3PID medium or address or the Matrix ID
+ description: an array of array containing the `3PID Types`_ with the ``medium`` in first position, the ``address`` in second position and Matrix ID in third position.
+ required:
+ - "threepids"
diff --git a/api/identity/phone_associations.yaml b/api/identity/phone_associations.yaml
new file mode 100644
index 00000000..c2cc6cfe
--- /dev/null
+++ b/api/identity/phone_associations.yaml
@@ -0,0 +1,203 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Identity Service Phone Number Associations API"
+ version: "1.0.0"
+host: localhost:8090
+schemes:
+ - https
+ - http
+basePath: /_matrix/identity/api/v1
+produces:
+ - application/json
+paths:
+ "/validate/msisdn/requestToken":
+ post:
+ summary: Request a token for validating a phone number.
+ description: |-
+ Create a session for validating a phone number.
+
+ The identity service will send an SMS message containing a token. If
+ that token is presented to the identity service in the future, it
+ indicates that that user was able to read the SMS for that phone
+ number, and so we validate ownership of the phone number.
+
+ Note that Home Servers offer APIs that proxy this API, adding
+ additional behaviour on top, for example,
+ ``/register/msisdn/requestToken`` is designed specifically for use when
+ registering an account and therefore will inform the user if the phone
+ number given is already registered on the server.
+
+ Note: for backwards compatibility with older versions of this
+ specification, the parameters may also be specified as
+ ``application/x-form-www-urlencoded`` data. However, this usage is
+ deprecated.
+ operationId: msisdnRequestToken
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "client_secret": "monkeys_are_GREAT",
+ "country": "GB",
+ "phone_number": "07700900001",
+ "send_attempt": 1
+ }
+ properties:
+ client_secret:
+ type: string
+ description: A unique string used to identify the validation attempt.
+ country:
+ type: string
+ description: |-
+ The two-letter uppercase ISO country code that the number in
+ ``phone_number`` should be parsed as if it were dialled from.
+ phone_number:
+ type: string
+ description: The phone number to validate.
+ send_attempt:
+ type: integer
+ description: |-
+ Optional. If specified, the server will only send an SMS if
+ the ``send_attempt`` is a number greater than the most recent
+ one which it has seen (or if it has never seen one), scoped
+ to that ``country`` + ``phone_number`` + ``client_secret``
+ triple. This is to avoid repeatedly sending the same SMS in
+ the case of request retries between the POSTing user and the
+ identity service. The client should increment this value if
+ they desire a new SMS (e.g. a reminder) to be sent.
+ next_link:
+ type: string
+ description: |-
+ Optional. When the validation is completed, the identity
+ service will redirect the user to this URL.
+ required: ["client_secret", "country", "phone_number"]
+ responses:
+ 200:
+ description:
+ Session created.
+ examples:
+ application/json: {
+ "sid": "1234"
+ }
+ schema:
+ type: object
+ properties:
+ sid:
+ type: string
+ description: The session ID.
+ 400:
+ description: |
+ An error ocurred. Some possible errors are:
+
+ - ``M_INVALID_ADDRESS``: The phone number provided was invalid.
+ - ``M_SEND_ERROR``: The validation SMS could not be sent.
+ "/validate/msisdn/submitToken":
+ post:
+ summary: Validate ownership of a phone number.
+ description: |-
+ Validate ownership of a phone number.
+
+ If the three parameters are consistent with a set generated by a
+ ``requestToken`` call, ownership of the phone number is considered to
+ have been validated. This does not publish any information publicly, or
+ associate the phone number address with any Matrix user
+ ID. Specifically, calls to ``/lookup`` will not show a binding.
+
+ Note: for backwards compatibility with older versions of this
+ specification, the parameters may also be specified as
+ ``application/x-form-www-urlencoded`` data. However, this usage is
+ deprecated.
+ operationId: msisdnSubmitTokenPost
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "sid": "1234",
+ "client_secret": "monkeys_are_GREAT",
+ "token": "atoken"
+ }
+ properties:
+ sid:
+ type: string
+ description: The session ID, generated by the ``requestToken`` call.
+ client_secret:
+ type: string
+ description: The client secret that was supplied to the ``requestToken`` call.
+ token:
+ type: string
+ description: The token generated by the ``requestToken`` call and sent to the user.
+ required: ["sid", "client_secret", "token"]
+ responses:
+ 200:
+ description:
+ The success of the validation.
+ examples:
+ application/json: {
+ "success": true
+ }
+ schema:
+ type: object
+ properties:
+ success:
+ type: boolean
+ description: Whether the validation was successful or not.
+ get:
+ summary: Validate ownership of a phone number.
+ description: |-
+ Validate ownership of a phone number.
+
+ If the three parameters are consistent with a set generated by a
+ ``requestToken`` call, ownership of the phone number address is
+ considered to have been validated. This does not publish any
+ information publicly, or associate the phone number with any Matrix
+ user ID. Specifically, calls to ``/lookup`` will not show a binding.
+
+ Note that, in contrast with the POST version, this endpoint will be
+ used by end-users, and so the response should be human-readable.
+ operationId: msisdnSubmitTokenGet
+ parameters:
+ - in: query
+ type: string
+ name: sid
+ required: true
+ description: The session ID, generated by the ``requestToken`` call.
+ x-example: 1234
+ - in: query
+ type: string
+ name: client_secret
+ required: true
+ description: The client secret that was supplied to the ``requestToken`` call.
+ x-example: monkeys_are_GREAT
+ - in: query
+ type: string
+ name: token
+ required: true
+ description: The token generated by the ``requestToken`` call and sent to the user.
+ x-example: atoken
+ responses:
+ "200":
+ description: Phone number is validated.
+ "3xx":
+ description: |-
+ Phone number address is validated, and the ``next_link`` parameter
+ was provided to the ``requestToken`` call. The user must be
+ redirected to the URL provided by the ``next_link`` parameter.
+ "4xx":
+ description:
+ Validation failed.
diff --git a/api/identity/ping.yaml b/api/identity/ping.yaml
new file mode 100644
index 00000000..005160a3
--- /dev/null
+++ b/api/identity/ping.yaml
@@ -0,0 +1,44 @@
+# Copyright 2018 Kamax Sàrl
+#
+# 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-Identity Versions API"
+ version: "1.0.0"
+host: localhost:8090
+schemes:
+ - https
+basePath: /_matrix/identity
+produces:
+ - application/json
+paths:
+ "/api/v1":
+ get:
+ summary: Checks that an Identity server is available at this API endpopint.
+ description: |-
+ Checks that an Identity server is available at this API endpopint.
+
+ To discover that an Identity server is available at a specific URL,
+ this endpoint can be queried and will return an empty object.
+
+ This is primarly used for auto-discovery and health check purposes
+ by entities acting as a client for the Identity server.
+ operationId: ping
+ responses:
+ 200:
+ description: An Identity server is ready to serve requests.
+ examples:
+ application/json: {}
+ schema:
+ type: object
diff --git a/api/identity/pubkey.yaml b/api/identity/pubkey.yaml
index 40d2a237..00796975 100644
--- a/api/identity/pubkey.yaml
+++ b/api/identity/pubkey.yaml
@@ -28,6 +28,7 @@ paths:
summary: Get a public key.
description: |-
Get the public key for the passed key ID.
+ operationId: getPubKey
parameters:
- in: path
type: string
@@ -43,8 +44,7 @@ paths:
description:
The public key exists.
examples:
- application/json: |-
- {
+ application/json: {
"public_key": "VXuGitF39UH5iRfvbIknlvlAVKgD1BsLDMvBf0pmp7c"
}
schema:
@@ -57,6 +57,7 @@ paths:
summary: Check whether a long-term public key is valid.
description: |-
Check whether a long-term public key is valid.
+ operationId: isPubKeyValid
parameters:
- in: query
type: string
@@ -70,8 +71,7 @@ paths:
description:
The validity of the public key.
examples:
- application/json: |-
- {
+ application/json: {
"valid": true
}
schema:
@@ -85,6 +85,7 @@ paths:
summary: Check whether a short-term public key is valid.
description: |-
Check whether a short-term public key is valid.
+ operationId: isEphemeralPubKeyValid
parameters:
- in: query
type: string
@@ -98,8 +99,7 @@ paths:
description:
The validity of the public key.
examples:
- application/json: |-
- {
+ application/json: {
"valid": true
}
schema:
diff --git a/api/identity/store_invite.yaml b/api/identity/store_invite.yaml
new file mode 100644
index 00000000..6b847b5b
--- /dev/null
+++ b/api/identity/store_invite.yaml
@@ -0,0 +1,114 @@
+# 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.
+swagger: '2.0'
+info:
+ title: "Matrix Identity Service Store Invitations API"
+ version: "1.0.0"
+host: localhost:8090
+schemes:
+ - https
+ - http
+basePath: /_matrix/identity/api/v1
+produces:
+ - application/json
+paths:
+ "/store-invite":
+ post:
+ summary: Store pending invitations to a user\'s 3pid.
+ description: |-
+ Store pending invitations to a user\'s 3pid.
+
+ In addition to the request parameters specified below, an arbitrary
+ number of other parameters may also be specified. These may be used in
+ the invite message generation described below.
+
+ The service will generate a random token and an ephemeral key used for
+ accepting the invite.
+
+ The service also generates a ``display_name`` for the inviter, which is
+ a redacted version of ``address`` which does not leak the full contents
+ of the ``address``.
+
+ The service records persistently all of the above information.
+
+ It also generates an email containing all of this data, sent to the
+ ``address`` parameter, notifying them of the invitation.
+
+ Also, the generated ephemeral public key will be listed as valid on
+ requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``.
+ operationId: storeInvite
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "medium": "email",
+ "address": "foo@bar.baz",
+ "room_id": "!something:example.tld",
+ "sender": "@bob:example.com"
+ }
+ properties:
+ medium:
+ type: string
+ description: The literal string ``email``.
+ address:
+ type: string
+ description: The email address of the invited user.
+ room_id:
+ type: string
+ description: The Matrix room ID to which the user is invited
+ sender:
+ type: string
+ description: The Matrix user ID of the inviting user
+ required: ["medium", "address", "room_id", "sender"]
+ responses:
+ 200:
+ description: The invitation was stored.
+ schema:
+ type: object
+ properties:
+ token:
+ type: string
+ description: The generated token.
+ public_keys:
+ type: array
+ description: A list of [server\'s long-term public key, generated ephemeral public key].
+ items:
+ type: string
+ display_name:
+ type: string
+ description: The generated (redacted) display_name.
+ example:
+ application/json: {
+ "token": "sometoken",
+ "public_keys": [
+ "serverpublickey",
+ "ephemeralpublickey"
+ ],
+ "display_name": "f...@b..."
+ }
+ 400:
+ description: |
+ An error has occured.
+
+ If the 3pid is already bound to a Matrix user ID, the error code
+ will be ``M_THREEPID_IN_USE``. If the medium is unsupported, the
+ error code will be ``M_UNRECOGNIZED``.
+ examples:
+ application/json: {
+ "errcode": "M_THREEPID_IN_USE",
+ "error": "Binding already known",
+ "mxid": mxid
+ }
diff --git a/api/openapi_extensions.md b/api/openapi_extensions.md
new file mode 100644
index 00000000..339452ba
--- /dev/null
+++ b/api/openapi_extensions.md
@@ -0,0 +1,45 @@
+# OpenAPI Extensions
+
+For some functionality that is not directly provided by the OpenAPI v2
+specification, some extensions have been added that are to be consistent
+across the specification. The defined extensions are listed below. Extensions
+should not break parsers, however if extra functionality is required, aware
+parsers should be able to take advantage of the added syntax.
+
+## Extensible Query Parameters
+
+
+
+If a unknown amount of query parameters can be added to a request, the `name`
+must be `fields...`, with the trailing ellipses representing the possibility
+of more fields.
+
+Example:
+
+```
+ - in: query
+ name: fields...
+ type: string
+```
+
+## Using oneOf to provide type alternatives
+
+
+
+`oneOf` (available in JSON Schema and Swagger/OpenAPI v3 but not in v2)
+is used in cases when a simpler type specification as a list of types
+doesn't work, as in the following example:
+```
+ properties:
+ old: # compliant with old Swagger
+ type:
+ - string
+ - object # Cannot specify a schema here
+ new: # uses oneOf extension
+ oneOf:
+ - type: string
+ - type: object
+ title: CustomSchemaForTheWin
+ properties:
+ ...
+```
diff --git a/api/push-gateway/push_notifier.yaml b/api/push-gateway/push_notifier.yaml
index 0effef08..4a6cb8f7 100644
--- a/api/push-gateway/push_notifier.yaml
+++ b/api/push-gateway/push_notifier.yaml
@@ -1,4 +1,5 @@
# 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.
@@ -19,7 +20,7 @@ host: localhost:8008
schemes:
- https
- http
-basePath: /_matrix/push/%CLIENT_MAJOR_VERSION%
+basePath: /_matrix/push/v1
consumes:
- application/json
produces:
@@ -38,15 +39,15 @@ paths:
Notifications about a particular event will normally cause the user to be
alerted in some way. It is therefore necessary to perform duplicate
- suppression for such notifications using the `event_id` field to avoid
+ suppression for such notifications using the ``event_id`` field to avoid
retries of this HTTP API causing duplicate alerts. The operation of
updating counts of unread notifications should be idempotent and
therefore do not require duplicate suppression.
- Notifications are sent to the URL configured when the pusher is
- created. This means that the HTTP path may be different depending on the
- push gateway.
-
+ Notifications are sent to the URL configured when the pusher is created.
+ This means that the HTTP path may be different depending on the push
+ gateway.
+ operationId: notify
parameters:
- in: body
name: notification
@@ -54,38 +55,37 @@ paths:
required: true
schema:
type: object
- example: |-
- {
- "notification": {
- "id": "$3957tyerfgewrf384",
- "room_id": "!slw48wfj34rtnrf:example.com",
- "type": "m.room.message",
- "sender": "@exampleuser:matrix.org",
- "sender_display_name": "Major Tom",
- "room_name": "Mission Control",
- "room_alias": "#exampleroom:matrix.org",
- "prio": "high",
- "content": {
- "msgtype": "m.text",
- "body": "I'm floating in a most peculiar way."
- },
- "counts": {
- "unread" : 2,
- "missed_calls": 1
- },
- "devices": [
- {
- "app_id": "org.matrix.matrixConsole.ios",
- "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/",
- "pushkey_ts": 12345678,
- "data" : {},
- "tweaks": {
- "sound": "bing"
- }
+ example: {
+ "notification": {
+ "id": "$3957tyerfgewrf384",
+ "room_id": "!slw48wfj34rtnrf:example.com",
+ "type": "m.room.message",
+ "sender": "@exampleuser:matrix.org",
+ "sender_display_name": "Major Tom",
+ "room_name": "Mission Control",
+ "room_alias": "#exampleroom:matrix.org",
+ "prio": "high",
+ "content": {
+ "msgtype": "m.text",
+ "body": "I'm floating in a most peculiar way."
+ },
+ "counts": {
+ "unread" : 2,
+ "missed_calls": 1
+ },
+ "devices": [
+ {
+ "app_id": "org.matrix.matrixConsole.ios",
+ "pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/",
+ "pushkey_ts": 12345678,
+ "data" : {},
+ "tweaks": {
+ "sound": "bing"
}
- ]
- }
+ }
+ ]
}
+ }
required: ["notification"]
properties:
notification:
@@ -112,14 +112,10 @@ paths:
type: string
description: |-
The type of the event as in the event's ``type`` field.
- Required if the notification relates to a specific
- Matrix event.
sender:
type: string
description: |-
The sender of the event as in the corresponding event field.
- Required if the notification relates to a specific
- Matrix event.
sender_display_name:
type: string
description: |-
@@ -149,15 +145,16 @@ paths:
type: object
title: EventContent
description: |-
- The ``content`` field from the event, if present. If the
- event had no content field, this field is omitted.
+ The ``content`` field from the event, if present. The pusher
+ may omit this if the event had no content or for any other
+ reason.
counts:
type: object
title: Counts
description: |-
This is a dictionary of the current number of unacknowledged
communications for the recipient user. Counts whose value is
- zero are omitted.
+ zero should be omitted.
properties:
unread:
type: integer
@@ -181,10 +178,10 @@ paths:
app_id:
type: string
description: |-
- The app_id given when the pusher was created.
+ The ``app_id`` given when the pusher was created.
pushkey:
type: string
- description: The pushkey given when the pusher was created.
+ description: The ``pushkey`` given when the pusher was created.
pushkey_ts:
type: integer
description: |-
@@ -203,14 +200,14 @@ paths:
description: |-
A dictionary of customisations made to the way this
notification is to be presented. These are added by push rules.
+ required: ['app_id', 'pushkey']
responses:
200:
description: A list of rejected push keys.
examples:
- application/json: |-
- {
- "rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ]
- }
+ application/json: {
+ "rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ]
+ }
schema:
type: object # empty json object
properties:
@@ -224,7 +221,8 @@ paths:
pushkeys and remove the associated pushers. It may not
necessarily be the notification in the request that failed:
it could be that a previous notification to the same pushkey
- failed.
+ failed. May be empty.
items:
type: string
- description: A pushkey
+ description: A pushkey that has been rejected.
+ required: ['rejected']
diff --git a/api/server-server/backfill.yaml b/api/server-server/backfill.yaml
new file mode 100644
index 00000000..6b3cfaef
--- /dev/null
+++ b/api/server-server/backfill.yaml
@@ -0,0 +1,148 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Events API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/backfill/{roomId}":
+ get:
+ summary: Retrieves the events which precede the given event
+ description: |-
+ Retrieves a sliding-window history of previous PDUs that occurred in the given room.
+ Starting from the PDU ID(s) given in the ``v`` argument, the PDUs that preceded it
+ are retrieved, up to the total number given by the ``limit``.
+ operationId: backfillRoom
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID to backfill.
+ required: true
+ x-example: "!SomeRoom:matrix.org"
+ - in: query
+ name: v
+ type: array
+ items:
+ type: string
+ description: The event IDs to backfill from.
+ required: true
+ x-example: ["$abc123:matrix.org"]
+ - in: query
+ name: limit
+ type: integer
+ description: The maximum number of PDUs to retrieve, including the given events.
+ required: true
+ x-example: 2
+ responses:
+ 200:
+ description: |-
+ A transaction containing the PDUs that preceded the given event(s), including the given
+ event(s), up to the given limit.
+ schema:
+ $ref: "definitions/transaction.yaml"
+ # Override the example to show the response of the request a bit better
+ examples:
+ application/json: {
+ "$ref": "examples/transaction.json",
+ "pdus": [
+ {
+ "$ref": "pdu.json",
+ "room_id": "!SomeRoom:matrix.org",
+ "event_id": "$abc123:matrix.org"
+ },
+ {
+ "$ref": "pdu.json",
+ "room_id": "!SomeRoom:matrix.org"
+ },
+ ]
+ }
+ "/get_missing_events/{roomId}":
+ post:
+ summary: Retrieves events that the sender is missing
+ description: |-
+ Retrieves previous events that the sender is missing. This is done by doing a breadth-first
+ walk of the ``prev_events`` for the ``latest_events``, ignoring any events in ``earliest_events``
+ and stopping at the ``limit``.
+ operationId: getMissingPreviousEvents
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID to search in.
+ required: true
+ x-example: "!SomeRoom:matrix.org"
+ - in: body
+ name: body
+ schema:
+ type: object
+ properties:
+ limit:
+ type: integer
+ description: The maximum number of events to retrieve. Defaults to 10.
+ example: 10
+ min_depth:
+ type: integer
+ description: The minimum depth of events to retrieve. Defaults to 0.
+ example: 0
+ earliest_events:
+ type: array
+ description: |-
+ The latest events that the sender already has. These are skipped when retrieving
+ the previous events of ``latest_events``.
+ items:
+ type: string
+ example: ["$missing_event:domain.com"]
+ latest_events:
+ type: array
+ description: The events to retrieve the previous events for.
+ items:
+ type: string
+ example: ["$event_that_has_the_missing_event_as_a_previous_event:domain.com"]
+ required: ['earliest_events', 'latest_events']
+ responses:
+ 200:
+ description: |-
+ The previous events for ``latest_events``, excluding any ``earliest_events``, up to the
+ provided ``limit``.
+ schema:
+ type: object
+ properties:
+ events:
+ type: array
+ description: The missing events.
+ items:
+ $ref: definitions/pdu.yaml
+ required: ['events']
+ examples:
+ application/json: {
+ "events": [
+ {"$ref": "examples/pdu.json"}
+ ]
+ }
diff --git a/api/server-server/definitions/edu.yaml b/api/server-server/definitions/edu.yaml
new file mode 100644
index 00000000..0e4edcc6
--- /dev/null
+++ b/api/server-server/definitions/edu.yaml
@@ -0,0 +1,28 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+title: Ephemeral Data Unit
+description: An ephemeral data unit.
+example:
+ $ref: "../examples/edu.json"
+properties:
+ edu_type:
+ type: string
+ description: The type of ephemeral message.
+ example: "m.presence"
+ content:
+ type: object
+ description: The content of the ephemeral message.
+required: ['edu_type', 'content']
\ No newline at end of file
diff --git a/api/server-server/definitions/event-schemas/m.presence.yaml b/api/server-server/definitions/event-schemas/m.presence.yaml
new file mode 100644
index 00000000..7f47add4
--- /dev/null
+++ b/api/server-server/definitions/event-schemas/m.presence.yaml
@@ -0,0 +1,71 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+title: m.presence
+description: |-
+ An EDU representing presence updates for users of the sending homeserver.
+allOf:
+ - $ref: ../edu.yaml
+ - type: object
+ properties:
+ edu_type:
+ type: enum
+ enum: ['m.presence']
+ description: The string ``m.presence``
+ example: "m.presence"
+ content:
+ type: object
+ description: The presence updates and requests.
+ title: Presence Update
+ properties:
+ push:
+ type: array
+ description: |-
+ A list of presence updates that the receiving server is likely
+ to be interested in.
+ items:
+ type: object
+ title: User Presence Update
+ properties:
+ user_id:
+ type: string
+ description: The user ID this presence EDU is for.
+ example: "@john:matrix.org"
+ presence:
+ type: enum
+ enum: ['offline', 'unavailable', 'online']
+ description: The presence of the user.
+ example: "online"
+ status_msg:
+ type: string
+ description: An optional description to accompany the presence.
+ example: "Making cupcakes"
+ last_active_ago:
+ type: integer
+ format: int64
+ description: |-
+ The number of milliseconds that have elapsed since the user
+ last did something.
+ example: 5000
+ currently_active:
+ type: boolean
+ description: |-
+ True if the user is likely to be interacting with their
+ client. This may be indicated by the user having a
+ ``last_active_ago`` within the last few minutes. Defaults
+ to false.
+ example: true
+ required: ['user_id', 'presence', 'last_active_ago']
+ required: ['push']
diff --git a/api/server-server/definitions/event-schemas/m.presence_accept.yaml b/api/server-server/definitions/event-schemas/m.presence_accept.yaml
new file mode 100644
index 00000000..3ba78b47
--- /dev/null
+++ b/api/server-server/definitions/event-schemas/m.presence_accept.yaml
@@ -0,0 +1,46 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+title: m.presence_accept
+description: |-
+ An EDU representing approval for the observing user to subscribe to the
+ presence of the the observed user.
+allOf:
+ - $ref: ../edu.yaml
+ - type: object
+ properties:
+ edu_type:
+ type: enum
+ enum: ['m.presence_accept']
+ description: The string ``m.presence_accept``
+ example: "m.presence_accept"
+ content:
+ type: object
+ description: The invite information.
+ title: Invite Information
+ properties:
+ observed_user:
+ type: string
+ description: |-
+ The user ID that has approved the ``observer_user`` to
+ subscribe to their presence.
+ example: "@alice:elsewhere.com"
+ observer_user:
+ type: string
+ description: |-
+ The user ID that requested to subscribe to the presence of
+ the ``observed_user``.
+ example: "@john:matrix.org"
+ required: ['observer_user', 'observed_user']
diff --git a/api/server-server/definitions/event-schemas/m.presence_deny.yaml b/api/server-server/definitions/event-schemas/m.presence_deny.yaml
new file mode 100644
index 00000000..2eb6feec
--- /dev/null
+++ b/api/server-server/definitions/event-schemas/m.presence_deny.yaml
@@ -0,0 +1,55 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+title: m.presence_deny
+description: |-
+ An EDU representing a declination or revocation for the observing user
+ to subscribe to the presence of the observed user.
+example: {
+ "origin": "domain.com",
+ "destination": "elsewhere.org",
+ "edu_type": "m.presence_deny",
+ "content": {
+ "observed_user": "@alice:elsewhere.org",
+ "observer_user": "@john:domain.com"
+ }
+}
+allOf:
+ - $ref: ../edu.yaml
+ - type: object
+ properties:
+ edu_type:
+ type: enum
+ enum: ['m.presence_deny']
+ description: The string ``m.presence_deny``
+ example: "m.presence_deny"
+ content:
+ type: object
+ description: The invite information.
+ title: Invite Information
+ properties:
+ observed_user:
+ type: string
+ description: |-
+ The user ID that has declined or revoked the ``observer_user`` from
+ subscribing to their presence.
+ example: "@alice:elsewhere.com"
+ observer_user:
+ type: string
+ description: |-
+ The user ID that requested to subscribe to the presence of
+ the ``observed_user``.
+ example: "@john:matrix.org"
+ required: ['observer_user', 'observed_user']
diff --git a/api/server-server/definitions/event-schemas/m.presence_invite.yaml b/api/server-server/definitions/event-schemas/m.presence_invite.yaml
new file mode 100644
index 00000000..a584897b
--- /dev/null
+++ b/api/server-server/definitions/event-schemas/m.presence_invite.yaml
@@ -0,0 +1,45 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+title: m.presence_invite
+description: |-
+ An EDU representing a request to subscribe to a user's presence.
+allOf:
+ - $ref: ../edu.yaml
+ - type: object
+ properties:
+ edu_type:
+ type: enum
+ enum: ['m.presence_invite']
+ description: The string ``m.presence_invite``
+ example: "m.presence_invite"
+ content:
+ type: object
+ description: The invite information.
+ title: Invite Information
+ properties:
+ observed_user:
+ type: string
+ description: |-
+ The user ID the ``observer_user`` would like to subscribe
+ to the presence of.
+ example: "@alice:elsewhere.com"
+ observer_user:
+ type: string
+ description: |-
+ The user ID that is wishing to subscribe to the presence of
+ the ``observed_user``.
+ example: "@john:matrix.org"
+ required: ['observer_user', 'observed_user']
diff --git a/api/server-server/definitions/event-schemas/m.receipt.yaml b/api/server-server/definitions/event-schemas/m.receipt.yaml
new file mode 100644
index 00000000..7f13ebee
--- /dev/null
+++ b/api/server-server/definitions/event-schemas/m.receipt.yaml
@@ -0,0 +1,82 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+title: m.receipt
+description: |-
+ An EDU representing receipt updates for users of the sending homeserver.
+ When receiving receipts, the server should only update entries that are
+ listed in the EDU. Receipts previously received that do not appear in the
+ EDU should not be removed or otherwise manipulated.
+allOf:
+ - $ref: ../edu.yaml
+ - type: object
+ properties:
+ edu_type:
+ type: enum
+ enum: ['m.receipt']
+ description: The string ``m.receipt``
+ example: "m.receipt"
+ content:
+ type: object
+ description: |-
+ Receipts for a particular room. The string key is the room ID for
+ which the receipts under it belong.
+ additionalProperties:
+ type: object
+ title: Room Receipts
+ properties:
+ # We strongly define the receipt type to help spec future ones later
+ # on. At that point, m.read can become optional (maybe).
+ "m.read":
+ type: object
+ description: Read receipts for users in the room.
+ title: User Read Receipt
+ properties:
+ event_ids:
+ type: array
+ description: |-
+ The extremity event IDs that the user has read up to.
+ minItems: 1
+ maxItems: 1
+ items:
+ type: string
+ example: ['$read_this_event:matrix.org']
+ data:
+ type: object
+ description: Metadata for the read receipt.
+ title: Read Receipt Metadata
+ properties:
+ ts:
+ type: integer
+ format: int64
+ description: |-
+ A POSIX timestamp in milliseconds for when the user read
+ the event specified in the read receipt.
+ example: 1533358089009
+ required: ['ts']
+ required: ['event_ids', 'data']
+ required: ['m.read']
+ example: {
+ "!some_room:domain.com": {
+ "m.read": {
+ "@john:matrix.org": {
+ "event_ids": ["$read_this_event:matrix.org"],
+ "data": {
+ "ts": 1533358089009
+ }
+ }
+ }
+ }
+ }
diff --git a/api/server-server/definitions/event-schemas/m.typing.yaml b/api/server-server/definitions/event-schemas/m.typing.yaml
new file mode 100644
index 00000000..ccbecf53
--- /dev/null
+++ b/api/server-server/definitions/event-schemas/m.typing.yaml
@@ -0,0 +1,46 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: object
+title: m.typing
+description: A typing notification EDU for a user in a room.
+allOf:
+ - $ref: ../edu.yaml
+ - type: object
+ properties:
+ edu_type:
+ type: enum
+ enum: ['m.typing']
+ description: The string ``m.typing``
+ example: "m.typing"
+ content:
+ type: object
+ description: The typing notification.
+ title: Typing Notification
+ properties:
+ room_id:
+ type: string
+ description: |-
+ The room where the user's typing status has been updated.
+ example: "!somewhere:matrix.org"
+ user_id:
+ type: string
+ description: |-
+ The user ID that has had their typing status changed.
+ example: "@john:matrix.org"
+ typing:
+ type: boolean
+ description: Whether the user is typing in the room or not.
+ example: true
+ required: ['room_id', 'user_id', 'typing']
diff --git a/api/server-server/definitions/invite_event.yaml b/api/server-server/definitions/invite_event.yaml
new file mode 100644
index 00000000..d196339a
--- /dev/null
+++ b/api/server-server/definitions/invite_event.yaml
@@ -0,0 +1,87 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: object
+title: Invite Event
+description: An invite event
+allOf:
+ - $ref: "pdu.yaml"
+ - type: object
+ properties:
+ # Note: we override a bunch of parameters to change their descriptions
+ sender:
+ type: string
+ # TODO: Verify/clarify this - it doesn't seem right, given this is a 'regular' invite
+ description: |-
+ The matrix ID of the user who sent the original ``m.room.third_party_invite``.
+ example: "@someone:example.org"
+ origin:
+ type: string
+ description: The name of the inviting homeserver.
+ example: "matrix.org"
+ origin_server_ts:
+ type: integer
+ format: int64
+ description: A timestamp added by the inviting homeserver.
+ example: 1234567890
+ type:
+ type: string
+ description: The value ``m.room.member``.
+ example: "m.room.member"
+ state_key:
+ type: string
+ description: The user ID of the invited member.
+ example: "@joe:elsewhere.com"
+ content:
+ type: object
+ title: Membership Event Content
+ description: |-
+ The content of the event, matching what is available in the
+ `Client-Server API`_. Must include a ``membership`` of ``invite``.
+ example: {"membership": "invite"}
+ properties:
+ membership:
+ type: string
+ description: The value ``invite``.
+ example: "invite"
+ required: ['membership']
+ auth_events:
+ type: array
+ description: |-
+ An event reference list containing the authorization events that would
+ allow the member to be invited to the room.
+ items:
+ type: array
+ maxItems: 2
+ minItems: 2
+ items:
+ - type: string
+ title: Event ID
+ example: "$abc123:matrix.org"
+ - type: object
+ title: Event Hash
+ example: {
+ "sha256": "abase64encodedsha256hashshouldbe43byteslong"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The event hash.
+ example: abase64encodedsha256hashshouldbe43byteslong
+ required: ['sha256']
+ redacts:
+ type: string
+ description: Not used.
+ required:
+ # Every other field is already flagged as required by the $ref
+ - state_key
diff --git a/api/server-server/definitions/keys.yaml b/api/server-server/definitions/keys.yaml
new file mode 100644
index 00000000..738e9e46
--- /dev/null
+++ b/api/server-server/definitions/keys.yaml
@@ -0,0 +1,110 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: object
+title: Server Keys
+description: Server keys
+example:
+ $ref: "../examples/server_key.json"
+properties:
+ server_name:
+ type: string
+ description: DNS name of the homeserver.
+ required: true
+ example: "example.org"
+ verify_keys:
+ type: object
+ description: |-
+ Public keys of the homeserver for verifying digital signatures.
+
+ The object's key is the algorithm and version combined (``ed25519`` being the
+ algorithm and ``abc123`` being the version in the example below). Together,
+ this forms the Key ID. The version must have characters matching the regular
+ expression ``[a-zA-Z0-9_]``.
+ required: true
+ additionalProperties:
+ type: object
+ title: Verify Key
+ example: {
+ "ed25519:abc123": {
+ "key": "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA"
+ }
+ }
+ properties:
+ key:
+ type: string
+ description: The `Unpadded Base64`_ encoded key.
+ required: true
+ example: "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA"
+ old_verify_keys:
+ type: object
+ description: |-
+ The public keys that the server used to use and when it stopped using them.
+
+ The object's key is the algorithm and version combined (``ed25519`` being the
+ algorithm and ``0ldK3y`` being the version in the example below). Together,
+ this forms the Key ID. The version must have characters matching the regular
+ expression ``[a-zA-Z0-9_]``.
+ additionalProperties:
+ type: object
+ title: Old Verify Key
+ example: {
+ "ed25519:0ldK3y": {
+ "expired_ts": 1532645052628,
+ "key": "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg"
+ }
+ }
+ properties:
+ expired_ts:
+ type: integer
+ format: int64
+ description: POSIX timestamp in milliseconds for when this key expired.
+ required: true
+ example: 1532645052628
+ key:
+ type: string
+ description: The `Unpadded Base64`_ encoded key.
+ required: true
+ example: "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg"
+ signatures:
+ type: object
+ description: Digital signatures for this object signed using the ``verify_keys``.
+ additionalProperties:
+ type: object
+ title: Signed Server
+ example: {
+ "example.org": {
+ "ad25519:abc123": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU"
+ }
+ }
+ additionalProperties:
+ type: string
+ name: Encoded Signature Verification Key
+ tls_fingerprints:
+ type: array
+ description: Hashes of X.509 TLS certificates used by this server.
+ items:
+ type: object
+ title: TLS Fingerprint
+ properties:
+ sha256:
+ type: string
+ description: The `Unpadded Base64`_ encoded fingerprint.
+ example: "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw"
+ valid_until_ts:
+ type: integer
+ format: int64
+ description: |-
+ POSIX timestamp when the list of valid keys should be refreshed. Keys used beyond this
+ timestamp are no longer valid.
+ example: 1052262000000
diff --git a/api/server-server/definitions/keys_query_response.yaml b/api/server-server/definitions/keys_query_response.yaml
new file mode 100644
index 00000000..52ad506c
--- /dev/null
+++ b/api/server-server/definitions/keys_query_response.yaml
@@ -0,0 +1,27 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: object
+description: Server keys
+example: {
+ "server_keys": [{
+ $ref: "../examples/server_key_notary_signed.json"
+ }]
+}
+properties:
+ server_keys:
+ type: array
+ title: Server Keys
+ description: The queried server's keys, signed by the notary server.
+ items:
+ $ref: "keys.yaml"
diff --git a/api/server-server/definitions/pdu.yaml b/api/server-server/definitions/pdu.yaml
new file mode 100644
index 00000000..bb14ede2
--- /dev/null
+++ b/api/server-server/definitions/pdu.yaml
@@ -0,0 +1,52 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: object
+title: Persistent Data Unit
+description: A persistent data unit (event)
+example:
+ $ref: "../examples/pdu.json"
+allOf:
+ - $ref: "unsigned_pdu.yaml"
+ - type: object
+ properties:
+ hashes:
+ type: object
+ title: Event Hash
+ description: Hashes of the PDU, following the algorithm specified in `Signing Events`_.
+ example: {
+ "sha256": "thishashcoversallfieldsincasethisisredacted"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The hash.
+ example: thishashcoversallfieldsincasthisisredacted
+ required: ['sha256']
+ signatures:
+ type: object
+ description: |-
+ Signatures for the PDU, following the algorithm specified in `Signing Events`_.
+ example: {
+ "example.com": {
+ "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus"
+ }
+ }
+ additionalProperties:
+ type: object
+ title: Server Signatures
+ additionalProperties:
+ type: string
+ required:
+ - hashes
+ - signatures
diff --git a/api/server-server/definitions/security.yaml b/api/server-server/definitions/security.yaml
new file mode 100644
index 00000000..6c9cc808
--- /dev/null
+++ b/api/server-server/definitions/security.yaml
@@ -0,0 +1,19 @@
+# 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.
+signedRequest:
+ type: apiKey
+ description: |-
+ The ``Authorization`` header defined in the `Authentication`_ section.
+ name: Authorization
+ in: header
diff --git a/api/server-server/definitions/transaction.yaml b/api/server-server/definitions/transaction.yaml
new file mode 100644
index 00000000..7df8b646
--- /dev/null
+++ b/api/server-server/definitions/transaction.yaml
@@ -0,0 +1,37 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: object
+title: Transaction
+description: Transaction
+example:
+ $ref: "../examples/transaction.json"
+properties:
+ origin:
+ type: string
+ description: |-
+ The ``server_name`` of the homeserver sending this transaction.
+ example: "example.org"
+ origin_server_ts:
+ type: integer
+ format: int64
+ description: |-
+ POSIX timestamp in milliseconds on originating homeserver when this
+ transaction started.
+ example: 1532991320875
+ pdus:
+ type: array
+ description: List of persistent updates to rooms.
+ items:
+ $ref: "pdu.yaml"
+required: ['origin', 'origin_server_ts', 'pdus']
diff --git a/api/server-server/definitions/unsigned_pdu.yaml b/api/server-server/definitions/unsigned_pdu.yaml
new file mode 100644
index 00000000..64991d22
--- /dev/null
+++ b/api/server-server/definitions/unsigned_pdu.yaml
@@ -0,0 +1,155 @@
+# Copyright 2018 New Vector Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+type: object
+title: Unsigned Persistent Data Unit
+description: An unsigned persistent data unit (event)
+example:
+ $ref: "../examples/unsigned_pdu.json"
+properties:
+ event_id:
+ type: string
+ description: The event ID for the PDU.
+ example: "$a4ecee13e2accdadf56c1025:example.com"
+ room_id:
+ type: string
+ description: Room identifier.
+ example: "!abc123:matrix.org"
+ sender:
+ type: string
+ description: The ID of the user sending the event.
+ example: "@someone:matrix.org"
+ origin:
+ type: string
+ description: The ``server_name`` of the homeserver that created this event.
+ example: "matrix.org"
+ origin_server_ts:
+ type: integer
+ format: int64
+ description: Timestamp in milliseconds on origin homeserver when this event was created.
+ example: 1234567890
+ type:
+ type: string
+ description: Event type
+ example: "m.room.message"
+ state_key:
+ type: string
+ description: |-
+ If this key is present, the event is a state event, and it will replace previous events
+ with the same ``type`` and ``state_key`` in the room state.
+ example: "my_key"
+ content:
+ type: object
+ description: The content of the event.
+ example: {"key": "value"}
+ prev_events:
+ type: array
+ description: |-
+ Event IDs and hashes of the most recent events in the room that the homeserver was aware
+ of when it made this event.
+ items:
+ type: array
+ maxItems: 2
+ minItems: 2
+ items:
+ - type: string
+ title: Event ID
+ example: "$abc123:matrix.org"
+ - type: object
+ title: Event Hash
+ example: {
+ "sha256": "abase64encodedsha256hashshouldbe43byteslong"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The event hash.
+ example: abase64encodedsha256hashshouldbe43byteslong
+ required: ['sha256']
+ depth:
+ type: integer
+ description: |-
+ The maximum depth of the ``prev_events``, plus one. Must be less than the
+ maximum value for an integer (2^63 - 1). If the room's depth is already at
+ the limit, the depth must be set to the limit.
+ example: 12
+ auth_events:
+ type: array
+ description: |-
+ An event reference list containing the authorization events that would
+ allow this event to be in the room.
+ items:
+ type: array
+ maxItems: 2
+ minItems: 2
+ items:
+ - type: string
+ title: Event ID
+ example: "$abc123:matrix.org"
+ - type: object
+ title: Event Hash
+ example: {
+ "sha256": "abase64encodedsha256hashshouldbe43byteslong"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The event hash.
+ example: abase64encodedsha256hashshouldbe43byteslong
+ required: ['sha256']
+ redacts:
+ type: string
+ description: For redaction events, the ID of the event being redacted.
+ example: "$def456:matrix.org"
+ unsigned:
+ type: object
+ title: Example Unsigned Data
+ description: |-
+ Additional data added by the origin server but not covered by the ``signatures``. More
+ keys than those defined here may be used.
+ example: {"key": "value"}
+ properties:
+ age:
+ type: integer
+ description: The number of milliseconds that have passed since this message was sent.
+ example: 4612
+ replaces_state:
+ type: string
+ description: The event ID of the state event this event replaces.
+ example: "$state_event:domain.com"
+ prev_sender:
+ type: string
+ description: The sender of the replaced state event.
+ example: "@someone:domain.com"
+ prev_content:
+ type: object
+ description: The content of the replaced state event.
+ example: {
+ "membership": "join",
+ "displayname": "Bob"
+ }
+ redacted_because:
+ type: string
+ description: A reason for why the event was redacted.
+ example: "Inappropriate content"
+required:
+ - event_id
+ - room_id
+ - sender
+ - origin
+ - origin_server_ts
+ - type
+ - content
+ - prev_events
+ - depth
+ - auth_events
diff --git a/api/server-server/event_auth.yaml b/api/server-server/event_auth.yaml
new file mode 100644
index 00000000..8857131f
--- /dev/null
+++ b/api/server-server/event_auth.yaml
@@ -0,0 +1,180 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Event Authorization API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/event_auth/{roomId}/{eventId}":
+ get:
+ summary: Get the auth chain for a given event
+ description: |-
+ Retrieves the complete auth chain for a given event.
+ operationId: getEventAuth
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID to get the auth chain for.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: path
+ name: eventId
+ type: string
+ description: The event ID to get the auth chain of.
+ required: true
+ x-example: "$helloworld:domain.com"
+ responses:
+ 200:
+ description: The auth chain for the event.
+ schema:
+ type: object
+ properties:
+ auth_chain:
+ type: array
+ description: |-
+ The full set of authorization events that make up the state of
+ the room, and their authorization events, recursively.
+ items:
+ $ref: "definitions/pdu.yaml"
+ example: [{"$ref": "examples/pdu.json"}]
+ required: ['auth_chain']
+ "/query_auth/{roomId}/{eventId}":
+ post:
+ summary: Compare auth chains with the receiving server
+ description: |-
+ Compares the auth chain provided with what the receiving server has for the
+ room ID and event ID combination.
+
+ The auth difference can be calculated in two parts, where the "remote auth"
+ is the auth chain provided by the sending server and the "local auth" is the
+ auth chain the receiving server has. With those lists, the algorithm works
+ bottom-up after sorting each chain by depth then by event ID. The differences
+ are then discovered and returned as the response to this API call.
+ operationId: compareEventAuth
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID to compare the auth chain in.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: path
+ name: eventId
+ type: string
+ description: The event ID to compare the auth chain of.
+ required: true
+ x-example: "$helloworld:domain.com"
+ - in: body
+ name: body
+ schema:
+ type: object
+ properties:
+ auth_chain:
+ type: array
+ description: The auth chain (the "remote auth").
+ items:
+ $ref: "definitions/pdu.yaml"
+ example: [{"$ref": "examples/pdu.json"}]
+ missing:
+ type: array
+ description: |-
+ A list of event IDs that the sender thinks the receiver is missing.
+ items:
+ type: string
+ example: []
+ rejects:
+ type: object
+ description: |-
+ The set of events that the sending server has rejected from the provided
+ auth chain.
+
+ The ``string`` key is the event ID that was rejected.
+ additionalProperties:
+ type: object
+ title: Rejection Reason
+ properties:
+ reason:
+ type: enum
+ enum: ['auth_error', 'replaced', 'not_ancestor']
+ description: |-
+ The reason for the event being rejected.
+ required: ['reason']
+ example: {
+ "$some_event:domain.com": {
+ "reason": "auth_error"
+ }
+ }
+ required: ['auth_chain']
+ responses:
+ 200:
+ description: The auth chain differences, as determined by the receiver.
+ schema:
+ type: object
+ properties:
+ auth_chain:
+ type: array
+ description: |-
+ The auth chain the receiver has, and used to determine the auth
+ chain differences (the "local auth").
+ items:
+ $ref: "definitions/pdu.yaml"
+ example: [{"$ref": "examples/pdu.json"}]
+ missing:
+ type: array
+ description: |-
+ The list of event IDs that the receiver believes it is missing,
+ after comparing the "remote auth" and "local auth" chains.
+ items:
+ type: string
+ example: ["$a_missing_event:domain.com"]
+ rejects:
+ type: object
+ description: |-
+ The set of events that the receiving server has rejected from the
+ auth chain, not including events that the sending server is missing
+ as determined from the difference algorithm.
+
+ The ``string`` key is the event ID that was rejected.
+ additionalProperties:
+ type: object
+ title: Rejection Reason
+ properties:
+ reason:
+ type: enum
+ enum: ['auth_error', 'replaced', 'not_ancestor']
+ description: |-
+ The reason for the event being rejected.
+ required: ['reason']
+ example: {
+ "$some_event:domain.com": {
+ "reason": "auth_error"
+ }
+ }
+ required: ['auth_chain', 'missing', 'rejects']
diff --git a/api/server-server/events.yaml b/api/server-server/events.yaml
new file mode 100644
index 00000000..cf3988a2
--- /dev/null
+++ b/api/server-server/events.yaml
@@ -0,0 +1,139 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Events API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/state/{roomId}":
+ get:
+ summary: Get all the state of a given room
+ description: |-
+ Retrieves a snapshot of a room's state at a given event.
+ operationId: getRoomState
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID to get state for.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: query
+ name: event_id
+ type: string
+ description: An event ID in the room to retrieve the state at.
+ required: true
+ x-example: "$helloworld:matrix.org"
+ responses:
+ 200:
+ description: |-
+ The fully resolved state for the room, including the authorization
+ chain for the events.
+ schema:
+ type: object
+ properties:
+ auth_chain:
+ type: array
+ description: |-
+ The full set of authorization events that make up the state
+ of the room, and their authorization events, recursively.
+ items:
+ $ref: "definitions/pdu.yaml"
+ example: [{"$ref": "examples/pdu.json"}]
+ pdus:
+ type: array
+ description: |-
+ The fully resolved state of the room at the given event.
+ items:
+ $ref: "definitions/pdu.yaml"
+ example: [{"$ref": "examples/pdu.json"}]
+ required: ['auth_chain', 'pdus']
+ "/state_ids/{roomId}":
+ get:
+ summary: Get all the state event IDs of a given room
+ description: |-
+ Retrieves a snapshot of a room's state at a given event, in the form of
+ event IDs. This performs the same function as calling ``/state/{roomId}``,
+ however this returns just the event IDs rather than the full events.
+ operationId: getRoomStateIds
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID to get state for.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: query
+ name: event_id
+ type: string
+ description: An event ID in the room to retrieve the state at.
+ required: true
+ x-example: "$helloworld:matrix.org"
+ responses:
+ 200:
+ description: |-
+ The fully resolved state for the room, including the authorization
+ chain for the events.
+ schema:
+ type: object
+ properties:
+ auth_chain_ids:
+ type: array
+ description: |-
+ The full set of authorization events that make up the state
+ of the room, and their authorization events, recursively.
+ items:
+ type: string
+ example: ["$an_event:domain.com"]
+ pdu_ids:
+ type: array
+ description: |-
+ The fully resolved state of the room at the given event.
+ items:
+ type: string
+ example: ["$an_event:domain.com"]
+ required: ['auth_chain_ids', 'pdu_ids']
+ "/event/{eventId}":
+ get:
+ summary: Get a single event
+ description: |-
+ Retrieves a single event.
+ operationId: getEvent
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: eventId
+ type: string
+ description: The event ID to get.
+ required: true
+ x-example: "$abc123:matrix.org"
+ responses:
+ 200:
+ description: A transaction containing a single PDU which is the event requested.
+ schema:
+ $ref: "definitions/transaction.yaml"
diff --git a/api/server-server/examples/edu.json b/api/server-server/examples/edu.json
new file mode 100644
index 00000000..f5a58e21
--- /dev/null
+++ b/api/server-server/examples/edu.json
@@ -0,0 +1,6 @@
+{
+ "edu_type": "m.presence",
+ "content": {
+ "key": "value"
+ }
+}
\ No newline at end of file
diff --git a/api/server-server/examples/pdu.json b/api/server-server/examples/pdu.json
new file mode 100644
index 00000000..81981b23
--- /dev/null
+++ b/api/server-server/examples/pdu.json
@@ -0,0 +1,11 @@
+{
+ "$ref": "unsigned_pdu.json",
+ "hashes": {
+ "sha256": "thishashcoversallfieldsincasethisisredacted"
+ },
+ "signatures": {
+ "example.com": {
+ "ed25519:key_version:": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus"
+ }
+ }
+}
\ No newline at end of file
diff --git a/api/server-server/examples/server_key.json b/api/server-server/examples/server_key.json
new file mode 100644
index 00000000..bebd8445
--- /dev/null
+++ b/api/server-server/examples/server_key.json
@@ -0,0 +1,23 @@
+{
+ "server_name": "example.org",
+ "verify_keys": {
+ "ed25519:abc123": {
+ "key": "VGhpcyBzaG91bGQgYmUgYSByZWFsIGVkMjU1MTkgcGF5bG9hZA"
+ }
+ },
+ "old_verify_keys": {
+ "ed25519:0ldk3y": {
+ "expired_ts": 1532645052628,
+ "key": "VGhpcyBzaG91bGQgYmUgeW91ciBvbGQga2V5J3MgZWQyNTUxOSBwYXlsb2FkLg"
+ }
+ },
+ "signatures": {
+ "example.org": {
+ "ed25519:auto2": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU"
+ }
+ },
+ "tls_fingerprints": [{
+ "sha256": "VGhpcyBpcyBoYXNoIHdoaWNoIHNob3VsZCBiZSBieXRlcw"
+ }],
+ "valid_until_ts": 1652262000000
+}
\ No newline at end of file
diff --git a/api/server-server/examples/server_key_notary_signed.json b/api/server-server/examples/server_key_notary_signed.json
new file mode 100644
index 00000000..d3a461ba
--- /dev/null
+++ b/api/server-server/examples/server_key_notary_signed.json
@@ -0,0 +1,11 @@
+{
+ "$ref": "server_key.json",
+ "signatures": {
+ "example.org": {
+ "ed25519:abc123": "VGhpcyBzaG91bGQgYWN0dWFsbHkgYmUgYSBzaWduYXR1cmU"
+ },
+ "notary.server.com": {
+ "ed25519:010203": "VGhpcyBpcyBhbm90aGVyIHNpZ25hdHVyZQ"
+ }
+ }
+}
\ No newline at end of file
diff --git a/api/server-server/examples/transaction.json b/api/server-server/examples/transaction.json
new file mode 100644
index 00000000..bd8ac3dc
--- /dev/null
+++ b/api/server-server/examples/transaction.json
@@ -0,0 +1,5 @@
+{
+ "origin": "matrix.org",
+ "origin_server_ts": 1234567890,
+ "pdus": [{"$ref": "pdu.json"}]
+}
\ No newline at end of file
diff --git a/api/server-server/examples/unsigned_pdu.json b/api/server-server/examples/unsigned_pdu.json
new file mode 100644
index 00000000..f4d2e749
--- /dev/null
+++ b/api/server-server/examples/unsigned_pdu.json
@@ -0,0 +1,27 @@
+{
+ "room_id": "!UcYsUzyxTGDxLBEvLy:example.org",
+ "sender": "@alice:example.com",
+ "origin": "example.com",
+ "event_id": "$a4ecee13e2accdadf56c1025:example.com",
+ "origin_server_ts": 1404838188000,
+ "depth": 12,
+ "auth_events": [
+ [
+ "$af232176:example.org",
+ {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}
+ ]
+ ],
+ "type": "m.room.message",
+ "prev_events": [
+ [
+ "$af232176:example.org",
+ {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}
+ ]
+ ],
+ "content": {
+ "key": "value"
+ },
+ "unsigned": {
+ "age": 4612
+ }
+}
\ No newline at end of file
diff --git a/api/server-server/invites.yaml b/api/server-server/invites.yaml
new file mode 100644
index 00000000..6d905e17
--- /dev/null
+++ b/api/server-server/invites.yaml
@@ -0,0 +1,213 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Invite User To Room API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/invite/{roomId}/{eventId}":
+ put:
+ summary: Invites a remote user to a room
+ description: |-
+ Invites a remote user to a room. Once the event has been signed by both the inviting
+ homeserver and the invited homeserver, it can be sent to all of the servers in the
+ room by the inviting homeserver.
+ operationId: sendInvite
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID that the user is being invited to.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: path
+ name: eventId
+ type: string
+ description: The event ID for the invite event, generated by the inviting server.
+ required: true
+ x-example: "$abc123:example.org"
+ - in: body
+ name: body
+ type: object
+ required: true
+ schema:
+ allOf:
+ - $ref: "definitions/invite_event.yaml"
+ - type: object
+ properties:
+ unsigned:
+ type: object
+ title: Unsigned Event Content
+ description: |-
+ Information included alongside the event that is not signed. May include more
+ than what is listed here.
+ properties:
+ invite_room_state:
+ type: array
+ description: |-
+ An optional list of simplified events to help the receiver of the invite
+ identify the room. The recommended events to include are the join rules,
+ canonical alias, avatar, and name of the room.
+ items:
+ type: object
+ title: Invite Room State Event
+ properties:
+ type:
+ type: string
+ description: The type of event.
+ example: "m.room.join_rules"
+ state_key:
+ type: string
+ description: The state key for the event. May be an empty string.
+ example: ""
+ content:
+ type: object
+ description: The content for the event.
+ sender:
+ type: string
+ description: The sender of the event.
+ example: "@someone:matrix.org"
+ required: ['type', 'state_key', 'content', 'sender']
+ example: [
+ {
+ "type": "m.room.join_rules",
+ "sender": "@someone:matrix.org",
+ "state_key": "",
+ "content": {
+ "join_rule": "public"
+ }
+ }
+ ]
+ example: {
+ "$ref": "examples/pdu.json",
+ "type": "m.room.member",
+ "state_key": "@joe:elsewhere.com",
+ "unsigned": {
+ "invite_room_state": [
+ {
+ "type": "m.room.join_rules",
+ "sender": "@someone:matrix.org",
+ "state_key": "",
+ "content": {
+ "join_rule": "public"
+ }
+ },
+ {
+ "type": "m.room.name",
+ "sender": "@someone:matrix.org",
+ "state_key": "",
+ "content": {
+ "name": "Cool New Room"
+ }
+ }
+ ]
+ },
+ "content": {
+ "membership": "invite"
+ },
+ "signatures": {
+ "example.com": {
+ "ed25519:key_version": "SomeSignatureHere"
+ },
+ }
+ }
+ responses:
+ 200:
+ description: |-
+ The event with the invited server's signature added. All other fields of the events
+ should remain untouched.
+ schema:
+ type: array
+ minItems: 2
+ maxItems: 2
+ items:
+ - type: integer
+ description: The value ``200``.
+ example: 200
+ - type: object
+ description: An object containing the signed invite event.
+ title: Event Container
+ properties:
+ event:
+ $ref: "definitions/invite_event.yaml"
+ required: ['event']
+ examples:
+ application/json: [
+ 200,
+ {
+ "event": {
+ "$ref": "examples/pdu.json",
+ "type": "m.room.member",
+ "state_key": "@someone:example.org",
+ "unsigned": {
+ "invite_room_state": [
+ {
+ "type": "m.room.join_rules",
+ "sender": "@someone:matrix.org",
+ "state_key": "",
+ "content": {
+ "join_rule": "public"
+ }
+ },
+ {
+ "type": "m.room.name",
+ "sender": "@someone:matrix.org",
+ "state_key": "",
+ "content": {
+ "name": "Cool New Room"
+ }
+ }
+ ]
+ },
+ "content": {
+ "membership": "invite"
+ },
+ "signatures": {
+ "example.com": {
+ "ed25519:key_version": "SomeSignatureHere"
+ },
+ "elsewhere.com": {
+ "ed25519:k3y_versi0n": "SomeOtherSignatureHere"
+ }
+ }
+ }
+ }
+ ]
+ 403:
+ description: |-
+ The invite is not allowed. This could be for a number of reasons, including:
+
+ * The sender is not allowed to send invites to the target user/homeserver.
+ * The homeserver does not permit anyone to invite its users.
+ * The homeserver refuses to participate in the room.
+ schema:
+ $ref: "../client-server/definitions/errors/error.yaml"
+ examples:
+ application/json: {
+ "errcode": "M_FORBIDDEN",
+ "error": "User cannot invite the target user."
+ }
diff --git a/api/server-server/joins.yaml b/api/server-server/joins.yaml
new file mode 100644
index 00000000..4902ea9e
--- /dev/null
+++ b/api/server-server/joins.yaml
@@ -0,0 +1,326 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Join Room API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/make_join/{roomId}/{userId}":
+ get:
+ summary: Get information required to make a join event for a room
+ description: |-
+ Asks the receiving server to return information that the sending
+ server will need to prepare a join event to get into the room.
+ operationId: makeJoin
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID that is about to be joined.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: path
+ name: userId
+ type: string
+ description: The user ID the join event will be for.
+ required: true
+ x-example: "@someone:example.org"
+ - in: query
+ type: array
+ items:
+ type: string
+ name: ver
+ description: |-
+ The room versions the sending server has support for. Defaults
+ to ``[1]``.
+ x-example: ["1", "2"]
+ responses:
+ 200:
+ description: |-
+ An unsigned event that the server may now use as a template
+ for the rest of the `Joining Rooms`_ handshake.
+ schema:
+ allOf:
+ - $ref: "definitions/unsigned_pdu.yaml"
+ - type: object
+ properties:
+ # Note: we override a bunch of parameters to change their descriptions
+ sender:
+ type: string
+ description: The user ID of the joining member.
+ example: "@someone:example.org"
+ origin:
+ type: string
+ description: The name of the resident homeserver.
+ example: "matrix.org"
+ origin_server_ts:
+ type: integer
+ format: int64
+ description: A timestamp added by the resident homeserver.
+ example: 1234567890
+ type:
+ type: string
+ description: The value ``m.room.member``.
+ example: "m.room.member"
+ state_key:
+ type: string
+ description: The user ID of the joining member.
+ example: "@someone:example.org"
+ content:
+ type: object
+ title: Membership Event Content
+ description: The content of the event.
+ example: {"membership": "join"}
+ properties:
+ membership:
+ type: string
+ description: The value ``join``.
+ example: "join"
+ required: ['membership']
+ depth:
+ type: integer
+ description: This field must be present but is ignored; it may be 0.
+ example: 12
+ auth_events:
+ type: array
+ description: |-
+ An event reference list containing the authorization events that would
+ allow the member to join the room. This should normally be the
+ ``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules``
+ events.
+ items:
+ type: array
+ maxItems: 2
+ minItems: 2
+ items:
+ - type: string
+ title: Event ID
+ example: "$abc123:matrix.org"
+ - type: object
+ title: Event Hash
+ example: {
+ "sha256": "abase64encodedsha256hashshouldbe43byteslong"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The event hash.
+ example: abase64encodedsha256hashshouldbe43byteslong
+ required: ['sha256']
+ redacts:
+ type: string
+ description: Not used.
+ required:
+ # Every other field is already flagged as required by the $ref
+ - state_key
+ examples:
+ application/json: {
+ "$ref": "examples/unsigned_pdu.json",
+ "type": "m.room.member",
+ "state_key": "@someone:example.org",
+ "content": {
+ "membership": "join"
+ },
+ "auth_events": [
+ ["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
+ ["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
+ ["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}]
+ ]
+ }
+ 400:
+ description: |-
+ The request is invalid or the room the server is attempting
+ to join has a version that is not listed in the ``ver``
+ parameters.
+
+ The error should be passed through to clients so that they
+ may give better feedback to users.
+ schema:
+ allOf:
+ - $ref: "../client-server/definitions/errors/error.yaml"
+ - type: object
+ properties:
+ room_version:
+ type: string
+ description: |-
+ The version of the room. Required if the ``errcode``
+ is ``M_INCOMPATIBLE_ROOM_VERSION``.
+ examples:
+ application/json: {
+ "errcode": "M_INCOMPATIBLE_ROOM_VERSION",
+ "error": "Your homeserver does not support the features required to join this room",
+ "room_version": "3"
+ }
+ "/send_join/{roomId}/{eventId}":
+ put:
+ summary: Submit a signed join event to a resident server
+ description: |-
+ Submits a signed join event to the resident server for it
+ to accept it into the room's graph.
+ operationId: sendJoin
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID that is about to be joined.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: path
+ name: eventId
+ type: string
+ description: The event ID for the join event.
+ required: true
+ x-example: "$abc123:example.org"
+ - in: body
+ name: body
+ type: object
+ required: true
+ schema:
+ allOf:
+ - $ref: "definitions/pdu.yaml"
+ - type: object
+ properties:
+ # Note: we override a bunch of parameters to change their descriptions
+ sender:
+ type: string
+ description: The user ID of the joining member.
+ example: "@someone:example.org"
+ origin:
+ type: string
+ description: The name of the joining homeserver.
+ example: "matrix.org"
+ origin_server_ts:
+ type: integer
+ format: int64
+ description: A timestamp added by the joining homeserver.
+ example: 1234567890
+ type:
+ type: string
+ description: The value ``m.room.member``.
+ example: "m.room.member"
+ state_key:
+ type: string
+ description: The user ID of the joining member.
+ example: "@someone:example.org"
+ content:
+ type: object
+ title: Membership Event Content
+ description: The content of the event.
+ example: {"membership": "join"}
+ properties:
+ membership:
+ type: string
+ description: The value ``join``.
+ example: "join"
+ required: ['membership']
+ depth:
+ type: integer
+ description: This field must be present but is ignored; it may be 0.
+ example: 12
+ auth_events:
+ type: array
+ description: |-
+ An event reference list containing the authorization events that would
+ allow the member to join the room.
+ items:
+ type: array
+ maxItems: 2
+ minItems: 2
+ items:
+ - type: string
+ title: Event ID
+ example: "$abc123:matrix.org"
+ - type: object
+ title: Event Hash
+ example: {
+ "sha256": "abase64encodedsha256hashshouldbe43byteslong"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The event hash.
+ example: abase64encodedsha256hashshouldbe43byteslong
+ required: ['sha256']
+ redacts:
+ type: string
+ description: Not used.
+ required:
+ # Every other field is already flagged as required by the $ref
+ - state_key
+ example: {
+ "$ref": "examples/pdu.json",
+ "type": "m.room.member",
+ "state_key": "@someone:example.org",
+ "content": {
+ "membership": "join"
+ }
+ }
+ responses:
+ 200:
+ description: |-
+ The full state for the room, having accepted the join event.
+ schema:
+ type: array
+ minItems: 2
+ maxItems: 2
+ items:
+ - type: integer
+ description: The value ``200``.
+ example: 200
+ - type: object
+ title: Room State
+ description: The state for the room.
+ properties:
+ origin:
+ type: string
+ description: The resident server's DNS name.
+ auth_chain:
+ type: array
+ description: The auth chain.
+ items:
+ type: object
+ schema:
+ $ref: "definitions/pdu.yaml"
+ state:
+ type: array
+ description: The room state.
+ items:
+ type: object
+ schema:
+ $ref: "definitions/pdu.yaml"
+ required: ["auth_chain", "state", "origin"]
+ examples:
+ application/json: [
+ 200,
+ {
+ "origin": "matrix.org",
+ "auth_chain": [{"$ref": "examples/pdu.json"}],
+ "state": [{"$ref": "examples/pdu.json"}]
+ }
+ ]
diff --git a/api/server-server/keys_query.yaml b/api/server-server/keys_query.yaml
new file mode 100644
index 00000000..e616915b
--- /dev/null
+++ b/api/server-server/keys_query.yaml
@@ -0,0 +1,131 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Key Exchange API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/key/v2
+consumes:
+ - application/json
+produces:
+ - application/json
+paths:
+ "/query/{serverName}/{keyId}":
+ get:
+ summary: Query for another server's keys
+ description: |-
+ Query for another server's keys. The receiving (notary) server must
+ sign the keys returned by the queried server.
+ operationId: perspectivesKeyQuery
+ parameters:
+ - in: path
+ name: serverName
+ type: string
+ description: The server's DNS name to query
+ required: true
+ x-example: matrix.org
+ - in: path
+ name: keyId
+ type: string
+ description: |-
+ **Deprecated**. Servers should not use this parameter and instead
+ opt to return all keys, not just the requested one. The key ID to
+ look up.
+ required: false
+ x-example: "ed25519:abc123"
+ - in: query
+ name: minimum_valid_until_ts
+ type: integer
+ format: int64
+ description: |-
+ A millisecond POSIX timestamp in milliseconds indicating when the returned
+ certificates will need to be valid until to be useful to the requesting server.
+
+ If not supplied, the current time as determined by the notary server is used.
+ required: false
+ x-example: 1234567890
+ responses:
+ 200:
+ description: |-
+ The keys for the server, or an empty array if the server could not be reached
+ and no cached keys were available.
+ schema:
+ $ref: "definitions/keys_query_response.yaml"
+ "/query":
+ post:
+ summary: Query for several server's keys
+ description: |-
+ Query for keys from multiple servers in a batch format. The receiving (notary)
+ server must sign the keys returned by the queried servers.
+ operationId: bulkPerspectivesKeyQuery
+ parameters:
+ - in: body
+ name: body
+ schema:
+ type: object
+ example: {
+ "server_keys": {
+ "example.org": {
+ "ed25519:abc123": {
+ "minimum_valid_until_ts": 1234567890
+ }
+ }
+ }
+ }
+ properties:
+ server_keys:
+ type: object
+ description: |-
+ The query criteria. The outer ``string`` key on the object is the
+ server name (eg: ``matrix.org``). The inner ``string`` key is the
+ Key ID to query for the particular server. If no key IDs are given
+ to be queried, the notary server should query for all keys. If no
+ servers are given, the notary server must return an empty ``server_keys``
+ array in the response.
+
+ The notary server may return multiple keys regardless of the Key IDs
+ given.
+ additionalProperties:
+ type: object
+ name: ServerName
+ description: The server names to query.
+ additionalProperties:
+ type: object
+ title: Query Criteria
+ description: The server key IDs to query.
+ properties:
+ minimum_valid_until_ts:
+ type: integer
+ format: int64
+ description: |-
+ A millisecond POSIX timestamp in milliseconds indicating when
+ the returned certificates will need to be valid until to be
+ useful to the requesting server.
+
+ If not supplied, the current time as determined by the notary
+ server is used.
+ example: 1234567890
+ required: ['server_keys']
+ responses:
+ 200:
+ description: |-
+ The keys for the queried servers, signed by the notary server. Servers which
+ are offline and have no cached keys will not be included in the result. This
+ may result in an empty array.
+ schema:
+ $ref: "definitions/keys_query_response.yaml"
diff --git a/api/server-server/keys_server.yaml b/api/server-server/keys_server.yaml
new file mode 100644
index 00000000..8734f2ed
--- /dev/null
+++ b/api/server-server/keys_server.yaml
@@ -0,0 +1,61 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Key Exchange API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/key/v2
+produces:
+ - application/json
+paths:
+ "/server/{keyId}":
+ get:
+ summary: Get the homeserver's public key(s)
+ description: |-
+ Gets the homeserver's published TLS fingerprints and signing keys.
+ The homeserver may have any number of active keys and may have a
+ number of old keys.
+
+ Intermediate notary servers should cache a response for half of its
+ lifetime to avoid serving a stale response. Originating servers should
+ avoid returning responses that expire in less than an hour to avoid
+ repeated reqests for a certificate that is about to expire. Requesting
+ servers should limit how frequently they query for certificates to
+ avoid flooding a server with requests.
+
+ If the server fails to respond to this request, intermediate notary
+ servers should continue to return the last response they received
+ from the server so that the signatures of old events can still be
+ checked.
+ operationId: getServerKey
+ parameters:
+ - in: path
+ name: keyId
+ type: string
+ description: |-
+ **Deprecated**. Servers should not use this parameter and instead
+ opt to return all keys, not just the requested one. The key ID to
+ look up.
+ required: false
+ x-example: "ed25519:abc123"
+ deprecated: true
+ responses:
+ 200:
+ description: The homeserver's keys
+ schema:
+ $ref: "definitions/keys.yaml"
diff --git a/api/server-server/leaving.yaml b/api/server-server/leaving.yaml
new file mode 100644
index 00000000..be08acba
--- /dev/null
+++ b/api/server-server/leaving.yaml
@@ -0,0 +1,274 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Leave Room API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/make_leave/{roomId}/{userId}":
+ get:
+ summary: Get information required to make a leave event for a room
+ description: |-
+ Asks the receiving server to return information that the sending
+ server will need to prepare a leave event to get out of the room.
+ operationId: makeLeave
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID that is about to be left.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: path
+ name: userId
+ type: string
+ description: The user ID the leave event will be for.
+ required: true
+ x-example: "@someone:example.org"
+ responses:
+ 200:
+ description: |-
+ An unsigned event that the sending server may use as a template
+ for when it calls ``/send_leave``.
+ schema:
+ allOf:
+ - $ref: "definitions/unsigned_pdu.yaml"
+ - type: object
+ properties:
+ # Note: we override a bunch of parameters to change their descriptions
+ sender:
+ type: string
+ description: The user ID of the leaving member.
+ example: "@someone:example.org"
+ origin:
+ type: string
+ description: The name of the resident homeserver.
+ example: "matrix.org"
+ origin_server_ts:
+ type: integer
+ format: int64
+ description: A timestamp added by the resident homeserver.
+ example: 1234567890
+ type:
+ type: string
+ description: The value ``m.room.member``.
+ example: "m.room.member"
+ state_key:
+ type: string
+ description: The user ID of the leaving member.
+ example: "@someone:example.org"
+ content:
+ type: object
+ title: Membership Event Content
+ description: The content of the event.
+ example: {"membership": "leave"}
+ properties:
+ membership:
+ type: string
+ description: The value ``leave``.
+ example: "leave"
+ required: ['membership']
+ auth_events:
+ type: array
+ description: |-
+ An event reference list containing the authorization events that would
+ allow the member to leave the room. This should normally be the
+ ``m.room.create``, ``m.room.power_levels``, and ``m.room.join_rules``
+ events.
+ items:
+ type: array
+ maxItems: 2
+ minItems: 2
+ items:
+ - type: string
+ title: Event ID
+ example: "$abc123:matrix.org"
+ - type: object
+ title: Event Hash
+ example: {
+ "sha256": "abase64encodedsha256hashshouldbe43byteslong"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The event hash.
+ example: abase64encodedsha256hashshouldbe43byteslong
+ required: ['sha256']
+ redacts:
+ type: string
+ description: Not used.
+ required:
+ # Every other field is already flagged as required by the $ref
+ - state_key
+ examples:
+ application/json: {
+ "$ref": "examples/unsigned_pdu.json",
+ "type": "m.room.member",
+ "state_key": "@someone:example.org",
+ "content": {
+ "membership": "leave"
+ },
+ "auth_events": [
+ ["$room_cre4te_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
+ ["$room_j0in_rul3s_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}],
+ ["$room_p0wer_l3vels_3vent:matrix.org", {"sha256": "abase64encodedsha256hashshouldbe43byteslong"}]
+ ]
+ }
+ 403:
+ description: |-
+ The request is not authorized. This could mean that the user is not in the room.
+ schema:
+ $ref: "../client-server/definitions/errors/error.yaml"
+ examples:
+ application/json: {
+ "errcode": "M_FORBIDDEN",
+ "error": "User is not in the room."
+ }
+ "/send_leave/{roomId}/{eventId}":
+ put:
+ summary: Submit a signed leave event to a resident server
+ description: |-
+ Submits a signed leave event to the resident server for it
+ to accept it into the room's graph.
+ operationId: sendLeave
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID that is about to be left.
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: path
+ name: eventId
+ type: string
+ description: The event ID for the leave event.
+ required: true
+ x-example: "$abc123:example.org"
+ - in: body
+ name: body
+ type: object
+ required: true
+ schema:
+ allOf:
+ - $ref: "definitions/pdu.yaml"
+ - type: object
+ properties:
+ # Note: we override a bunch of parameters to change their descriptions
+ sender:
+ type: string
+ description: The user ID of the leaving member.
+ example: "@someone:example.org"
+ origin:
+ type: string
+ description: The name of the leaving homeserver.
+ example: "matrix.org"
+ origin_server_ts:
+ type: integer
+ format: int64
+ description: A timestamp added by the leaving homeserver.
+ example: 1234567890
+ type:
+ type: string
+ description: The value ``m.room.member``.
+ example: "m.room.member"
+ state_key:
+ type: string
+ description: The user ID of the leaving member.
+ example: "@someone:example.org"
+ content:
+ type: object
+ title: Membership Event Content
+ description: The content of the event.
+ example: {"membership": "leave"}
+ properties:
+ membership:
+ type: string
+ description: The value ``leave``.
+ example: "leave"
+ required: ['membership']
+ depth:
+ type: integer
+ description: This field must be present but is ignored; it may be 0.
+ example: 12
+ auth_events:
+ type: array
+ description: |-
+ An event reference list containing the authorization events that would
+ allow the member to leave the room.
+ items:
+ type: array
+ maxItems: 2
+ minItems: 2
+ items:
+ - type: string
+ title: Event ID
+ example: "$abc123:matrix.org"
+ - type: object
+ title: Event Hash
+ example: {
+ "sha256": "abase64encodedsha256hashshouldbe43byteslong"
+ }
+ properties:
+ sha256:
+ type: string
+ description: The event hash.
+ example: abase64encodedsha256hashshouldbe43byteslong
+ required: ['sha256']
+ redacts:
+ type: string
+ description: Not used.
+ required:
+ # Every other field is already flagged as required by the $ref
+ - state_key
+ example: {
+ "$ref": "examples/pdu.json",
+ "type": "m.room.member",
+ "state_key": "@someone:example.org",
+ "content": {
+ "membership": "leave"
+ }
+ }
+ responses:
+ 200:
+ description: |-
+ An empty response to indicate the event was accepted into the graph by
+ the receiving homeserver.
+ schema:
+ type: array
+ minItems: 2
+ maxItems: 2
+ items:
+ - type: integer
+ description: The value ``200``.
+ example: 200
+ - type: object
+ title: Empty Object
+ description: An empty object.
+ examples:
+ application/json: [200, {}]
diff --git a/api/server-server/openid.yaml b/api/server-server/openid.yaml
new file mode 100644
index 00000000..0eac48c8
--- /dev/null
+++ b/api/server-server/openid.yaml
@@ -0,0 +1,63 @@
+# Copyright 2017 Kamax.io
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation OpenID API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+produces:
+ - application/json
+paths:
+ "/openid/userinfo":
+ get:
+ summary: Exchange an OpenID token for user information
+ description: |-
+ Exchanges an OpenID access token for information about the user
+ who generated the token. Currently this only exposes the Matrix
+ User ID of the owner.
+ operationId: exchangeOpenIdToken
+ parameters:
+ - in: path
+ name: access_token
+ type: string
+ description: |-
+ The OpenID access token to get information about the owner for.
+ required: true
+ x-example: SomeT0kenHere
+ responses:
+ 200:
+ description: |-
+ Information about the user who generated the OpenID access token.
+ schema:
+ type: object
+ properties:
+ sub:
+ type: string
+ description: The Matrix User ID who generated the token.
+ example: "@alice:example.com"
+ required: ['sub']
+ 401:
+ description: The token was not recognized or has expired.
+ schema:
+ $ref: "../client-server/definitions/errors/error.yaml"
+ examples:
+ application/json: {
+ "errcode": "M_UNKNOWN_TOKEN",
+ "error": "Access token unknown or expired"
+ }
diff --git a/api/server-server/public_rooms.yaml b/api/server-server/public_rooms.yaml
new file mode 100644
index 00000000..b7691023
--- /dev/null
+++ b/api/server-server/public_rooms.yaml
@@ -0,0 +1,70 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Public Rooms API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/publicRooms":
+ get:
+ summary: Get all the public rooms for a homeserver
+ description: |-
+ Gets all the public rooms for the homeserver. This should not return
+ rooms that are listed on another homeserver's directory, just those
+ listed on the receiving homeserver's directory.
+ operationId: getPublicRooms
+ security:
+ - signedRequest: []
+ parameters:
+ - in: query
+ name: limit
+ type: integer
+ description: |-
+ The maximum number of rooms to return. Defaults to 0 (no limit).
+ x-example: 10
+ - in: query
+ name: since
+ type: string
+ description: |-
+ A pagination token from a previous call to this endpoint to fetch more
+ rooms.
+ x-example: "GetMoreRoomsTokenHere"
+ - in: query
+ name: include_all_networks
+ type: boolean
+ description: |-
+ Whether or not to include all networks/protocols defined by application
+ services on the homeserver. Defaults to false.
+ x-example: false
+ - in: query
+ name: third_party_instance_id
+ type: string
+ description: |-
+ The specific third party network/protocol to request from the homeserver.
+ Can only be used if ``include_all_networks`` is false.
+ x-example: "irc"
+ responses:
+ 200:
+ description: The public room list for the homeserver.
+ schema:
+ $ref: "../client-server/definitions/public_rooms_response.yaml"
diff --git a/api/server-server/query.yaml b/api/server-server/query.yaml
new file mode 100644
index 00000000..dc14724c
--- /dev/null
+++ b/api/server-server/query.yaml
@@ -0,0 +1,175 @@
+# Copyright 2017 Kamax.io
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Query API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/query/{queryType}":
+ get:
+ summary: Query for information
+ description: |-
+ Performs a single query request on the receiving homeserver. The query string
+ arguments are dependent on which type of query is being made. Known query types
+ are specified as their own endpoints as an extension to this definition.
+ operationId: queryInfo
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: queryType
+ type: string
+ description: The type of query to make
+ required: true
+ x-example: profile
+ responses:
+ 200:
+ description: |-
+ The query response. The schema varies depending on the query being made.
+ "/query/directory":
+ get:
+ summary: Query for the room ID and resident homeservers for a room alias
+ description: |-
+ Performs a query to get the mapped room ID and list of resident homeservers in
+ the room for a given room alias. Homeservers should only query room aliases
+ that belong to the target server (identified by the DNS Name in the alias).
+
+ Servers may wish to cache the response to this query to avoid requesting the
+ information too often.
+ operationId: queryRoomDirectory
+ security:
+ - signedRequest: []
+ parameters:
+ - in: query
+ name: room_alias
+ type: string
+ description: The room alias to query.
+ required: true
+ x-example: "#room_alias:example.org"
+ responses:
+ 200:
+ description: |-
+ The corresponding room ID and list of known resident homeservers for the room.
+ schema:
+ type: object
+ properties:
+ room_id:
+ type: string
+ description: The room ID mapped to the queried room alias.
+ x-example: "!roomid1234:example.org"
+ servers:
+ type: array
+ description: |-
+ An array of server names that are likely to hold the given room. This
+ list may or may not include the server answering the query.
+ items:
+ type: string
+ required:
+ - "room_id"
+ - "servers"
+ examples:
+ application/json: {
+ "room_id": "!roomid1234:example.org",
+ "servers": [
+ "example.org",
+ "example.com",
+ "another.example.com:8449",
+ ]
+ }
+ 404:
+ description: The room alias was not found.
+ schema:
+ $ref: "../client-server/definitions/errors/error.yaml"
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND",
+ "error": "Room alias not found."
+ }
+ "/query/profile":
+ get:
+ summary: Query for profile information about a given user
+ description: |-
+ Performs a query to get profile information, such as a display name or avatar,
+ for a given user. Homeservers should only query profiles for users that belong
+ to the target server (identified by the DNS Name in the user ID).
+
+ Servers may wish to cache the response to this query to avoid requesting the
+ information too often.
+ operationId: queryProfile
+ security:
+ - signedRequest: []
+ parameters:
+ - in: query
+ name: user_id
+ type: string
+ description: The user ID to query.
+ required: true
+ x-example: "@someone:example.org"
+ - in: query
+ name: field
+ type: enum
+ enum: ['displayname', 'avatar_url']
+ description: |-
+ The field to query. If specified, the server will only return the given field
+ in the response. If not specified, the server will return the full profile for
+ the user.
+ responses:
+ 200:
+ description: |-
+ The profile for the user. If a ``field`` is specified in the request, only the
+ matching field should be included in the response. If no ``field`` was specified,
+ the response should include the fields of the user's profile that can be made
+ public, such as the display name and avatar.
+
+ If the user does not have a particular field set on their profile, the server
+ should exclude it from the response body or give it the value ``null``.
+ schema:
+ type: object
+ properties:
+ displayname:
+ type: string
+ description: |-
+ The display name of the user. May be omitted if the user does not have a
+ display name set.
+ x-example: "John Doe"
+ avatar_url:
+ type: string
+ description: |-
+ The avatar URL for the user's avatar. May be omitted if the user does not
+ have an avatar set.
+ x-example: "mxc://matrix.org/MyC00lAvatar"
+ examples:
+ application/json: {
+ "displayname": "John Doe",
+ "avatar_url": "mxc://matrix.org/MyC00lAvatar"
+ }
+ 404:
+ description: The user does not exist or does not have a profile.
+ schema:
+ $ref: "../client-server/definitions/errors/error.yaml"
+ examples:
+ application/json: {
+ "errcode": "M_NOT_FOUND",
+ "error": "User does not exist."
+ }
diff --git a/api/server-server/third_party_invite.yaml b/api/server-server/third_party_invite.yaml
new file mode 100644
index 00000000..5c12247c
--- /dev/null
+++ b/api/server-server/third_party_invite.yaml
@@ -0,0 +1,196 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Third Party Invites API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/exchange_third_party_invite/{roomId}":
+ put:
+ summary: Request a server to auth a third party invite event
+ description: |-
+ The receiving server will verify the partial ``m.room.member`` event
+ given in the request body. If valid, the receiving server will issue
+ an invite as per the `Inviting to a room`_ section before returning a
+ response to this request.
+ operationId: exchangeThirdPartyInvite
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: roomId
+ type: string
+ description: The room ID to exchange a third party invite in
+ required: true
+ x-example: "!abc123:matrix.org"
+ - in: body
+ name: body
+ type: object
+ description: A partial ``m.room.member`` event
+ required: true
+ schema:
+ type: object
+ properties:
+ type:
+ type: string
+ description: The event type. Must be ``m.room.member``
+ example: "m.room.member"
+ room_id:
+ type: string
+ description: |-
+ The room ID the event is for. Must match the ID given in
+ the path.
+ example: "!abc123:matrix.org"
+ sender:
+ type: string
+ description: |-
+ The user ID of the user who sent the original ``m.room.third_party_invite``
+ event.
+ example: "@joe:matrix.org"
+ state_key:
+ type: string
+ description: The user ID of the invited user
+ example: "@someone:example.org"
+ content:
+ type: object
+ description: The event content
+ title: Event Content
+ properties:
+ membership:
+ type: string
+ description: The membership state. Must be ``invite``
+ example: invite
+ third_party_invite:
+ type: object
+ description: The third party invite
+ properties:
+ display_name:
+ type: string
+ description: |-
+ A name which can be displayed to represent the user instead of their
+ third party identifier.
+ example: "alice"
+ signed:
+ type: object
+ description: |-
+ A block of content which has been signed, which servers can use to
+ verify the event.
+ properties:
+ signatures:
+ type: object
+ description: The server signatures for this event.
+ additionalProperties:
+ type: object
+ title: Server Signatures
+ additionalProperties:
+ type: string
+ example: {
+ "magic.forest": {
+ "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
+ }
+ }
+ mxid:
+ type: string
+ description: The invited matrix user ID
+ example: "@alice:localhost"
+ token:
+ type: string
+ description: The token used to verify the event
+ example: abc123
+ required: ['signatures', 'mxid', 'token']
+ example: {
+ "mxid": "@alice:localhost",
+ "token": "abc123",
+ "signatures": {
+ "magic.forest": {
+ "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
+ }
+ }
+ }
+ required: ['display_name', 'signed']
+ example: {
+ "display_name": "alice",
+ "signed": {
+ "mxid": "@alice:localhost",
+ "token": "abc123",
+ "signatures": {
+ "magic.forest": {
+ "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
+ }
+ }
+ }
+ }
+ required: ['membership', 'third_party_invite']
+ example: {
+ "membership": "invite",
+ "third_party_invite": {
+ "display_name": "alice",
+ "signed": {
+ "mxid": "@alice:localhost",
+ "token": "abc123",
+ "signatures": {
+ "magic.forest": {
+ "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
+ }
+ }
+ }
+ }
+ }
+ required:
+ - type
+ - room_id
+ - sender
+ - state_key
+ - content
+ example: {
+ "type": "m.room.member",
+ "room_id": "!abc123:matrix.org",
+ "sender": "@joe:matrix.org",
+ "state_key": "@someone:example.org",
+ "content": {
+ "membership": "invite",
+ "third_party_invite": {
+ "display_name": "alice",
+ "signed": {
+ "mxid": "@alice:localhost",
+ "token": "abc123",
+ "signatures": {
+ "magic.forest": {
+ "ed25519:3": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
+ }
+ }
+ }
+ }
+ }
+ }
+ responses:
+ 200:
+ description: The invite has been issued successfully.
+ examples:
+ application/json: {}
+ schema:
+ type: object
+ description: An empty object
+ example: {}
diff --git a/api/server-server/transactions.yaml b/api/server-server/transactions.yaml
new file mode 100644
index 00000000..8d810ad5
--- /dev/null
+++ b/api/server-server/transactions.yaml
@@ -0,0 +1,116 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Transaction API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+consumes:
+ - application/json
+produces:
+ - application/json
+securityDefinitions:
+ $ref: definitions/security.yaml
+paths:
+ "/send/{txnId}":
+ put:
+ summary: Send a transaction
+ description: |-
+ Push messages representing live activity to another server. The destination name
+ will be set to that of the receiving server itself. Each embedded PDU in the
+ transaction body will be processed.
+
+ The sending server must wait and retry for a 200 OK response before sending a
+ transaction with a different ``txnId`` to the receiving server.
+ operationId: sendTransaction
+ security:
+ - signedRequest: []
+ parameters:
+ - in: path
+ name: txnId
+ type: string
+ description: |-
+ The transaction ID.
+ required: true
+ x-example: S0meTransacti0nId
+ - in: body
+ name: body
+ type: object
+ required: true
+ schema:
+ allOf:
+ - $ref: "definitions/transaction.yaml"
+ - type: object
+ properties:
+ edus:
+ type: array
+ description: |-
+ List of ephemeral messages. May be omitted if there are no ephemeral
+ messages to be sent.
+ items:
+ $ref: "definitions/edu.yaml"
+ example: {
+ "$ref": "examples/transaction.json",
+ "edus": [{"$ref": "edu.json"}] # Relative to the examples directory
+ }
+ responses:
+ 200:
+ description: |-
+ The result of processing the transaction. The server is to use this response even in
+ the event of one or more PDUs failing to be processed.
+ schema:
+ type: array
+ minItems: 2
+ maxItems: 2
+ items:
+ - type: integer
+ description: The value ``200``.
+ example: 200
+ - type: object
+ title: PDU Processing Results
+ description: The results for the processing of each PDU in the transaction.
+ properties:
+ pdus:
+ type: object
+ description: |-
+ The PDUs from the original transaction. The string key represents the ID of the
+ PDU (event) that was processed.
+ additionalProperties:
+ type: object
+ title: PDU Processing Result
+ description: Information about how the PDU was handled.
+ properties:
+ error:
+ type: string
+ description: |-
+ A human readable description about what went wrong in processing this PDU.
+ If no error is present, the PDU can be considered successfully handled.
+ example: "You are not allowed to send a message to this room."
+ required: ['pdus']
+ examples:
+ application/json: [
+ 200,
+ {
+ "pdus": {
+ "$successful_event:domain.com": {},
+ "$failed_event:example.org": {
+ "error": "You are not allowed to send a message to this room."
+ }
+ }
+ }
+ ]
diff --git a/api/server-server/version.yaml b/api/server-server/version.yaml
new file mode 100644
index 00000000..19975529
--- /dev/null
+++ b/api/server-server/version.yaml
@@ -0,0 +1,53 @@
+# 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.
+
+swagger: '2.0'
+info:
+ title: "Matrix Federation Version API"
+ version: "1.0.0"
+host: localhost:8448
+schemes:
+ - https
+basePath: /_matrix/federation/v1
+produces:
+ - application/json
+paths:
+ "/version":
+ get:
+ summary: Get the implementation name and version of this homeserver.
+ description: Get the implementation name and version of this homeserver.
+ responses:
+ 200:
+ description:
+ The implementation name and version of this homeserver.
+ examples:
+ application/json: {
+ "server": {
+ "name": "My_Homeserver_Implementation",
+ "version": "ArbitraryVersionNumber"
+ }
+ }
+ schema:
+ type: object
+ properties:
+ server:
+ title: Server
+ type: object
+ properties:
+ name:
+ type: string
+ description: Arbitrary name that identify this implementation.
+ version:
+ type: string
+ description: Version of this implementation. The version format depends on the implementation.
diff --git a/api/validator.js b/api/validator.js
index 0d76c09d..c20dd993 100644
--- a/api/validator.js
+++ b/api/validator.js
@@ -34,38 +34,53 @@ var errFn = function(err, api) {
process.exit(1);
};
-var isDir = fs.lstatSync(opts.schema).isDirectory()
+/**
+ * @brief Produce a handler for parser.validate().
+ * Recommended usage: `parser.validate(filename, makeHandler(filename));`
+ * or `parser.validate(schema, makeHandler());`.
+ * @param scope - usually a filename, this will be used to denote
+ * an (in)valid schema in console output; "Schema" if undefined
+ * @returns {function} the handler that can be passed to parser.validate
+ */
+function makeHandler(scope) {
+ if (!scope)
+ scope = "Schema";
+ return function(err, api, metadata) {
+ if (err) {
+ console.error("%s is not valid.", scope || "Schema");
+ errFn(err, api, metadata); // Won't return
+ }
+
+ Object.keys(api.paths).forEach(function (endpoint) {
+ var operationsMap = api.paths[endpoint];
+ Object.keys(operationsMap).forEach(function (verb) {
+ if (!operationsMap[verb]["operationId"]) {
+ console.error("%s is not valid", scope);
+ errFn("operationId is missing in " + endpoint + ", verb " + verb, api);
+ }
+ })
+ });
+
+ console.log("%s is valid.", scope);
+ }
+}
+
+var isDir = fs.lstatSync(opts.schema).isDirectory();
if (isDir) {
console.log("Checking directory %s for .yaml files...", opts.schema);
fs.readdir(opts.schema, function(err, files) {
if (err) {
- console.error(err);
- process.exit(1);
+ errFn(err); // Won't return
}
files.forEach(function(f) {
var suffix = ".yaml";
if (f.indexOf(suffix, f.length - suffix.length) > 0) {
- parser.validate(path.join(opts.schema, f), function(err, api, metadata) {
- if (!err) {
- console.log("%s is valid.", f);
- }
- else {
- console.error("%s is not valid.", f);
- errFn(err, api, metadata);
- }
- });
- }
+ parser.validate(path.join(opts.schema, f), makeHandler(f));
+ }
});
});
}
else{
- parser.validate(opts.schema, function(err, api) {
- if (!err) {
- console.log("%s is valid", opts.schema);
- }
- else {
- errFn(err, api);
- }
- });
-};
+ parser.validate(opts.schema, makeHandler(opts.schema));
+}
diff --git a/changelogs/README.md b/changelogs/README.md
new file mode 100644
index 00000000..a5fb1fb7
--- /dev/null
+++ b/changelogs/README.md
@@ -0,0 +1,55 @@
+
+
+# Changelogs
+
+[Towncrier](https://github.com/hawkowl/towncrier) is used to manage the changelog and
+keep it up to date. Because of this, updating a changelog is really easy.
+
+## How to update a changelog when releasing an API
+
+1. Ensure you're in your Python 3 virtual environment
+2. `cd` your way to the API you're releasing (eg: `cd changelogs/client_server`)
+3. Run `towncrier --version "r0.4.0" --name "client-server" --yes` substituting the
+ variables as approprite. Note that `--name` is required although the value is ignored.
+4. Commit the changes and finish the release process.
+
+## How to prepare a changelog for a new API
+
+For this example, we're going to pretend that the `server_server` API doesn't exist.
+
+1. Create the file `changelogs/server_server.rst`
+2. Create the folder `changelogs/server_server`
+3. In the new folder, create a `pyproject.toml` file with these contents:
+ ```toml
+ [tool.towncrier]
+ filename = "../server_server.rst"
+ directory = "newsfragments"
+ issue_format = "`#{issue} `_"
+ title_format = "{version}"
+
+ [[tool.towncrier.type]]
+ directory = "breaking"
+ name = "Breaking Changes"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "deprecation"
+ name = "Deprecations"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "new"
+ name = "New Endpoints"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "feature"
+ name = "Backwards Compatible Changes"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "clarification"
+ name = "Spec Clarifications"
+ showcontent = true
+ ```
+4. Create a `.gitignore` in `changelogs/server_server/newsfragments` with the contents `!.gitignore`
diff --git a/changelogs/client_server.rst b/changelogs/client_server.rst
index 952a72e2..d2ef7f2d 100644
--- a/changelogs/client_server.rst
+++ b/changelogs/client_server.rst
@@ -1,10 +1,10 @@
-
-====================
+r0.3.0
+======
- Breaking changes:
- - Change the rule kind of `.m.rule.contains_display_name` from
- `underride` to `override`. This works with all known clients
+ - Change the rule kind of ``.m.rule.contains_display_name`` from
+ ``underride`` to ``override``. This works with all known clients
which support push rules, but any other clients implementing
the push rules API should be aware of this change. This
makes it simple to mute rooms correctly in the API
@@ -13,6 +13,11 @@
(`#395 `_).
- Remove requirement that tokens used in token-based login be macaroons
(`#395 `_).
+ - Move ``thumbnail_url`` and ``thumbnail_info`` members of json objects
+ for ``m.room.message`` events with msgtypes ``m.image``, ``m.file``
+ and ``m.location``, inside the ``info`` member, to match ``m.video``
+ events
+ (`#723 `_).
- Changes to the API which will be backwards-compatible for clients:
@@ -46,9 +51,41 @@
- Allow guest accounts to use a number of endpoints which are required for
end-to-end encryption.
(`#751 `_).
+ - Add key distribution APIs, for use with end-to-end encryption.
+ (`#894 `_).
+ - Add ``m.room.pinned_events`` state event for rooms.
+ (`#1007 `_).
+ - Add mention of ability to send Access Token via an Authorization Header.
+ - Add ``guest_can_join`` parameter to ``POST /createRoom``
+ (`#1093 `_).
+
+ - New endpoints:
+
+ - ``GET /joined_rooms``
+ (`#999 `_).
+
+ - ``GET /rooms/{roomId}/joined_members``
+ (`#999 `_).
+
+ - ``GET /account/whoami``
+ (`#1063 `_).
+
+ - ``GET /media/{version}/preview_url``
+ (`#1064 `_).
- Spec clarifications:
+ - Add endpoints and logic for invites and third-party invites to the federation
+ spec and update the JSON of the request sent by the identity server upon 3PID
+ binding
+ (`#997 `_)
+ - Fix "membership" property on third-party invite upgrade example
+ (`#995 `_)
+ - Fix response format and 404 example for room alias lookup
+ (`#960 `_)
+ - Fix examples of ``m.room.member`` event and room state change,
+ and added a clarification on the membership event sent upon profile update
+ (`#950 `_).
- Spell out the way that state is handled by ``POST /createRoom``
(`#362 `_).
- Clarify the fields which are applicable to different types of push rule
@@ -64,6 +101,16 @@
- Make ``m.notice`` description a bit harder in its phrasing to try to
dissuade the same issues that occurred with IRC
(`#750 `_).
+ - ``GET /user/{userId}/filter/{filterId}`` requires authentication
+ (`#1003 `_).
+ - Add some clarifying notes on the behaviour of rooms with no
+ ``m.room.power_levels`` event
+ (`#1026 `_).
+ - Clarify the relationship between ``username`` and ``user_id`` in the
+ ``/register`` API
+ (`#1032 `_).
+ - Clarify rate limiting and security for content repository.
+ (`#1064 `_).
r0.2.0
======
diff --git a/changelogs/client_server/newsfragments/.gitignore b/changelogs/client_server/newsfragments/.gitignore
new file mode 100644
index 00000000..b722e9e1
--- /dev/null
+++ b/changelogs/client_server/newsfragments/.gitignore
@@ -0,0 +1 @@
+!.gitignore
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1096.new b/changelogs/client_server/newsfragments/1096.new
new file mode 100644
index 00000000..50d86879
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1096.new
@@ -0,0 +1 @@
+``POST /user_directory/search``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1097.clarification b/changelogs/client_server/newsfragments/1097.clarification
new file mode 100644
index 00000000..2a7cb93a
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1097.clarification
@@ -0,0 +1 @@
+Mark ``home_server`` return field for ``/login`` and ``/register`` endpoints as deprecated
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1104.feature b/changelogs/client_server/newsfragments/1104.feature
new file mode 100644
index 00000000..9b85343f
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1104.feature
@@ -0,0 +1 @@
+Add ``token`` parameter to the ``/keys/query`` endpoint
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1106.clarification b/changelogs/client_server/newsfragments/1106.clarification
new file mode 100644
index 00000000..f7a1fe3e
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1106.clarification
@@ -0,0 +1 @@
+Fix response format of ``/keys/changes`` endpoint
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1109.clarification b/changelogs/client_server/newsfragments/1109.clarification
new file mode 100644
index 00000000..176d9403
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1109.clarification
@@ -0,0 +1 @@
+Clarify default values for some fields on the ``/search`` API
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1110.new b/changelogs/client_server/newsfragments/1110.new
new file mode 100644
index 00000000..e1b80b8b
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1110.new
@@ -0,0 +1 @@
+``GET /rooms/{roomId}/event/{eventId}``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1137.clarification b/changelogs/client_server/newsfragments/1137.clarification
new file mode 100644
index 00000000..5ad8ec34
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1137.clarification
@@ -0,0 +1 @@
+Fix the representation of ``m.presence`` events
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1139.clarification b/changelogs/client_server/newsfragments/1139.clarification
new file mode 100644
index 00000000..b5193ad3
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1139.clarification
@@ -0,0 +1 @@
+Clarify that ``m.tag`` ordering is done with numbers, not strings
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1141.feature b/changelogs/client_server/newsfragments/1141.feature
new file mode 100644
index 00000000..da041819
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1141.feature
@@ -0,0 +1 @@
+Add the room visibility options for the room directory
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1142.feature b/changelogs/client_server/newsfragments/1142.feature
new file mode 100644
index 00000000..0a0842c4
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1142.feature
@@ -0,0 +1 @@
+Add spec for ignoring users
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1151.feature b/changelogs/client_server/newsfragments/1151.feature
new file mode 100644
index 00000000..8875812b
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1151.feature
@@ -0,0 +1 @@
+Add the ``/register/available`` endpoint for username availability
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1152.clarification b/changelogs/client_server/newsfragments/1152.clarification
new file mode 100644
index 00000000..bbecc9b2
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1152.clarification
@@ -0,0 +1 @@
+Clarify that ``/account/whoami`` should consider application services
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1158.clarification b/changelogs/client_server/newsfragments/1158.clarification
new file mode 100644
index 00000000..dc1f6d14
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1158.clarification
@@ -0,0 +1,3 @@
+Update ``ImageInfo`` and ``ThumbnailInfo`` dimension schema descriptions
+to clarify that they relate to intended display size, as opposed to the
+intrinsic size of the image file.
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1158.feature b/changelogs/client_server/newsfragments/1158.feature
new file mode 100644
index 00000000..a55df4fb
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1158.feature
@@ -0,0 +1 @@
+Add sticker messages
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1239.new b/changelogs/client_server/newsfragments/1239.new
new file mode 100644
index 00000000..9bcf357d
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1239.new
@@ -0,0 +1 @@
+``POST /delete_devices``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1245.clarification b/changelogs/client_server/newsfragments/1245.clarification
new file mode 100644
index 00000000..e0a63834
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1245.clarification
@@ -0,0 +1 @@
+Mark ``GET /rooms/{roomId}/members`` as requiring authentication
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1263.feature b/changelogs/client_server/newsfragments/1263.feature
new file mode 100644
index 00000000..04964a7d
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1263.feature
@@ -0,0 +1 @@
+Document ``/logout/all`` endpoint
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1264.feature b/changelogs/client_server/newsfragments/1264.feature
new file mode 100644
index 00000000..9cb06f71
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1264.feature
@@ -0,0 +1 @@
+Add report content API
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1265.feature b/changelogs/client_server/newsfragments/1265.feature
new file mode 100644
index 00000000..1e270fa8
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1265.feature
@@ -0,0 +1 @@
+Add ``allow_remote`` to the content repo to avoid routing loops
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1274.feature b/changelogs/client_server/newsfragments/1274.feature
new file mode 100644
index 00000000..d4958131
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1274.feature
@@ -0,0 +1 @@
+Document `highlights` field in /search response
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1284.clarification b/changelogs/client_server/newsfragments/1284.clarification
new file mode 100644
index 00000000..7bc92f47
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1284.clarification
@@ -0,0 +1 @@
+Clarify ``changed`` field behaviour in device tracking process
diff --git a/changelogs/client_server/newsfragments/1284.feature b/changelogs/client_server/newsfragments/1284.feature
new file mode 100644
index 00000000..c658142e
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1284.feature
@@ -0,0 +1,7 @@
+End-to-end encryption for group chats:
+
+ - Olm and Megolm messaging algorithms.
+ - ``m.room.encrypted``, ``m.room.encryption``, ``m.room_key`` events.
+ - Device verification process.
+ - ``device_one_time_keys_count`` sync parameter.
+ - ``device_lists:left`` sync parameter.
diff --git a/changelogs/client_server/newsfragments/1329.clarification b/changelogs/client_server/newsfragments/1329.clarification
new file mode 100644
index 00000000..970d3d94
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1329.clarification
@@ -0,0 +1 @@
+Describe ``StateEvent`` for ``/createRoom``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1353.new b/changelogs/client_server/newsfragments/1353.new
new file mode 100644
index 00000000..0af0c520
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1353.new
@@ -0,0 +1 @@
+``GET /thirdparty/*`` Endpoints
diff --git a/changelogs/client_server/newsfragments/1361.feature b/changelogs/client_server/newsfragments/1361.feature
new file mode 100644
index 00000000..b1d4c2f1
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1361.feature
@@ -0,0 +1 @@
+Document the GET version of ``/login``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1362.clarification b/changelogs/client_server/newsfragments/1362.clarification
new file mode 100644
index 00000000..1deb4500
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1362.clarification
@@ -0,0 +1 @@
+Describe how the ``reason`` is handled for kicks/bans
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1364.feature b/changelogs/client_server/newsfragments/1364.feature
new file mode 100644
index 00000000..733d6a1f
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1364.feature
@@ -0,0 +1 @@
+Document the ``server_name`` parameter on ``/join/{roomIdOrAlias}``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1365.feature b/changelogs/client_server/newsfragments/1365.feature
new file mode 100644
index 00000000..d2864e96
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1365.feature
@@ -0,0 +1 @@
+Document the CORS/preflight headers
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1371.clarification b/changelogs/client_server/newsfragments/1371.clarification
new file mode 100644
index 00000000..88552fcd
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1371.clarification
@@ -0,0 +1 @@
+Mark ``GET /presence/{userId}/status`` as requiring authentication
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1373.clarification b/changelogs/client_server/newsfragments/1373.clarification
new file mode 100644
index 00000000..21e18966
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1373.clarification
@@ -0,0 +1 @@
+Describe the rate limit error response schema
diff --git a/changelogs/client_server/newsfragments/1378.clarification b/changelogs/client_server/newsfragments/1378.clarification
new file mode 100644
index 00000000..f952428b
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1378.clarification
@@ -0,0 +1 @@
+Clarify that clients must leave rooms before forgetting them
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1379.clarification b/changelogs/client_server/newsfragments/1379.clarification
new file mode 100644
index 00000000..122b3900
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1379.clarification
@@ -0,0 +1 @@
+Document guest access in ``/createRoom`` presets
diff --git a/changelogs/client_server/newsfragments/1380.clarification b/changelogs/client_server/newsfragments/1380.clarification
new file mode 100644
index 00000000..490a9a43
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1380.clarification
@@ -0,0 +1 @@
+Define what a ``RoomEvent`` is on ``/rooms/{roomId}/messages``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1381.clarification b/changelogs/client_server/newsfragments/1381.clarification
new file mode 100644
index 00000000..e5e599ac
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1381.clarification
@@ -0,0 +1 @@
+Clarify the request and result types on ``/search``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1390.feature b/changelogs/client_server/newsfragments/1390.feature
new file mode 100644
index 00000000..48a53b0a
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1390.feature
@@ -0,0 +1 @@
+Add new user identifier object for logging in
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1397.feature b/changelogs/client_server/newsfragments/1397.feature
new file mode 100644
index 00000000..e4bd248a
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1397.feature
@@ -0,0 +1 @@
+Document message formats on ``m.text`` and ``m.emote`` messages
diff --git a/changelogs/client_server/newsfragments/1400.clarification b/changelogs/client_server/newsfragments/1400.clarification
new file mode 100644
index 00000000..3fd29e92
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1400.clarification
@@ -0,0 +1 @@
+Clarify some of the properties on the search result
diff --git a/changelogs/client_server/newsfragments/1506.feature b/changelogs/client_server/newsfragments/1506.feature
new file mode 100644
index 00000000..62ad1260
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1506.feature
@@ -0,0 +1 @@
+Document and improve client interaction with pushers.
diff --git a/changelogs/client_server/newsfragments/1507.new b/changelogs/client_server/newsfragments/1507.new
new file mode 100644
index 00000000..6cc46870
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1507.new
@@ -0,0 +1 @@
+``POST /account/3pid/msisdn/requestToken``, ``POST /register/msisdn/requestToken``, and ``POST /account/password/msisdn/requestToken``
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1516.feature b/changelogs/client_server/newsfragments/1516.feature
new file mode 100644
index 00000000..8e9b26df
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1516.feature
@@ -0,0 +1 @@
+Add support for Room Versions.
diff --git a/changelogs/client_server/newsfragments/1517.clarification b/changelogs/client_server/newsfragments/1517.clarification
new file mode 100644
index 00000000..b16928c1
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1517.clarification
@@ -0,0 +1 @@
+Clarify how access tokens are meant to be supplied to the homeserver.
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1518.clarification b/changelogs/client_server/newsfragments/1518.clarification
new file mode 100644
index 00000000..3e2adea3
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1518.clarification
@@ -0,0 +1 @@
+Document additional parameters on the ``/createRoom`` API.
\ No newline at end of file
diff --git a/changelogs/client_server/newsfragments/1542.feature b/changelogs/client_server/newsfragments/1542.feature
new file mode 100644
index 00000000..7e94bdc5
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1542.feature
@@ -0,0 +1 @@
+Guests can now call /context and /event to fetch events
diff --git a/changelogs/client_server/newsfragments/1550.feature b/changelogs/client_server/newsfragments/1550.feature
new file mode 100644
index 00000000..f04fa9ae
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1550.feature
@@ -0,0 +1 @@
+Add server ACLs as an option for controlling federation in a room.
diff --git a/changelogs/client_server/newsfragments/1551.clarification b/changelogs/client_server/newsfragments/1551.clarification
new file mode 100644
index 00000000..06eac4da
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1551.clarification
@@ -0,0 +1 @@
+Clarify that new push rules should be enabled by default, and that unrecognised conditions should not match.
diff --git a/changelogs/client_server/newsfragments/1551.feature b/changelogs/client_server/newsfragments/1551.feature
new file mode 100644
index 00000000..dfce0f0a
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1551.feature
@@ -0,0 +1 @@
+Add new push rules for encrypted events and ``@room`` notifications.
diff --git a/changelogs/client_server/newsfragments/1554.feature b/changelogs/client_server/newsfragments/1554.feature
new file mode 100644
index 00000000..ec7ffe71
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1554.feature
@@ -0,0 +1 @@
+Add third party network room directories, as provided by application services.
diff --git a/changelogs/client_server/newsfragments/1562.clarification b/changelogs/client_server/newsfragments/1562.clarification
new file mode 100644
index 00000000..c46e189d
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1562.clarification
@@ -0,0 +1 @@
+Clarify the supported HTML features for room messages.
diff --git a/changelogs/client_server/newsfragments/1568.clarification b/changelogs/client_server/newsfragments/1568.clarification
new file mode 100644
index 00000000..4b7a6eaf
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1568.clarification
@@ -0,0 +1 @@
+Move the ``invite_room_state`` definition under ``unsigned`` where it actually resides.
diff --git a/changelogs/client_server/newsfragments/1570.clarification b/changelogs/client_server/newsfragments/1570.clarification
new file mode 100644
index 00000000..dbf8a821
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1570.clarification
@@ -0,0 +1 @@
+Clarify the object structures and defaults for Filters.
diff --git a/changelogs/client_server/newsfragments/1571.clarification b/changelogs/client_server/newsfragments/1571.clarification
new file mode 100644
index 00000000..2410baf3
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1571.clarification
@@ -0,0 +1 @@
+Clarify instances of ``type: number`` in the swagger/OpenAPI schema definitions.
diff --git a/changelogs/client_server/newsfragments/1572.clarification b/changelogs/client_server/newsfragments/1572.clarification
new file mode 100644
index 00000000..7e84098f
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1572.clarification
@@ -0,0 +1 @@
+Clarify that left rooms also have account data in ``/sync``.
diff --git a/changelogs/client_server/newsfragments/1574.clarification b/changelogs/client_server/newsfragments/1574.clarification
new file mode 100644
index 00000000..8d07ef56
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1574.clarification
@@ -0,0 +1 @@
+Fix naming of the body field in ``PUT /directory/room``.
diff --git a/changelogs/client_server/newsfragments/1577.clarification b/changelogs/client_server/newsfragments/1577.clarification
new file mode 100644
index 00000000..aec3248f
--- /dev/null
+++ b/changelogs/client_server/newsfragments/1577.clarification
@@ -0,0 +1 @@
+Clarify the filter object schema used in room searching.
diff --git a/changelogs/client_server/pyproject.toml b/changelogs/client_server/pyproject.toml
new file mode 100644
index 00000000..8fa3f6b5
--- /dev/null
+++ b/changelogs/client_server/pyproject.toml
@@ -0,0 +1,30 @@
+[tool.towncrier]
+ filename = "../client_server.rst"
+ directory = "newsfragments"
+ issue_format = "`#{issue} `_"
+ title_format = "{version}"
+
+ [[tool.towncrier.type]]
+ directory = "breaking"
+ name = "Breaking Changes"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "deprecation"
+ name = "Deprecations"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "new"
+ name = "New Endpoints"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "feature"
+ name = "Backwards Compatible Changes"
+ showcontent = true
+
+ [[tool.towncrier.type]]
+ directory = "clarification"
+ name = "Spec Clarifications"
+ showcontent = true
diff --git a/drafts/erikj_federation.rst b/drafts/erikj_federation.rst
index ce089def..86d5acd4 100644
--- a/drafts/erikj_federation.rst
+++ b/drafts/erikj_federation.rst
@@ -3,115 +3,6 @@ Federation
.. sectnum::
.. contents:: Table of Contents
-Authorization
--------------
-
-When receiving new events from remote servers, or creating new events, a server
-must know whether that event is allowed by the authorization rules. These rules
-depend solely on the state at that event. The types of state events that affect
-authorization are:
-
-- ``m.room.create``
-- ``m.room.member``
-- ``m.room.join_rules``
-- ``m.room.power_levels``
-
-Servers should not create new events that reference unauthorized events.
-However, any event that does reference an unauthorized event is not itself
-automatically considered unauthorized.
-
-Unauthorized events that appear in the event graph do *not* have any effect on
-the state of the graph.
-
-.. Note:: This is in contrast to redacted events which can still affect the
- state of the graph. For example, a redacted *"join"* event will still
- result in the user being considered joined.
-
-
-Rules
-~~~~~
-
-The following are the rules to determine if an event is authorized (this does
-include validation).
-
-**TODO**: What signatures do we expect?
-
-1. If type is ``m.room.create`` allow if and only if it has no prev events.
-#. If type is ``m.room.member``:
-
- a. If ``membership`` is ``join``:
-
- i. If the previous event is an ``m.room.create``, the depth is 1 and
- the ``state_key`` is the creator, then allow.
- #. If the ``state_key`` does not match ``sender`` key, reject.
- #. If the current state has ``membership`` set to ``join``.
- #. If the ``sender`` is in the ``m.room.may_join`` list. [Not currently
- implemented]
- #. If the ``join_rules`` is:
-
- - ``public``: allow.
- - ``invite``: allow if the current state has ``membership`` set to
- ``invite``
- - ``knock``: **TODO**.
- - ``private``: Reject.
-
- #. Reject
-
- #. If ``membership`` is ``invite`` then allow if ``sender`` is in room,
- otherwise reject.
- #. If ``membership`` is ``leave``:
-
- i. If ``sender`` matches ``state_key`` allow.
- #. If ``sender``'s power level is greater than the the ``kick_level``
- given in the current ``m.room.power_levels`` state (defaults to 50),
- and the ``state_key``'s power level is less than or equal to the
- ``sender``'s power level, then allow.
- #. Reject.
-
- #. If ``membership`` is ``ban``:
-
- i. **TODO**.
-
- #. Reject.
-
-#. Reject the event if the event type's required power level is less that the
- ``sender``'s power level.
-#. If the ``sender`` is not in the room, reject.
-#. If the type is ``m.room.power_levels``:
-
- a. **TODO**.
-
-#. Allow.
-
-
-Definitions
-~~~~~~~~~~~
-
-Required Power Level
- A given event type has an associated *required power level*. This is given
- by the current ``m.room.power_levels`` event, it is either listed explicitly
- in the ``events`` section or given by either ``state_default`` or
- ``events_default`` depending on if the event type is a state event or not.
-
-
-Auth events
-~~~~~~~~~~~
-
-The auth events of an event are the set of events used by the authorization
-algorithm to accept the event. These should be a subset of the current state.
-
-A server is required to store the complete chain of auth events for all events
-it serves to remote servers.
-
-All auth events have type:
-
- - ``m.room.create``
- - ``m.room.power_levels``
- - ``m.room.member``
-
-.. todo
- We probably should probably give a lower band of how long auth events
- should be kept around for.
Auth chain
~~~~~~~~~~
@@ -156,51 +47,6 @@ that the other is correct; i.e. we always accept that the other side is correct
unless we can prove otherwise.
-
-State Resolution
-----------------
-
- **TODO**
-
-When two branches in the event graph merge, the state of those branches might
-differ, so a *state resolution* algorithm must be used to determine the current
-state of the resultant merge.
-
-The properties of the state resolution algorithm are:
-
-- Must only depend on the event graph, and not local server state.
-- When two state events are comparable, the descendant one should be picked.
-- Must not require the full event graph.
-
-The following algorithm satisfies these requirements; given two or more events,
-pick the one with the greatest:
-
-#. Depth.
-#. Hash of event_id.
-
-
-This works except in the case of auth events, where we need to mitigate against
-the attack where servers artificially netsplit to avoid bans or power level
-changes.
-
-We want the following rules to apply:
-
-#. If power levels have been changed on two different branches use the rules
- above, ensuring that the one picked is a valid change from the one not picked.
-#. Similarly handle membership changes (e.g. bans, kicks, etc.)
-#. Any state merged must be allowed by the newly merged auth events. If none of
- the candidate events for a given state are allowed, we pick the last event
- given by the ordering above (i.e. we pick one with the least depth).
-
-
-
-State Conflict Resolution
--------------------------
-
-If a server discovers that it disagrees with another about the current state,
-it can follow the same process outlined in *Auth chain resolution* to resolve
-these conflicts.
-
Constructing a new event
------------------------
@@ -405,4 +251,3 @@ Example event:
"age": 500
}
}
-
diff --git a/drafts/human-id-rules.rst b/drafts/human-id-rules.rst
index d0c2ee2c..3614d999 100644
--- a/drafts/human-id-rules.rst
+++ b/drafts/human-id-rules.rst
@@ -1,81 +1,133 @@
-This document outlines the format for human-readable IDs within matrix.
+Abstract
+========
-Overview
---------
-UTF-8 is quickly becoming the standard character encoding set on the web. As
-such, Matrix requires that all strings MUST be encoded as UTF-8. However,
+This document outlines a proposed format for human-readable IDs within Matrix.
+For status see https://github.com/matrix-org/matrix-doc/pull/3/files
+
+Background
+----------
+UTF-8 is the dominant character encoding for Unicode on the web. However,
using Unicode as the character set for human-readable IDs is troublesome. There
are many different characters which appear identical to each other, but would
-identify different users. In addition, there are non-printable characters which
-cannot be rendered by the end-user. This opens up a security vulnerability with
+produce different IDs. In addition, there are non-printable characters which
+cannot be rendered by the end-user. This creates an opportunity for
phishing/spoofing of IDs, commonly known as a homograph attack.
-Web browers encountered this problem when International Domain Names were
+Web browsers encountered this problem when International Domain Names were
introduced. A variety of checks were put in place in order to protect users. If
an address failed the check, the raw punycode would be displayed to
-disambiguate the address. Similar checks are performed by homeservers in
-Matrix. However, Matrix does not use punycode representations, and so does not
-show raw punycode on a failed check. Instead, homeservers must outright reject
-these misleading IDs.
-
-Types of human-readable IDs
----------------------------
-There are two main human-readable IDs in question:
-
-- Room aliases
-- User IDs
+disambiguate the address.
+The human-readable IDs in Matrix are Room Aliases and User IDs.
Room aliases look like ``#localpart:domain``. These aliases point to opaque
-non human-readable room IDs. These pointers can change, so there is already an
-issue present with the same ID pointing to a different destination at a later
-date.
+non human-readable room IDs. These pointers can change to point at a different
+room ID at any time. User IDs look like ``@localpart:domain``. These represent
+actual end-users (there is no indirection).
-User IDs look like ``@localpart:domain``. These represent actual end-users, and
-unlike room aliases, there is no layer of indirection. This presents a much
-greater concern with homograph attacks.
+Proposal
+========
-Checks
-------
-- Similar to web browsers.
-- blacklisted chars (e.g. non-printable characters)
-- mix of language sets from 'preferred' language not allowed.
-- Language sets from CLDR dataset.
-- Treated in segments (localpart, domain)
-- Additional restrictions for ease of processing IDs.
+User IDs and Room Aliases MUST be Unicode as UTF-8. Checks are performed on
+these IDs by homeservers to protect users from phishing/spoofing attacks.
+These checks are:
- - Room alias localparts MUST NOT have ``#`` or ``:``.
- - User ID localparts MUST NOT have ``@`` or ``:``.
+User ID Localparts:
+ - MUST NOT contain a ``:`` or start with a ``@`` or ``.``
+ - MUST NOT contain one of the 107 blacklisted characters on this list:
+ http://kb.mozillazine.org/Network.IDN.blacklist_chars
+ - After stripping " 0-9, +, -, [, ], _, and the space character it MUST NOT
+ contain characters from >1 language, defined by the `exemplar characters`_
+ on http://cldr.unicode.org/
-Rejecting
----------
-- Homeservers MUST reject room aliases which do not pass the check, both on
- GETs and PUTs.
-- Homeservers MUST reject user ID localparts which do not pass the check, both
- on creation and on events.
-- Any homeserver whose domain does not pass this check, MUST use their punycode
- domain name instead of the IDN, to prevent other homeservers rejecting you.
-- Error code is ``M_FAILED_HUMAN_ID_CHECK``. (generic enough for both failing
- due to homograph attacks, and failing due to including ``:`` s, etc)
-- Error message MAY go into further information about which characters were
- rejected and why.
-- Error message SHOULD contain a ``failed_keys`` key which contains an array
- of strings which represent the keys which failed the check e.g::
+.. _exemplar characters: http://cldr.unicode.org/translation/characters#TOC-Exemplar-Characters
- failed_keys: [ user_id, room_alias ]
+Room Alias Localparts:
+ - MUST NOT contain a ``:``
+ - MUST NOT contain one of the 107 blacklisted characters on this list:
+ http://kb.mozillazine.org/Network.IDN.blacklist_chars
+ - After stripping " 0-9, +, -, [, ], _, and the space character it MUST NOT
+ contain characters from >1 language, defined by the `exemplar characters`_
+ on http://cldr.unicode.org/
-Other considerations
---------------------
-- Basic security: Informational key on the event attached by HS to say "unsafe
+.. _exemplar characters: http://cldr.unicode.org/translation/characters#TOC-Exemplar-Characters
+
+In the event of a failed user ID check, well behaved homeservers MUST:
+ - Rewrite user IDs in the offending events to be punycode with an additional ``@``
+ prefix **before** delivering them to clients. There are no guarantees for
+ consistency between homeserver ID checking implementations. As a result, user
+ IDs MUST be sent in their *original* form over federation. This can be done in
+ a stateless manner as the punycode form has no information loss.
+
+In the event of a failed room alias check, well behaved homeservers MUST:
+ - Send an HTTP status code 400 with an ``errcode`` of ``M_FAILED_HUMAN_ID_CHECK``
+ to the client if the client is attempting to *create* this alias.
+ - Send an HTTP status code 400 with an ``errcode`` of ``M_FAILED_HUMAN_ID_CHECK``
+ to the client if the client is attempting to *join* a room via this alias.
+
+Examples::
+
+ @ebаy:domain.com (Cyrillic 'a', everything else English)
+ @@xn--eby-7cd:domain.com (Punycode with additional '@')
+
+Homeservers SHOULD NOT allow two user IDs that differ only by case. This
+SHOULD be applied based on the capitalisation rules in the CLDR dataset:
+http://cldr.unicode.org/
+
+This check SHOULD be applied when the user ID is created, in order to prevent
+registration with the same name and different capitalisations, e.g.
+``@foo:bar`` vs ``@Foo:bar`` vs ``@FOO:bar``. Homeservers MAY canonicalise
+the user ID to be completely lower-case if desired.
+
+Rationale
+=========
+
+Each ID is split into segments (localpart/domain) around the ``:``. For
+this reason, ``:`` is a reserved character and cannot be a localpart character.
+The 107 blacklisted characters are used to prevent non-printable characters and
+spaces from being used. The decision to ban characters from more than 1 language
+matches the behaviour of `Google Chrome for IDN handling`_. This is to protect
+against common homograph attacks such as ebаy.com (Cyrillic "a", rest is
+English). This would always result in a failed check. Even with this though
+there are limitations. For example, сахар is entirely Cyrillic, whereas caxap is
+entirely Latin.
+
+.. _Google Chrome for IDN handling: https://www.chromium.org/developers/design-documents/idn-in-google-chrome
+
+User ID localparts cannot start with ``@`` so that a namespace of localparts
+beginning with ``@`` can be created. This namespace is used for user IDs which
+fail the ID checks. A failed ID could look like ``@@xn--c1yn36f:domain.com``.
+
+If a user ID fails the check, the user ID on the event is renamed. This doesn't
+require extra work for clients, and users will see an odd user ID rather than a
+spoofed name. Renaming is done in order to protect users of a given HS, so if a
+malicious HS doesn't rename their IDs, it doesn't affect any other HS.
+
+Room aliases cannot be rewritten as punycode and sent to the HS the alias is
+referring to as the HS will not necessarily understand the rewritten alias.
+
+Other rejected solutions for failed checks
+------------------------------------------
+- Additional key: Informational key on the event attached by HS to say "unsafe
ID". Problem: clients can just ignore it, and since it will appear only very
rarely, easy to forget when implementing clients.
-- Moderate security: Requires client handshake. Forces clients to implement
+- Require client handshake: Forces clients to implement
a check, else they cannot communicate with the misleading ID. However, this
is extra overhead in both client implementations and round-trips.
-- High security: Outright rejection of the ID at the point of creation /
+- Reject event: Outright rejection of the ID at the point of creation /
receiving event. Point of creation rejection is preferable to avoid the ID
entering the system in the first place. However, malicious HSes can just
allow the ID. Hence, other homeservers must reject them if they see them in
events. Client never sees the problem ID, provided the HS is correctly
- implemented.
-- High security decided; client doesn't need to worry about it, no additional
- protocol complexity aside from rejection of an event.
+ implemented. However, it is difficult to ensure that ALL HSes will come to the
+ same conclusion (given the CLDR dataset does come out with new versions).
+
+Outstanding Problems
+====================
+
+Capitalisation
+--------------
+
+The capitalisation rules outlined above are nice but do not fully resolve issues
+where ``@alice:example.com`` tries to speak with ``@bob:domain.com`` using
+``@Bob:domain.com``. It is up to ``domain.com`` to map ``Bob`` to ``bob`` in
+a sensible way.
diff --git a/drafts/state_resolution.rst b/drafts/state_resolution.rst
deleted file mode 100644
index 3c570b8e..00000000
--- a/drafts/state_resolution.rst
+++ /dev/null
@@ -1,52 +0,0 @@
-State Resolution
-================
-This section describes why we need state resolution and how it works.
-
-
-Motivation
------------
-We want to be able to associate some shared state with rooms, e.g. a room name
-or members list. This is done by having a current state dictionary that maps
-from the pair event type and state key to an event.
-
-However, since the servers involved in the room are distributed we need to be
-able to handle the case when two (or more) servers try and update the state at
-the same time. This is done via the state resolution algorithm.
-
-
-State Tree
-------------
-State events contain a reference to the state it is trying to replace. These
-relations form a tree where the current state is one of the leaf nodes.
-
-Note that state events are events, and so are part of the PDU graph. Thus we
-can be sure that (modulo the internet being particularly broken) we will see
-all state events eventually.
-
-
-Algorithm requirements
-----------------------
-We want the algorithm to have the following properties:
-
-- Since we aren't guaranteed what order we receive state events in, except that
- we see parents before children, the state resolution algorithm must not depend
- on the order and must always come to the same result.
-- If we receive a state event whose parent is the current state, then the
- algorithm will select it.
-- The algorithm does not depend on internal state, ensuring all servers should
- come to the same decision.
-
-These three properties mean it is enough to keep track of the current state and
-compare it with any new proposed state, rather than having to keep track of all
-the leafs of the tree and recomputing across the entire state tree.
-
-
-Current Implementation
-----------------------
-The current implementation works as follows: Upon receipt of a newly proposed
-state change we first find the common ancestor. Then we take the maximum
-across each branch of the users' power levels, if one is higher then it is
-selected as the current state. Otherwise, we check if one chain is longer than
-the other, if so we choose that one. If that also fails, then we concatenate
-all the pdu ids and take a SHA1 hash and compare them to select a common
-ancestor.
diff --git a/drafts/websockets.rst b/drafts/websockets.rst
index bd0ff081..0fe8d968 100644
--- a/drafts/websockets.rst
+++ b/drafts/websockets.rst
@@ -14,6 +14,9 @@ will reduce the latency between server and client by allowing the server to
send events as soon as they arrive, rather than having to wait for a poll from
the client.
+Note: This proposal got continued in a google document you can find here:
+https://docs.google.com/document/d/104ClehFBgqLQbf4s-AKX2ijr8sOAxcizfcRs_atsB0g
+
Handshake
---------
1. Instead of calling ``/sync``, the client makes a websocket request to
diff --git a/event-schemas/examples/m.ignored_user_list b/event-schemas/examples/m.ignored_user_list
new file mode 100644
index 00000000..f3a328f7
--- /dev/null
+++ b/event-schemas/examples/m.ignored_user_list
@@ -0,0 +1,8 @@
+{
+ "type": "m.ignored_user_list",
+ "content": {
+ "ignored_users": {
+ "@someone:domain.com": {}
+ }
+ }
+}
diff --git a/event-schemas/examples/m.presence b/event-schemas/examples/m.presence
index ead92ccd..824ffcb7 100644
--- a/event-schemas/examples/m.presence
+++ b/event-schemas/examples/m.presence
@@ -3,9 +3,8 @@
"avatar_url": "mxc://localhost:wefuiwegh8742w",
"last_active_ago": 2478593,
"presence": "online",
- "currently_active": false,
- "user_id": "@example:localhost"
+ "currently_active": false
},
- "event_id": "$WLGTSEFSEF:localhost",
+ "sender": "@example:localhost",
"type": "m.presence"
}
diff --git a/event-schemas/examples/m.room.encrypted#megolm b/event-schemas/examples/m.room.encrypted#megolm
new file mode 100644
index 00000000..1f9b7520
--- /dev/null
+++ b/event-schemas/examples/m.room.encrypted#megolm
@@ -0,0 +1,14 @@
+{
+ "content": {
+ "algorithm": "m.megolm.v1.aes-sha2",
+ "ciphertext": "AwgAEnACgAkLmt6qF84IK++J7UDH2Za1YVchHyprqTqsg...",
+ "device_id": "RJYKSTBOIE",
+ "sender_key": "IlRMeOPX2e0MurIyfWEucYBRVOEEUMrOHqn/8mLqMjA",
+ "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ"
+ },
+ "event_id": "$WLGTSEFSEF:localhost",
+ "room_id": "!Cuyf34gef24t:localhost",
+ "origin_server_ts": 1476648761524,
+ "sender": "@example:localhost",
+ "type": "m.room.encrypted"
+}
diff --git a/event-schemas/examples/m.room.encrypted#olm b/event-schemas/examples/m.room.encrypted#olm
new file mode 100644
index 00000000..abb23c31
--- /dev/null
+++ b/event-schemas/examples/m.room.encrypted#olm
@@ -0,0 +1,14 @@
+{
+ "type": "m.room.encrypted",
+ "sender": "@example:localhost",
+ "content": {
+ "algorithm": "m.olm.v1.curve25519-aes-sha2",
+ "sender_key": "Szl29ksW/L8yZGWAX+8dY1XyFi+i5wm+DRhTGkbMiwU",
+ "ciphertext": {
+ "7qZcfnBmbEGzxxaWfBjElJuvn7BZx+lSz/SvFrDF/z8": {
+ "type": 0,
+ "body": "AwogGJJzMhf/S3GQFXAOrCZ3iKyGU5ZScVtjI0KypTYrW..."
+ }
+ }
+ }
+}
diff --git a/event-schemas/examples/m.room.encryption b/event-schemas/examples/m.room.encryption
new file mode 100644
index 00000000..08f15239
--- /dev/null
+++ b/event-schemas/examples/m.room.encryption
@@ -0,0 +1,13 @@
+{
+ "content": {
+ "algorithm": "m.megolm.v1.aes-sha2",
+ "rotation_period_ms": 604800000,
+ "rotation_period_msgs": 100
+ },
+ "event_id": "$WLGTSEFJJKJ:localhost",
+ "origin_server_ts": 1476648761524,
+ "sender": "@example:localhost",
+ "room_id": "!Cuyf34gef24t:localhost",
+ "state_key": "",
+ "type": "m.room.encryption"
+}
diff --git a/event-schemas/examples/m.room.member b/event-schemas/examples/m.room.member
index 133cad96..2495145b 100644
--- a/event-schemas/examples/m.room.member
+++ b/event-schemas/examples/m.room.member
@@ -5,22 +5,6 @@
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto",
"displayname": "Alice Margatroid"
},
- "invite_room_state": [
- {
- "type": "m.room.name",
- "state_key": "",
- "content": {
- "name": "Forest of Magic"
- }
- },
- {
- "type": "m.room.join_rules",
- "state_key": "",
- "content": {
- "join_rules": "invite"
- }
- }
- ],
"state_key": "@alice:localhost",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
diff --git a/event-schemas/examples/m.room.member#invite_room_state b/event-schemas/examples/m.room.member#invite_room_state
index 133cad96..965669ad 100644
--- a/event-schemas/examples/m.room.member#invite_room_state
+++ b/event-schemas/examples/m.room.member#invite_room_state
@@ -1,26 +1,28 @@
{
"age": 242352,
"content": {
- "membership": "join",
+ "membership": "invite",
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto",
"displayname": "Alice Margatroid"
},
- "invite_room_state": [
- {
- "type": "m.room.name",
- "state_key": "",
- "content": {
- "name": "Forest of Magic"
+ "unsigned": {
+ "invite_room_state": [
+ {
+ "type": "m.room.name",
+ "state_key": "",
+ "content": {
+ "name": "Forest of Magic"
+ }
+ },
+ {
+ "type": "m.room.join_rules",
+ "state_key": "",
+ "content": {
+ "join_rule": "invite"
+ }
}
- },
- {
- "type": "m.room.join_rules",
- "state_key": "",
- "content": {
- "join_rules": "invite"
- }
- }
- ],
+ ]
+ },
"state_key": "@alice:localhost",
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
diff --git a/event-schemas/examples/m.room.member#third_party_invite b/event-schemas/examples/m.room.member#third_party_invite
index 61718ba1..244e1556 100644
--- a/event-schemas/examples/m.room.member#third_party_invite
+++ b/event-schemas/examples/m.room.member#third_party_invite
@@ -1,7 +1,7 @@
{
"age": 242352,
"content": {
- "membership": "join",
+ "membership": "invite",
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto",
"displayname": "Alice Margatroid",
"third_party_invite": {
diff --git a/event-schemas/examples/m.room.message#m.emote b/event-schemas/examples/m.room.message#m.emote
index 4280928e..79292ddf 100644
--- a/event-schemas/examples/m.room.message#m.emote
+++ b/event-schemas/examples/m.room.message#m.emote
@@ -2,7 +2,9 @@
"age": 242352,
"content": {
"body": "thinks this is an example emote",
- "msgtype": "m.emote"
+ "msgtype": "m.emote",
+ "format": "org.matrix.custom.html",
+ "formatted_body": "thinks this is an example emote"
},
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
diff --git a/event-schemas/examples/m.room.message#m.notice b/event-schemas/examples/m.room.message#m.notice
index 978c67e6..876cbbb7 100644
--- a/event-schemas/examples/m.room.message#m.notice
+++ b/event-schemas/examples/m.room.message#m.notice
@@ -2,7 +2,9 @@
"age": 242352,
"content": {
"body": "This is an example notice",
- "msgtype": "m.notice"
+ "msgtype": "m.notice",
+ "format": "org.matrix.custom.html",
+ "formatted_body": "This is an example notice"
},
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
diff --git a/event-schemas/examples/m.room.message#m.text b/event-schemas/examples/m.room.message#m.text
index e00c7aa5..48a97db8 100644
--- a/event-schemas/examples/m.room.message#m.text
+++ b/event-schemas/examples/m.room.message#m.text
@@ -2,7 +2,9 @@
"age": 242352,
"content": {
"body": "This is an example text message",
- "msgtype": "m.text"
+ "msgtype": "m.text",
+ "format": "org.matrix.custom.html",
+ "formatted_body": "This is an example text message"
},
"origin_server_ts": 1431961217939,
"event_id": "$WLGTSEFSEF:localhost",
diff --git a/event-schemas/examples/m.room.pinned_events b/event-schemas/examples/m.room.pinned_events
new file mode 100644
index 00000000..6f41e97d
--- /dev/null
+++ b/event-schemas/examples/m.room.pinned_events
@@ -0,0 +1,12 @@
+{
+ "age": 242352,
+ "content": {
+ "pinned": ["$someevent:localhost"]
+ },
+ "state_key": "",
+ "origin_server_ts": 1431961217939,
+ "event_id": "$WLGTSEFSEF:localhost",
+ "type": "m.room.pinned_events",
+ "room_id": "!Cuyf34gef24t:localhost",
+ "sender": "@example:localhost"
+}
diff --git a/event-schemas/examples/m.room.redaction b/event-schemas/examples/m.room.redaction
index 46df2ab4..e24a8cdb 100644
--- a/event-schemas/examples/m.room.redaction
+++ b/event-schemas/examples/m.room.redaction
@@ -9,6 +9,6 @@
"event_id": "$WLGTSEFSEF:localhost",
"type": "m.room.redaction",
"room_id": "!Cuyf34gef24t:localhost",
- "redacts": "!fukweghifu23:localhost",
+ "redacts": "$fukweghifu23:localhost",
"sender": "@example:localhost"
}
diff --git a/event-schemas/examples/m.room.server_acl b/event-schemas/examples/m.room.server_acl
new file mode 100644
index 00000000..86da2093
--- /dev/null
+++ b/event-schemas/examples/m.room.server_acl
@@ -0,0 +1,14 @@
+{
+ "age": 242352,
+ "content": {
+ "allow_ip_literals": false,
+ "allow": ["*"],
+ "deny": ["*.evil.com", "evil.com"]
+ },
+ "state_key": "",
+ "origin_server_ts": 1431961217939,
+ "event_id": "$WLGTSEFSEF:localhost",
+ "type": "m.room.server_acl",
+ "room_id": "!Cuyf34gef24t:localhost",
+ "sender": "@example:localhost"
+}
diff --git a/event-schemas/examples/m.room_key b/event-schemas/examples/m.room_key
new file mode 100644
index 00000000..53f83e52
--- /dev/null
+++ b/event-schemas/examples/m.room_key
@@ -0,0 +1,9 @@
+{
+ "content": {
+ "algorithm": "m.megolm.v1.aes-sha2",
+ "room_id": "!Cuyf34gef24t:localhost",
+ "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ",
+ "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..."
+ },
+ "type": "m.room_key"
+}
diff --git a/event-schemas/examples/m.sticker b/event-schemas/examples/m.sticker
new file mode 100644
index 00000000..f00e5b23
--- /dev/null
+++ b/event-schemas/examples/m.sticker
@@ -0,0 +1,25 @@
+{
+ "age": 242352,
+ "content": {
+ "body": "Landing",
+ "info": {
+ "mimetype": "image/png",
+ "thumbnail_info": {
+ "mimetype": "image/png",
+ "h": 200,
+ "w": 140,
+ "size": 73602
+ },
+ "h": 200,
+ "thumbnail_url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP",
+ "w": 140,
+ "size": 73602
+ },
+ "url": "mxc://matrix.org/sHhqkFCvSkFwtmvtETOtKnLP"
+ },
+ "origin_server_ts": 1431961217939,
+ "event_id": "$WLGTSEFSEF:localhost",
+ "type": "m.sticker",
+ "room_id": "!Cuyf34gef24t:localhost",
+ "sender": "@example:localhost"
+}
diff --git a/event-schemas/examples/m.tag b/event-schemas/examples/m.tag
index 00e81060..53dbc921 100644
--- a/event-schemas/examples/m.tag
+++ b/event-schemas/examples/m.tag
@@ -2,7 +2,7 @@
"type": "m.tag",
"content": {
"tags": {
- "work": {"order": 1}
+ "u.work": {"order": 1}
}
}
}
diff --git a/event-schemas/schema/core-event-schema/event.yaml b/event-schemas/schema/core-event-schema/event.yaml
index ebf67aa3..7a060283 100644
--- a/event-schemas/schema/core-event-schema/event.yaml
+++ b/event-schemas/schema/core-event-schema/event.yaml
@@ -3,11 +3,12 @@ properties:
content:
description: The fields in this object will vary depending on the type of event.
When interacting with the REST API, this is the HTTP body.
- title: EventContent
type: object
type:
description: The type of event. This SHOULD be namespaced similar to Java package
naming conventions e.g. 'com.example.subdomain.event.type'
type: string
+required:
+ - type
title: Event
type: object
diff --git a/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml b/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml
index b4eab413..4d2a9964 100644
--- a/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml
+++ b/event-schemas/schema/core-event-schema/msgtype_infos/image_info.yaml
@@ -2,10 +2,14 @@ $schema: http://json-schema.org/draft-04/schema#
description: Metadata about an image.
properties:
h:
- description: The height of the image in pixels.
+ description: |-
+ The intended display height of the image in pixels. This may
+ differ from the intrinsic dimensions of the image file.
type: integer
w:
- description: The width of the image in pixels.
+ description: |-
+ The intended display width of the image in pixels. This may
+ differ from the intrinsic dimensions of the image file.
type: integer
mimetype:
description: The mimetype of the image, e.g. ``image/jpeg``.
diff --git a/event-schemas/schema/core-event-schema/msgtype_infos/thumbnail_info.yaml b/event-schemas/schema/core-event-schema/msgtype_infos/thumbnail_info.yaml
index 5a233b24..82ffaf5e 100644
--- a/event-schemas/schema/core-event-schema/msgtype_infos/thumbnail_info.yaml
+++ b/event-schemas/schema/core-event-schema/msgtype_infos/thumbnail_info.yaml
@@ -2,10 +2,14 @@ $schema: http://json-schema.org/draft-04/schema#
description: Metadata about a thumbnail image.
properties:
h:
- description: The height of the image in pixels.
+ description: |-
+ The intended display height of the image in pixels. This may
+ differ from the intrinsic dimensions of the image file.
type: integer
w:
- description: The width of the image in pixels.
+ description: |-
+ The intended display width of the image in pixels. This may
+ differ from the intrinsic dimensions of the image file.
type: integer
mimetype:
description: The mimetype of the image, e.g. ``image/jpeg``.
diff --git a/event-schemas/schema/core-event-schema/room_event.yaml b/event-schemas/schema/core-event-schema/room_event.yaml
index de72f570..ebf970ad 100644
--- a/event-schemas/schema/core-event-schema/room_event.yaml
+++ b/event-schemas/schema/core-event-schema/room_event.yaml
@@ -4,19 +4,20 @@ description: In addition to the Event fields, Room Events have the following add
fields.
properties:
event_id:
- description: Required. The globally unique event identifier.
+ description: The globally unique event identifier.
type: string
room_id:
- description: Required. The ID of the room associated with this event.
+ description: The ID of the room associated with this event.
type: string
sender:
- description: Required. Contains the fully-qualified ID of the user who *sent*
+ description: Contains the fully-qualified ID of the user who *sent*
this event.
type: string
origin_server_ts:
- description: Required. Timestamp in milliseconds on originating homeserver
+ description: Timestamp in milliseconds on originating homeserver
when this event was sent.
- type: number
+ type: integer
+ format: int64
unsigned:
description: Contains optional extra information about the event.
properties:
diff --git a/event-schemas/schema/m.ignored_user_list b/event-schemas/schema/m.ignored_user_list
new file mode 100644
index 00000000..7f7a6604
--- /dev/null
+++ b/event-schemas/schema/m.ignored_user_list
@@ -0,0 +1,28 @@
+---
+allOf:
+ - $ref: core-event-schema/event.yaml
+description: |-
+ A map of users which are considered ignored is kept in ``account_data``
+ in an event type of ``m.ignored_user_list``.
+properties:
+ content:
+ type: object
+ properties:
+ ignored_users:
+ type: object
+ title: "Ignored users"
+ description: "The map of users to ignore"
+ patternProperties:
+ "^@":
+ type: "object"
+ title: "Ignored User"
+ description: "An empty object for future enhancement"
+ x-pattern: "$USER_ID"
+ required:
+ - ignored_users
+ type:
+ enum:
+ - m.ignored_user_list
+ type: string
+title: Ignored User List
+type: object
diff --git a/event-schemas/schema/m.presence b/event-schemas/schema/m.presence
index 36108db6..3c884fd6 100644
--- a/event-schemas/schema/m.presence
+++ b/event-schemas/schema/m.presence
@@ -29,21 +29,17 @@
"currently_active": {
"type": boolean,
"description": "Whether the user is currently active"
- },
- "user_id": {
- "type": "string",
- "description": "The user's ID."
}
},
- "required": ["presence", "user_id"]
+ "required": ["presence"]
},
"type": {
"type": "string",
"enum": ["m.presence"]
},
- "event_id": {
+ "sender": {
"type": "string"
}
},
- "required": ["event_id", "type", "content"]
+ "required": ["sender", "type", "content"]
}
diff --git a/event-schemas/schema/m.room.create b/event-schemas/schema/m.room.create
index a07ab90f..d12b9ccd 100644
--- a/event-schemas/schema/m.room.create
+++ b/event-schemas/schema/m.room.create
@@ -11,6 +11,9 @@ properties:
m.federate:
description: Whether users on other servers can join this room. Defaults to ``true`` if key does not exist.
type: boolean
+ room_version:
+ description: The version of the room. Defaults to ``"1"`` if the key does not exist.
+ type: string
required:
- creator
type: object
diff --git a/event-schemas/schema/m.room.encrypted b/event-schemas/schema/m.room.encrypted
new file mode 100644
index 00000000..44b09c3f
--- /dev/null
+++ b/event-schemas/schema/m.room.encrypted
@@ -0,0 +1,61 @@
+---
+allOf:
+ - $ref: core-event-schema/event.yaml
+
+description: |-
+ This event type is used when sending encrypted events. It can be used either
+ within a room (in which case it will have all of the `Room Event fields`_), or
+ as a `to-device`_ event.
+
+properties:
+ content:
+ properties:
+ algorithm:
+ type: string
+ enum:
+ - m.olm.v1.curve25519-aes-sha2
+ - m.megolm.v1.aes-sha2
+ description: |-
+ The encryption algorithm used to encrypt this event. The
+ value of this field determines which other properties will be
+ present.
+ ciphertext:
+ oneOf:
+ - type: string
+ - type: object
+ additionalProperties:
+ type: object
+ title: CiphertextInfo
+ properties:
+ body:
+ type: string
+ description: The encrypted payload.
+ type:
+ type: integer
+ description: The Olm message type.
+ description: |-
+ The encrypted content of the event. Either the encrypted payload
+ itself, in the case of a Megolm event, or a map from the recipient
+ Curve25519 identity key to ciphertext information, in the case of an
+ Olm event. For more details, see `Messaging Algorithms`_.
+ sender_key:
+ type: string
+ description: The Curve25519 key of the sender.
+ device_id:
+ type: string
+ description: The ID of the sending device. Required with Megolm.
+ session_id:
+ type: string
+ description: |-
+ The ID of the session used to encrypt the message. Required with
+ Megolm.
+ required:
+ - algorithm
+ - sender_key
+ - ciphertext
+ type: object
+ type:
+ enum:
+ - m.room.encrypted
+ type: string
+type: object
diff --git a/event-schemas/schema/m.room.encryption b/event-schemas/schema/m.room.encryption
new file mode 100644
index 00000000..d7c4d429
--- /dev/null
+++ b/event-schemas/schema/m.room.encryption
@@ -0,0 +1,36 @@
+---
+allOf:
+ - $ref: core-event-schema/state_event.yaml
+description: Defines how messages sent in this room should be encrypted.
+properties:
+ content:
+ properties:
+ algorithm:
+ type: string
+ enum:
+ - "m.megolm.v1.aes-sha2"
+ description: |-
+ The encryption algorithm to be used to encrypt messages sent in this
+ room.
+ rotation_period_ms:
+ type: integer
+ description: |-
+ How long the session should be used before changing it. ``604800000``
+ (a week) is the recommended default.
+ rotation_period_msgs:
+ type: integer
+ description: |-
+ How many messages should be sent before changing the session. ``100`` is the
+ recommended default.
+ required:
+ - algorithm
+ type: object
+ state_key:
+ description: A zero-length string.
+ pattern: '^$'
+ type: string
+ type:
+ enum:
+ - m.room.encryption
+ type: string
+type: object
diff --git a/event-schemas/schema/m.room.member b/event-schemas/schema/m.room.member
index 4f4077a7..5fb5356d 100644
--- a/event-schemas/schema/m.room.member
+++ b/event-schemas/schema/m.room.member
@@ -18,7 +18,9 @@ description: |-
The ``third_party_invite`` property will be set if this invite is an ``invite`` event and is the successor of an ``m.room.third_party_invite`` event, and absent otherwise.
- This event may also include an ``invite_room_state`` key **outside the** ``content`` **key**. If present, this contains an array of ``StrippedState`` Events. These events provide information on a subset of state events such as the room name.
+ This event may also include an ``invite_room_state`` key inside the event's ``unsigned`` data.
+ If present, this contains an array of ``StrippedState`` Events. These events provide information
+ on a subset of state events such as the room name.
properties:
content:
properties:
@@ -71,32 +73,37 @@ properties:
- signed
title: Invite
type: object
+ unsigned:
+ type: object
+ title: UnsignedData
+ description: Contains optional extra information about the event.
+ properties:
+ invite_room_state:
+ description: 'A subset of the state of the room at the time of the invite, if ``membership`` is ``invite``. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for ``m.room.avatar``, ``m.room.canonical_alias``, ``m.room.join_rules``, and ``m.room.name`` SHOULD be included.'
+ items:
+ description: 'A stripped down state event, with only the ``type``, ``state_key`` and ``content`` keys.'
+ properties:
+ content:
+ description: The ``content`` for the event.
+ title: EventContent
+ type: object
+ state_key:
+ description: The ``state_key`` for the event.
+ type: string
+ type:
+ description: The ``type`` for the event.
+ type: string
+ required:
+ - type
+ - state_key
+ - content
+ title: StrippedState
+ type: object
+ type: array
required:
- membership
title: EventContent
type: object
- invite_room_state:
- description: 'A subset of the state of the room at the time of the invite, if ``membership`` is ``invite``. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for ``m.room.avatar``, ``m.room.canonical_alias``, ``m.room.join_rules``, and ``m.room.name`` SHOULD be included.'
- items:
- description: 'A stripped down state event, with only the ``type``, ``state_key`` and ``content`` keys.'
- properties:
- content:
- description: The ``content`` for the event.
- title: EventContent
- type: object
- state_key:
- description: The ``state_key`` for the event.
- type: string
- type:
- description: The ``type`` for the event.
- type: string
- required:
- - type
- - state_key
- - content
- title: StrippedState
- type: object
- type: array
state_key:
description: The ``user_id`` this membership event relates to.
type: string
diff --git a/event-schemas/schema/m.room.message#m.emote b/event-schemas/schema/m.room.message#m.emote
index 88860cb2..f67a184b 100644
--- a/event-schemas/schema/m.room.message#m.emote
+++ b/event-schemas/schema/m.room.message#m.emote
@@ -12,6 +12,16 @@ properties:
enum:
- m.emote
type: string
+ format:
+ description: |-
+ The format used in the ``formatted_body``. Currently only
+ ``org.matrix.custom.html`` is supported.
+ type: string
+ formatted_body:
+ description: |-
+ The formatted version of the ``body``. This is required if ``format``
+ is specified.
+ type: string
required:
- msgtype
- body
diff --git a/event-schemas/schema/m.room.message#m.text b/event-schemas/schema/m.room.message#m.text
index 2720172d..b481bcea 100644
--- a/event-schemas/schema/m.room.message#m.text
+++ b/event-schemas/schema/m.room.message#m.text
@@ -12,6 +12,16 @@ properties:
enum:
- m.text
type: string
+ format:
+ description: |-
+ The format used in the ``formatted_body``. Currently only
+ ``org.matrix.custom.html`` is supported.
+ type: string
+ formatted_body:
+ description: |-
+ The formatted version of the ``body``. This is required if ``format``
+ is specified.
+ type: string
required:
- msgtype
- body
diff --git a/event-schemas/schema/m.room.pinned_events b/event-schemas/schema/m.room.pinned_events
new file mode 100644
index 00000000..7b4a0d61
--- /dev/null
+++ b/event-schemas/schema/m.room.pinned_events
@@ -0,0 +1,25 @@
+---
+allOf:
+ - $ref: core-event-schema/state_event.yaml
+description: This event is used to "pin" particular events in a room for other participants to review later. The order of the pinned events is guaranteed and based upon the order supplied in the event. Clients should be aware that the current user may not be able to see some of the events pinned due to visibility settings in the room. Clients are responsible for determining if a particular event in the pinned list is displayable, and have the option to not display it if it cannot be pinned in the client.
+properties:
+ content:
+ properties:
+ pinned:
+ description: An ordered list of event IDs to pin.
+ items:
+ type: string
+ type: array
+ required:
+ - pinned
+ type: object
+ state_key:
+ description: A zero-length string.
+ pattern: '^$'
+ type: string
+ type:
+ enum:
+ - m.room.pinned_events
+ type: string
+title: Pinned events in a room
+type: object
diff --git a/event-schemas/schema/m.room.power_levels b/event-schemas/schema/m.room.power_levels
index 0353b166..b00d86a9 100644
--- a/event-schemas/schema/m.room.power_levels
+++ b/event-schemas/schema/m.room.power_levels
@@ -8,7 +8,8 @@ description: |-
If a ``user_id`` is in the ``users`` list, then that ``user_id`` has the
associated power level. Otherwise they have the default level
``users_default``. If ``users_default`` is not supplied, it is assumed to be
- 0.
+ 0. If the room contains no ``m.room.power_levels`` event, the room's creator has
+ a power level of 100, and all other users have a power level of 0.
The level required to send a certain event is governed by ``events``,
``state_default`` and ``events_default``. If an event type is specified in
@@ -29,15 +30,26 @@ description: |-
to 50 if they are not specified in the ``m.room.power_levels`` event, or if
the room contains no ``m.room.power_levels`` event.
+ .. NOTE::
+
+ As noted above, in the absence of an ``m.room.power_levels`` event, the
+ ``state_default`` is 0, and all users are considered to have power level 0.
+ That means that **any** member of the room can send an
+ ``m.room.power_levels`` event, changing the permissions in the room.
+
+ Server implementations should therefore ensure that each room has an
+ ``m.room.power_levels`` event as soon as it is created. See also the
+ documentation of the ``/createRoom`` API.
+
properties:
content:
properties:
ban:
description: The level required to ban a user. Defaults to 50 if unspecified.
- type: number
+ type: integer
events:
additionalProperties:
- type: number
+ type: integer
description: The level required to send specific event types. This is a mapping from event type to power level required.
title: Event power levels
type: object
@@ -45,25 +57,25 @@ properties:
description: |-
The default level required to send message events. Can be
overridden by the ``events`` key. Defaults to 0 if unspecified.
- type: number
+ type: integer
invite:
description: The level required to invite a user. Defaults to 50 if unspecified.
- type: number
+ type: integer
kick:
description: The level required to kick a user. Defaults to 50 if unspecified.
- type: number
+ type: integer
redact:
description: The level required to redact an event. Defaults to 50 if unspecified.
- type: number
+ type: integer
state_default:
description: |-
The default level required to send state events. Can be overridden
by the ``events`` key. Defaults to 50 if unspecified, but 0 if
there is no ``m.room.power_levels`` event at all.
- type: number
+ type: integer
users:
additionalProperties:
- type: number
+ type: integer
description: The power levels for specific users. This is a mapping from ``user_id`` to power level for that user.
title: User power levels
type: object
@@ -72,7 +84,7 @@ properties:
The default power level for every user in the room, unless their
``user_id`` is mentioned in the ``users`` key. Defaults to 0 if
unspecified.
- type: number
+ type: integer
type: object
state_key:
description: A zero-length string.
diff --git a/event-schemas/schema/m.room.server_acl b/event-schemas/schema/m.room.server_acl
new file mode 100644
index 00000000..86d83832
--- /dev/null
+++ b/event-schemas/schema/m.room.server_acl
@@ -0,0 +1,88 @@
+---
+title: Server ACL
+description: |-
+ An event to indicate which servers are permitted to participate in the
+ room. Server ACLs may allow or deny groups of hosts. All servers participating
+ in the room, including those that are denied, are expected to uphold the
+ server ACL. Servers that do not uphold the ACLs MUST be added to the denied hosts
+ list in order for the ACLs to remain effective.
+
+ The ``allow`` and ``deny`` lists are lists of globs supporting ``?`` and ``*``
+ as wildcards. When comparing against the server ACLs, the suspect server's port
+ number must not be considered. Therefore ``evil.com``, ``evil.com:8448``, and
+ ``evil.com:1234`` would all match rules that apply to ``evil.com``, for example.
+
+ The ACLs are applied to servers when they make requests, and are applied in
+ the following order:
+
+ 1. If there is no ``m.room.server_acl`` event in the room state, allow.
+ #. If the server name is an IP address (v4 or v6) literal, and ``allow_ip_literals``
+ is present and ``false``, deny.
+ #. If the server name matches an entry in the ``deny`` list, deny.
+ #. If the server name matches an entry in the ``allow`` list, allow.
+ #. Otherwise, deny.
+
+ .. Note::
+ Server ACLs do not restrict the events relative to the room DAG via authorisation
+ rules, but instead act purely at the network layer to determine which servers are
+ allowed to connect and interact with a given room.
+
+ .. WARNING::
+ Failing to provide an ``allow`` rule of some kind will prevent **all**
+ servers from participating in the room, including the sender. This renders
+ the room unusable. A common allow rule is ``[ "*" ]`` which would still
+ permit the use of the ``deny`` list without losing the room.
+
+ .. WARNING::
+ All compliant servers must implement server ACLs. However, legacy or noncompliant
+ servers exist which do not uphold ACLs, and these MUST be manually appended to
+ the denied hosts list when setting an ACL to prevent them from leaking events from
+ banned servers into a room. Currently, the only way to determine noncompliant hosts is
+ to check the ``prev_events`` of leaked events, therefore detecting servers which
+ are not upholding the ACLs. Server versions can also be used to try to detect hosts that
+ will not uphold the ACLs, although this is not comprehensive. Server ACLs were added
+ in Synapse v0.32.0, although other server implementations and versions exist in the world.
+allOf:
+ - $ref: core-event-schema/state_event.yaml
+type: object
+properties:
+ content:
+ properties:
+ allow_ip_literals:
+ type: boolean
+ description: |-
+ True to allow server names that are IP address literals. False to
+ deny. Defaults to true if missing or otherwise not a boolean.
+
+ This is strongly recommended to be set to ``false`` as servers running
+ with IP literal names are strongly discouraged in order to require
+ legitimate homeservers to be backed by a valid registered domain name.
+ allow:
+ type: array
+ description: |-
+ The server names to allow in the room, excluding any port information.
+ Wildcards may be used to cover a wider range of hosts, where ``*``
+ matches zero or more characters and ``?`` matches exactly one character.
+
+ **This defaults to an empty list when not provided, effectively disallowing
+ every server.**
+ items:
+ type: string
+ deny:
+ type: array
+ description: |-
+ The server names to disallow in the room, excluding any port information.
+ Wildcards may be used to cover a wider range of hosts, where ``*``
+ matches zero or more characters and ``?`` matches exactly one character.
+
+ This defaults to an empty list when not provided.
+ items:
+ type: string
+ type: object
+ state_key:
+ description: A zero-length string.
+ pattern: '^$'
+ type: string
+ type:
+ enum: ['m.room.server_acl']
+ type: string
diff --git a/event-schemas/schema/m.room_key b/event-schemas/schema/m.room_key
new file mode 100644
index 00000000..ef8c51c0
--- /dev/null
+++ b/event-schemas/schema/m.room_key
@@ -0,0 +1,35 @@
+---
+allOf:
+ - $ref: core-event-schema/event.yaml
+
+description: |-
+ This event type is used to exchange keys for end-to-end encryption. Typically
+ it is encrypted as an ``m.room.encrypted`` event, then sent as a `to-device`_ event.
+properties:
+ content:
+ properties:
+ algorithm:
+ type: string
+ enum: ["m.megolm.v1.aes-sha2"]
+ description: |-
+ The encryption algorithm the key in this event is to be used with.
+ room_id:
+ type: string
+ description: The room where the key is used.
+ session_id:
+ type: string
+ description: The ID of the session that the key is for.
+ session_key:
+ type: string
+ description: The key to be exchanged.
+ required:
+ - algorithm
+ - room_id
+ - session_id
+ - session_key
+ type: object
+ type:
+ enum:
+ - m.room_key
+ type: string
+type: object
diff --git a/event-schemas/schema/m.sticker b/event-schemas/schema/m.sticker
new file mode 100644
index 00000000..1dd1173c
--- /dev/null
+++ b/event-schemas/schema/m.sticker
@@ -0,0 +1,34 @@
+---
+allOf:
+ - $ref: core-event-schema/room_event.yaml
+description: This message represents a single sticker image.
+properties:
+ content:
+ properties:
+ body:
+ description: |-
+ A textual representation or associated description of the sticker
+ image. This could be the alt text of the original image, or a message
+ to accompany and further describe the sticker.
+ type: string
+ info:
+ allOf:
+ - $ref: core-event-schema/msgtype_infos/image_info.yaml
+ description: |-
+ Metadata about the image referred to in ``url`` including a thumbnail
+ representation.
+ url:
+ description: |-
+ The URL to the sticker image. This must be a valid ``mxc://`` URI.
+ type: string
+ required:
+ - body
+ - info
+ - url
+ type: object
+ type:
+ enum:
+ - m.sticker
+ type: string
+title: StickerMessage
+type: object
diff --git a/jenkins.sh b/jenkins.sh
index 717e8644..79b77acb 100755
--- a/jenkins.sh
+++ b/jenkins.sh
@@ -1,18 +1,3 @@
-#! /bin/bash
-
-set -ex
-
-(cd event-schemas/ && ./check_examples.py)
-(cd api && ./check_examples.py)
-(cd scripts && ./gendoc.py -v)
-(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.v1
-
-(cd scripts/continuserv && go build)
-(cd scripts/speculator && go build)
+#!/bin/sh
+exec ./scripts/test-and-build.sh
diff --git a/meta/documentation_style.rst b/meta/documentation_style.rst
index a6cfad51..c6bb62bf 100644
--- a/meta/documentation_style.rst
+++ b/meta/documentation_style.rst
@@ -1,8 +1,9 @@
Documentation Style
===================
-A brief single sentence to describe what this file contains; in this case a
-description of the style to write documentation in.
+Each document should begin with a brief single sentence to describe what this
+file contains: in this case a description of the style to write documentation
+in.
Format
------
@@ -24,13 +25,19 @@ complete specification to be merged correctly. These characters are:
- ``~``
- ``+``
- ``^``
-- `````
+- \ `````
- ``@``
- ``:``
If you find yourself using ``^`` or beyond, you should rethink your document
layout if possible.
+Correct capitalisation for long section names
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Headings should start with a capital letter, and use lower-case otherwise.
+
+
TODOs
-----
@@ -49,3 +56,57 @@ Line widths
We use 80 characters for line widths. This is a guideline and can be flouted IF
AND ONLY IF it makes reading more legible. Use common sense.
+
+Stylistic notes
+---------------
+
+General
+~~~~~~~
+
+Try to write clearly and unambiguously. Remember that many readers will not
+have English as their first language.
+
+Prefer British English (colour, -ise) to American English.
+
+The word "homeserver" is spelt thus (rather than "home server", "Homeserver",
+or (argh) "Home Server"). However, an identity server is two words.
+
+.. Rationale: "homeserver" distinguishes from a "home server" which is a server
+ you have at home. "Identity server" is clear, whereas "identityserver" is
+ horrible.
+
+Lists should:
+
+* Be introduced with a colon.
+* Be used where they provide clarity.
+* Contain entries which start with a capital and end with a full stop.
+
+OpenAPI
+~~~~~~~
+
+When writing OpenAPI specifications for the API endpoints, follow these rules:
+
+* ``summary``: a phrase summarising what this API does. Start with a capital,
+ end with a full stop. Examples: "Sends an event."; "Searches the directory."
+
+* ``description``: a longer description of the behaviour of this API, written
+ in complete sentences. Use multiple paragraphs if necessary.
+
+ Example:
+
+ This API sends an event to the room. The server must ensure that the user
+ has permission to post events to this room.
+
+* ``operationId``: a camelCased unique identifier for this endpoint. This will
+ be used to automatically generate bindings for the endpoint.
+
+* Parameter and property ``description``\s: a phrase summarising the behaviour
+ of this parameter or property, optionally followed by sentences giving more
+ detailed explanations. Start with a capital, end with a full stop.
+
+ The description is also the place to define default values for optional
+ properties. Use the wording "Defaults to X [if unspecified]."
+
+ Some descriptions start with the word "Optional" to explicitly mark optional
+ properties and parameters. This is redundant. Instead, use the ``required``
+ property to mark those that are required.
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..b53982b8
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,9 @@
+[ tool.giles ]
+
+ [ tool.giles.circleci_artifacts.docs ]
+ url = "gen/index.html"
+ message = "Click details to preview the HTML documentation."
+
+ [ tool.giles.circleci_artifacts.swagger ]
+ url = "client-server/index.html"
+ message = "Click to preview the swagger build."
diff --git a/scripts/README.md b/scripts/README.md
deleted file mode 100644
index 5178d5b7..00000000
--- a/scripts/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-Generating the HTML for the specification
-=========================================
-
-Requirements:
- - docutils (for converting RST to HTML)
- - Jinja2 (for templating)
- - PyYAML (for reading YAML files)
-
-Nix[2] users can enter an environment with the appropriate tools and
-dependencies available by invoking `nix-shell contrib/shell.nix` in this
-directory.
-
-To generate the complete specification along with supporting documentation, run:
- python gendoc.py
-
-The output of this will be inside the "scripts/gen" folder.
-
-Matrix.org only ("gen" folder has matrix.org tweaked pages):
- ./matrix-org-gendoc.sh /path/to/matrix.org/includes/nav.html
-
-
-Generating the Swagger documentation
-====================================
-Swagger[1] is a framework for representing RESTful APIs. We use it to generate
-interactive documentation for our APIs.
-
-Swagger UI reads a JSON description of the API. To generate this file from the
-YAML files in the `api` folder, run:
- ./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 `./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:
- * download the latest release from https://github.com/swagger-api/swagger-ui
- * copy the contents of the 'dist' directory alongside `api-docs.json`
- * View the UI via your browser at http://\?url=api-docs.json
-
-[1] http://swagger.io/
-[2] https://nixos.org/nix/
diff --git a/scripts/add-matrix-org-stylings.pl b/scripts/add-matrix-org-stylings.pl
deleted file mode 100755
index af6ac8de..00000000
--- a/scripts/add-matrix-org-stylings.pl
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-
-use File::Slurp qw/read_file/;
-
-if (scalar(@ARGV) < 1) {
- die "Usage: $0 include_dir file_to_replace...";
-}
-
-my $include_dir = $ARGV[0];
-if (! -d $include_dir) {
- die "'$include_dir' is not a directory";
-}
-
-my $header = read_file("${include_dir}/head.html");
-my $nav = read_file("${include_dir}/nav.html");
-my $footer = read_file("${include_dir}/footer.html");
-
-$header .= "\n";
-
-$nav = <
-
- $nav
-
-
-
-EOT
-
-$footer = <
-
-
-
-
-
- $footer
-
-
-EOT
-
-my $oldargv;
-while(<>) {
- if (!$oldargv || $ARGV ne $oldargv) {
- # new file: open output file
- unlink($ARGV);
- open(ARGVOUT, ">", $ARGV);
- select(ARGVOUT);
- $oldargv = $ARGV;
- }
-
- s//$&$header/;
-
- if (//) {
- my $match = $&;
- my $classes = "blog et_fixed_nav et_cover_background et_right_sidebar";
- if ($match =~ / class=/) {
- $match =~ s/ class="([^"]*)"/ class="$1 $classes"/;
- } else {
- $match =~ s/>/ class=\"$classes\">/;
- }
-
- s//$match$nav/;
- }
-
- s##$footer$
-
- print;
-}
diff --git a/scripts/continuserv/README b/scripts/continuserv/README
deleted file mode 100644
index 8ce37850..00000000
--- a/scripts/continuserv/README
+++ /dev/null
@@ -1,6 +0,0 @@
-continuserv proactively re-generates the spec on filesystem changes, and serves it over HTTP.
-
-To run it, you must install the `go` tool. You will also need to install fsnotify by running:
- `go get gopkg.in/fsnotify.v1`
-You can then run continuserv by running:
- `go run main.go`
diff --git a/scripts/continuserv/README.md b/scripts/continuserv/README.md
new file mode 100644
index 00000000..40321bb6
--- /dev/null
+++ b/scripts/continuserv/README.md
@@ -0,0 +1,3 @@
+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
index f698c5b3..24ed7ecb 100644
--- a/scripts/continuserv/index.html
+++ b/scripts/continuserv/index.html
@@ -3,7 +3,7 @@
window.onload = function() {
var url = new URL(window.location);
url.pathname += "api-docs.json";
- var newLoc = "http://matrix.org/docs/api/client-server/?url=" + encodeURIComponent(url);
+ var newLoc = "http://petstore.swagger.io/?url=" + encodeURIComponent(url);
document.getElementById("apidocs").href = newLoc;
};
diff --git a/scripts/continuserv/main.go b/scripts/continuserv/main.go
index b489e06a..2ef6fed9 100644
--- a/scripts/continuserv/main.go
+++ b/scripts/continuserv/main.go
@@ -19,7 +19,7 @@ import (
"sync/atomic"
"time"
- fsnotify "gopkg.in/fsnotify.v1"
+ fsnotify "gopkg.in/fsnotify/fsnotify.v1"
)
var (
@@ -52,7 +52,7 @@ func main() {
walker := makeWalker(dir, w)
paths := []string{"api", "changelogs", "event-schemas", "scripts",
- "specification", "templating"}
+ "specification"}
for _, p := range paths {
filepath.Walk(path.Join(dir, p), walker)
@@ -98,6 +98,9 @@ func makeWalker(base string, w *fsnotify.Watcher) filepath.WalkFunc {
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" ||
@@ -120,8 +123,19 @@ func filter(e fsnotify.Event) bool {
return false
}
- // Avoid some temp files that vim writes
- if strings.HasSuffix(e.Name, "~") || strings.HasSuffix(e.Name, ".swp") || strings.HasPrefix(e.Name, ".") {
+ _, 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
}
@@ -147,7 +161,7 @@ func serve(w http.ResponseWriter, req *http.Request) {
if file[0] == '/' {
file = file[1:]
}
- b, ok = m.bytes[file]
+ b, ok = m.bytes[filepath.FromSlash(file)] // de-normalize slashes
if ok && file == "api-docs.json" {
w.Header().Set("Access-Control-Allow-Origin", "*")
@@ -175,7 +189,7 @@ func generate(dir string) (map[string][]byte, error) {
// cheekily dump the swagger docs into the gen directory so that it is
// easy to serve
- cmd = exec.Command("python", "dump-swagger.py", "gen/api-docs.json")
+ 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 {
diff --git a/scripts/css/blockquote.css b/scripts/css/blockquote.css
index 151d3bce..05fa73bc 100644
--- a/scripts/css/blockquote.css
+++ b/scripts/css/blockquote.css
@@ -1,5 +1,4 @@
blockquote {
margin: 20px 0 30px;
- border-left: 5px solid;
padding-left: 20px;
}
diff --git a/scripts/dump-swagger.py b/scripts/dump-swagger.py
index 3b40bdb5..e02c554e 100755
--- a/scripts/dump-swagger.py
+++ b/scripts/dump-swagger.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
# dump-swagger reads all of the swagger API docs used in spec generation and
# outputs a JSON file which merges them all, for use as input to a swagger UI
@@ -19,46 +19,50 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
+import argparse
import errno
import json
import logging
import os.path
import re
-import shutil
import sys
import yaml
+
scripts_dir = os.path.dirname(os.path.abspath(__file__))
-templating_dir = os.path.join(os.path.dirname(scripts_dir), "templating")
+templating_dir = os.path.join(scripts_dir, "templating")
api_dir = os.path.join(os.path.dirname(scripts_dir), "api")
sys.path.insert(0, templating_dir)
-from matrix_templates.units import resolve_references, MatrixUnits
+from matrix_templates import units
-if len(sys.argv) > 3:
- sys.stderr.write("usage: %s [output_file] [client_release_label]\n" % (sys.argv[0],))
- sys.exit(1)
+parser = argparse.ArgumentParser(
+ "dump-swagger.py - assemble the Swagger specs into a single JSON file"
+)
+parser.add_argument(
+ "--client_release", "-c", metavar="LABEL",
+ default="unstable",
+ help="""The client-server release version to gneerate for. Default:
+ %(default)s""",
+)
+parser.add_argument(
+ "-o", "--output",
+ default=os.path.join(scripts_dir, "swagger", "api-docs.json"),
+ help="File to write the output to. Default: %(default)s"
+)
+args = parser.parse_args()
-if len(sys.argv) > 1:
- output_file = os.path.abspath(sys.argv[1])
-else:
- output_file = os.path.join(scripts_dir, "swagger", "api-docs.json")
-
-release_label = sys.argv[2] if len(sys.argv) > 2 else "unstable"
+output_file = os.path.abspath(args.output)
+release_label = args.client_release
major_version = release_label
-match = re.match("^(r\d)+(\.\d+)*$", major_version)
+match = re.match("^(r\d+)(\.\d+)*$", major_version)
if match:
major_version = match.group(1)
-
logging.basicConfig()
-os.chdir(templating_dir)
-apis = MatrixUnits().load_swagger_apis()
-
output = {
"basePath": "/",
"consumes": ["application/json"],
@@ -74,23 +78,32 @@ output = {
"swagger": "2.0",
}
-with open(os.path.join(api_dir, 'client-server', 'definitions',
+cs_api_dir = os.path.join(api_dir, 'client-server')
+with open(os.path.join(cs_api_dir, 'definitions',
'security.yaml')) as f:
output['securityDefinitions'] = yaml.load(f)
-for file, contents in apis.items():
- basePath = contents['basePath']
- for path, methods in contents["paths"].items():
- path = (basePath + path).replace('%CLIENT_MAJOR_VERSION%',
- major_version)
- for method, spec in methods.items():
- if "tags" in spec.keys():
- if path not in output["paths"]:
- output["paths"][path] = {}
- output["paths"][path][method] = spec
+for filename in os.listdir(cs_api_dir):
+ if not filename.endswith(".yaml"):
+ continue
+ filepath = os.path.join(cs_api_dir, filename)
+ print("Reading swagger API: %s" % filepath)
+ with open(filepath, "r") as f:
+ api = yaml.load(f.read())
+ api = units.resolve_references(filepath, api)
-print "Generating %s" % output_file
+ basePath = api['basePath']
+ for path, methods in api["paths"].items():
+ path = (basePath + path).replace('%CLIENT_MAJOR_VERSION%',
+ major_version)
+ for method, spec in methods.items():
+ if "tags" in spec.keys():
+ if path not in output["paths"]:
+ output["paths"][path] = {}
+ output["paths"][path][method] = spec
+
+print("Generating %s" % output_file)
try:
os.makedirs(os.path.dirname(output_file))
diff --git a/scripts/gendoc.py b/scripts/gendoc.py
index 2d06d7c4..16c40af5 100755
--- a/scripts/gendoc.py
+++ b/scripts/gendoc.py
@@ -27,11 +27,11 @@ import subprocess
import sys
import yaml
-os.chdir(os.path.dirname(os.path.abspath(__file__)))
-
-stylesheets = {
- "stylesheet_path": glob.glob("css/*.css"),
-}
+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
@@ -54,20 +54,14 @@ Example:
"""
def load_with_adjusted_titles(filename, file_stream, title_level, title_styles):
rst_lines = []
- title_chars = "".join(title_styles)
- title_regex = re.compile("^[" + re.escape(title_chars) + "]{3,}$")
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, 1):
- # ignore anything which isn't a title (e.g. '===============')
- if not title_regex.match(line):
- rst_lines.append(line)
- prev_non_title_line = line
- continue
- # The title underline must match at a minimum the length of the title
- if len(prev_non_title_line) > len(line):
+ 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
@@ -131,9 +125,34 @@ def load_with_adjusted_titles(filename, file_stream, title_level, title_styles):
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, basestring):
+ if isinstance(file_info, str):
log("%s %s" % (">" * (1 + title_level), file_info))
with open(os.path.join(spec_dir, file_info), "r") as f:
rst = None
@@ -173,10 +192,10 @@ def build_spec(target, out_filename):
file_info=file_info,
title_level=0,
title_styles=target["title_styles"],
- spec_dir="../specification/",
+ spec_dir=spec_dir,
adjust_titles=True
)
- outfile.write(section)
+ outfile.write(section.encode('UTF-8'))
"""
@@ -242,7 +261,7 @@ def fix_relative_titles(target, filename, out_filename):
-def rst2html(i, o):
+def rst2html(i, o, stylesheets):
log("rst2html %s -> %s" % (i, o))
with open(i, "r") as in_file:
with open(o, "w") as out_file:
@@ -252,28 +271,31 @@ def rst2html(i, o):
reader_name="standalone",
parser_name="restructuredtext",
writer_name="html",
- settings_overrides=stylesheets
+ settings_overrides={
+ "stylesheet_path": stylesheets,
+ },
)
def addAnchors(path):
log("add anchors %s" % path)
- with open(path, "r") as f:
+ with open(path, "rb") as f:
lines = f.readlines()
- replacement = replacement = r'
\n\1'
- with open(path, "w") as f:
+ replacement = r'
\n\1'
+ with open(path, "wb") as f:
for line in lines:
+ line = line.decode("UTF-8")
line = re.sub(r'()', replacement, line.rstrip())
- line = re.sub(r'(
", title=pv["title"], desc=pv["desc"],
+ )
+ tables.insert(0, TypeTable(None, rows=[first_table_row]))
+
+ logger.debug("response: %r" % tables)
+
+ return tables
+
+def get_example_for_schema(schema):
+ """Returns a python object representing a suitable example for this object"""
+ schema = inherit_parents(schema)
+
+ if 'example' in schema:
+ example = schema['example']
+ return example
+
+ proptype = schema['type']
+
+ if proptype == 'object':
+ if 'properties' not in schema:
+ raise Exception('"object" property has neither properties nor example')
+ res = OrderedDict()
+ for prop_name, prop in schema['properties'].items():
+ logger.debug("Parsing property %r" % prop_name)
+ prop_example = get_example_for_schema(prop)
+ res[prop_name] = prop_example
+ return res
+
+ if proptype == 'array':
+ if 'items' not in schema:
+ raise Exception('"array" property has neither items nor example')
+ items = schema['items']
+ if isinstance(items, list):
+ return [get_example_for_schema(i) for i in items]
+ return [get_example_for_schema(items)]
+
+ if proptype == 'integer':
+ return 0
+
+ if proptype == 'string':
+ return proptype
+
+ raise Exception("Don't know to make an example %s" % proptype)
+
+def get_example_for_param(param):
+ """Returns a stringified example for a parameter"""
+ if 'x-example' in param:
+ return param['x-example']
+ schema = param.get('schema')
+ if not schema:
+ return None
+
+ exampleobj = None
+ if 'example' in schema:
+ exampleobj = schema['example']
+
+ if exampleobj is None:
+ exampleobj = get_example_for_schema(schema)
+
+ return json.dumps(exampleobj, indent=2)
+
+def get_example_for_response(response):
+ """Returns a stringified example for a response"""
+ exampleobj = None
+ if 'examples' in response:
+ exampleobj = response["examples"].get("application/json")
+
+ if exampleobj is None:
+ schema = response.get('schema')
+ if schema:
+ if schema['type'] == 'file':
+ # no example for 'file' responses
+ return None
+ exampleobj = get_example_for_schema(schema)
+
+ if exampleobj is None:
+ return None
+
+ return json.dumps(exampleobj, indent=2)
+
+class MatrixUnits(Units):
+ def _load_swagger_meta(self, api, group_name):
+ endpoints = []
+ base_path = api.get("basePath", "")
+
+ for path in api["paths"]:
+ for method in api["paths"][path]:
+ logger.info(" ------- Endpoint: %s %s ------- " % (method, path))
+
+ try:
+ endpoint = self._handle_endpoint(
+ api["paths"][path][method], method,
+ base_path.rstrip("/") + path)
+
+ endpoints.append(endpoint)
+ except Exception as e:
+ logger.error("Error handling endpoint %s %s: %s",
+ method, path, e)
+ raise
+ return {
+ "base": api.get("basePath").rstrip("/"),
+ "group": group_name,
+ "endpoints": endpoints,
+ }
+
+ def _handle_endpoint(self, endpoint_swagger, method, path):
+ endpoint = {
+ "title": endpoint_swagger.get("summary", ""),
+ "deprecated": endpoint_swagger.get("deprecated", False),
+ "desc": endpoint_swagger.get("description",
+ endpoint_swagger.get("summary", "")),
+ "method": method.upper(),
+ "path": path.strip(),
+ "requires_auth": "security" in endpoint_swagger,
+ "rate_limited": 429 in endpoint_swagger.get("responses", {}),
+ "req_param_by_loc": {},
+ "req_body_tables": [],
+ "res_headers": None,
+ "res_tables": [],
+ "responses": [],
+ "example": {
+ "req": "",
+ }
+ }
+ path_template = path
+ example_query_params = []
+ example_body = ""
+ for param in endpoint_swagger.get("parameters", []):
+ # even body params should have names, otherwise the active docs don't work.
+ param_name = param["name"]
+
+ try:
+ param_loc = param["in"]
+
+ if param_loc == "body":
+ self._handle_body_param(param, endpoint)
+ example_body = get_example_for_param(param)
+ continue
+
+ # description
+ desc = param.get("description", "")
+ if param.get("required"):
+ desc = "**Required.** " + desc
+
+ # assign value expected for this param
+ val_type = param.get("type") # integer/string
+
+ if val_type == "array":
+ items = param.get("items")
+ if items:
+ if isinstance(items, list):
+ types = ", ".join(i.get("type") for i in items)
+ val_type = "[%s]" % (types,)
+ else:
+ val_type = "[%s]" % items.get("type")
+
+ if param.get("enum"):
+ val_type = "enum"
+ desc += (
+ " One of: %s" % json.dumps(param.get("enum"))
+ )
+
+ endpoint["req_param_by_loc"].setdefault(param_loc, []).append(
+ TypeTableRow(key=param_name, title=val_type, desc=desc),
+ )
+
+ example = get_example_for_param(param)
+ if example is None:
+ continue
+
+ if param_loc == "path":
+ path_template = path_template.replace(
+ "{%s}" % param_name, quote(example)
+ )
+ elif param_loc == "query":
+ if type(example) == list:
+ for value in example:
+ example_query_params.append((param_name, value))
+ else:
+ example_query_params.append((param_name, example))
+
+ except Exception as e:
+ raise Exception("Error handling parameter %s" % param_name, e)
+ # endfor[param]
+ good_response = None
+ for code in sorted(endpoint_swagger.get("responses", {}).keys()):
+ res = endpoint_swagger["responses"][code]
+ if not good_response and code == 200:
+ good_response = res
+ description = res.get("description", "")
+ example = get_example_for_response(res)
+ endpoint["responses"].append({
+ "code": code,
+ "description": description,
+ "example": example,
+ })
+
+ # add response params if this API has any.
+ if good_response:
+ if "schema" in good_response:
+ endpoint["res_tables"] = get_tables_for_response(
+ good_response["schema"]
+ )
+ if "headers" in good_response:
+ headers = TypeTable()
+ for (header_name, header) in good_response["headers"].items():
+ headers.add_row(
+ TypeTableRow(key=header_name, title=header["type"],
+ desc=header["description"]),
+ )
+ endpoint["res_headers"] = headers
+ query_string = "" if len(
+ example_query_params) == 0 else "?" + urlencode(
+ example_query_params)
+ if example_body:
+ endpoint["example"][
+ "req"] = "%s %s%s HTTP/1.1\nContent-Type: application/json\n\n%s" % (
+ method.upper(), path_template, query_string, example_body
+ )
+ else:
+ endpoint["example"]["req"] = "%s %s%s HTTP/1.1\n\n" % (
+ method.upper(), path_template, query_string
+ )
+ return endpoint
+
+ def _handle_body_param(self, param, endpoint_data):
+ """Update endpoint_data object with the details of the body param
+ :param string filepath path to the yaml
+ :param dict param the parameter data from the yaml
+ :param dict endpoint_data dictionary of endpoint data to be updated
+ """
+ try:
+ schema = inherit_parents(param["schema"])
+ if schema["type"] != "object":
+ logger.warn(
+ "Unsupported body type %s for %s %s", schema["type"],
+ endpoint_data["method"], endpoint_data["path"]
+ )
+ return
+
+ req_body_tables = get_tables_for_schema(schema)
+
+ if req_body_tables == []:
+ # no fields defined for the body.
+ return
+
+ # put the top-level parameters into 'req_param_by_loc', and the others
+ # into 'req_body_tables'
+ body_params = endpoint_data['req_param_by_loc'].setdefault("JSON body",[])
+ body_params.extend(req_body_tables[0].rows)
+
+ body_tables = req_body_tables[1:]
+ endpoint_data['req_body_tables'].extend(body_tables)
+
+ except Exception as e:
+ e2 = Exception(
+ "Error decoding body of API endpoint %s %s: %s" %
+ (endpoint_data["method"], endpoint_data["path"], e)
+ )
+ raise e2.with_traceback(sys.exc_info()[2])
+
+
+ def load_swagger_apis(self):
+ apis = {}
+ for path, suffix in HTTP_APIS.items():
+ for filename in os.listdir(path):
+ if not filename.endswith(".yaml"):
+ continue
+ filepath = os.path.join(path, filename)
+ logger.info("Reading swagger API: %s" % filepath)
+ with open(filepath, "r") as f:
+ # strip .yaml
+ group_name = filename[:-5].replace("-", "_")
+ group_name = "%s_%s" % (group_name, suffix)
+ api = yaml.load(f.read(), OrderedLoader)
+ api = resolve_references(filepath, api)
+ api["__meta"] = self._load_swagger_meta(
+ api, group_name
+ )
+ apis[group_name] = api
+ return apis
+
+
+ def load_swagger_definitions(self):
+ defs = {}
+ for path, prefix in SWAGGER_DEFINITIONS.items():
+ self._load_swagger_definitions_in_dir(defs, path, prefix)
+ return defs
+
+ def _load_swagger_definitions_in_dir(self, defs, path, prefix, recurse=True):
+ if not os.path.exists(path):
+ return defs
+ for filename in os.listdir(path):
+ filepath = os.path.join(path, filename)
+ if os.path.isdir(filepath) and recurse:
+ safe_name = re.sub(r"[^a-zA-Z0-9_]", "_", filename)
+ dir_prefix = "_".join([prefix, safe_name])
+ # We don't recurse because we have to stop at some point
+ self._load_swagger_definitions_in_dir(
+ defs, filepath, dir_prefix, recurse=False)
+ if not filename.endswith(".yaml"):
+ continue
+ filepath = os.path.join(path, filename)
+ logger.info("Reading swagger definition: %s" % filepath)
+ with open(filepath, "r") as f:
+ # strip .yaml
+ group_name = re.sub(r"[^a-zA-Z0-9_]", "_", filename[:-5])
+ group_name = "%s_%s" % (prefix, group_name)
+ definition = yaml.load(f.read(), OrderedLoader)
+ definition = resolve_references(filepath, definition)
+ if 'type' not in definition:
+ continue
+ try:
+ example = get_example_for_schema(definition)
+ except:
+ example = None
+ pass # do nothing - we don't care
+ if 'title' not in definition:
+ definition['title'] = "NO_TITLE"
+ definition['tables'] = get_tables_for_schema(definition)
+ defs[group_name] = {
+ "definition": definition,
+ "examples": [example] if example is not None else [],
+ }
+ return defs
+
+ def load_common_event_fields(self):
+ """Parse the core event schema files
+
+ Returns:
+ dict: with the following properties:
+ "title": Event title (from the 'title' field of the schema)
+ "desc": desc
+ "tables": list[TypeTable]
+ """
+ path = CORE_EVENT_SCHEMA
+ event_types = {}
+
+ for filename in os.listdir(path):
+ if not filename.endswith(".yaml"):
+ continue
+
+ filepath = os.path.join(path, filename)
+
+ event_type = filename[:-5] # strip the ".yaml"
+ logger.info("Reading event schema: %s" % filepath)
+
+ with open(filepath) as f:
+ event_schema = yaml.load(f, OrderedLoader)
+
+ schema_info = process_data_type(
+ event_schema,
+ enforce_title=True,
+ )
+ event_types[event_type] = schema_info
+ return event_types
+
+ def load_apis(self, substitutions):
+ cs_ver = substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable")
+ fed_ver = substitutions.get("%SERVER_RELEASE_LABEL%", "unstable")
+
+ # we abuse the typetable to return this info to the templates
+ return TypeTable(rows=[
+ TypeTableRow(
+ "`Client-Server API `_",
+ cs_ver,
+ "Interaction between clients and servers",
+ ), TypeTableRow(
+ "`Server-Server API `_",
+ fed_ver,
+ "Federation between servers",
+ ), TypeTableRow(
+ "`Application Service API `_",
+ "unstable",
+ "Privileged server plugins",
+ ), TypeTableRow(
+ "`Identity Service API `_",
+ "unstable",
+ "Mapping of third party IDs to Matrix IDs",
+ ), TypeTableRow(
+ "`Push Gateway API `_",
+ "unstable",
+ "Push notifications for Matrix events",
+ ),
+ ])
+
+ def load_event_examples(self):
+ path = EVENT_EXAMPLES
+ examples = {}
+ for filename in os.listdir(path):
+ if not filename.startswith("m."):
+ continue
+
+ event_name = filename.split("#")[0]
+ filepath = os.path.join(path, filename)
+ logger.info("Reading event example: %s" % filepath)
+ try:
+ with open(filepath, "r") as f:
+ example = json.load(f)
+ examples[filename] = examples.get(filename, [])
+ examples[filename].append(example)
+ if filename != event_name:
+ examples[event_name] = examples.get(event_name, [])
+ examples[event_name].append(example)
+ except Exception as e:
+ e2 = Exception("Error reading event example "+filepath+": "+
+ str(e))
+ # throw the new exception with the old stack trace, so that
+ # we don't lose information about where the error occurred.
+ raise e2.with_traceback(sys.exc_info()[2])
+
+ return examples
+
+ def load_event_schemas(self):
+ path = EVENT_SCHEMA
+ schemata = {}
+
+ for filename in os.listdir(path):
+ if not filename.startswith("m."):
+ continue
+ filepath = os.path.join(path, filename)
+ try:
+ schemata[filename] = self.read_event_schema(filepath)
+ except Exception as e:
+ e2 = Exception("Error reading event schema "+filepath+": "+
+ str(e))
+ # throw the new exception with the old stack trace, so that
+ # we don't lose information about where the error occurred.
+ raise e2.with_traceback(sys.exc_info()[2])
+
+ return schemata
+
+ def read_event_schema(self, filepath):
+ logger.info("Reading %s" % filepath)
+
+ with open(filepath, "r") as f:
+ json_schema = yaml.load(f, OrderedLoader)
+
+ schema = {
+ # one of "Message Event" or "State Event"
+ "typeof": "",
+ "typeof_info": "",
+
+ # event type, eg "m.room.member". Note *not* the type of the
+ # event object (which should always be 'object').
+ "type": None,
+ "title": None,
+ "desc": None,
+ "msgtype": None,
+ "content_fields": [
+ #
+ ]
+ }
+
+ # before we resolve the references, see if the first reference is to
+ # the message event or state event schemas, and add typeof info if so.
+ base_defs = {
+ ROOM_EVENT: "Message Event",
+ STATE_EVENT: "State Event"
+ }
+ if type(json_schema.get("allOf")) == list:
+ firstRef = json_schema["allOf"][0]["$ref"]
+ if firstRef in base_defs:
+ schema["typeof"] = base_defs[firstRef]
+
+ json_schema = resolve_references(filepath, json_schema)
+
+ # add type
+ schema["type"] = Units.prop(
+ json_schema, "properties/type/enum"
+ )[0]
+
+ # add summary and desc
+ schema["title"] = json_schema.get("title")
+ schema["desc"] = json_schema.get("description", "")
+
+ # walk the object for field info
+ schema["content_fields"] = get_tables_for_schema(
+ Units.prop(json_schema, "properties/content")
+ )
+
+ # grab msgtype if it is the right kind of event
+ msgtype = Units.prop(
+ json_schema, "properties/content/properties/msgtype/enum"
+ )
+ if msgtype:
+ schema["msgtype"] = msgtype[0] # enum prop
+
+ # link to msgtypes for m.room.message
+ if schema["type"] == "m.room.message" and not msgtype:
+ schema["desc"] += (
+ " For more information on ``msgtypes``, see "+
+ "`m.room.message msgtypes`_."
+ )
+
+ # Assign state key info if it has some
+ if schema["typeof"] == "State Event":
+ skey_desc = Units.prop(
+ json_schema, "properties/state_key/description"
+ )
+ if not skey_desc:
+ raise Exception("Missing description for state_key")
+ schema["typeof_info"] = "``state_key``: %s" % skey_desc
+
+ return schema
+
+ def load_changelogs(self):
+ changelogs = {}
+
+ for f in os.listdir(CHANGELOG_DIR):
+ if not f.endswith(".rst"):
+ continue
+ path = os.path.join(CHANGELOG_DIR, f)
+ name = f[:-4]
+
+ # If there's a directory with the same name, we'll try to generate
+ # a towncrier changelog and prepend it to the general changelog.
+ tc_path = os.path.join(CHANGELOG_DIR, name)
+ tc_lines = []
+ if os.path.isdir(tc_path):
+ logger.info("Generating towncrier changelog for: %s" % name)
+ p = subprocess.Popen(
+ ['towncrier', '--version', 'Unreleased Changes', '--name', name, '--draft'],
+ cwd=tc_path,
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ )
+ stdout, stderr = p.communicate()
+ if p.returncode != 0:
+ # Something broke - dump as much information as we can
+ logger.error("Towncrier exited with code %s" % p.returncode)
+ logger.error(stdout.decode('UTF-8'))
+ logger.error(stderr.decode('UTF-8'))
+ raw_log = ""
+ else:
+ raw_log = stdout.decode('UTF-8')
+
+ # This is a bit of a hack, but it does mean that the log at least gets *something*
+ # to tell us it broke
+ if not raw_log.startswith("Unreleased Changes"):
+ logger.error("Towncrier appears to have failed to generate a changelog")
+ logger.error(raw_log)
+ raw_log = ""
+ tc_lines = raw_log.splitlines()
+
+ title_part = None
+ changelog_lines = []
+ with open(path, "r") as f:
+ lines = f.readlines()
+ prev_line = None
+ for line in (tc_lines + lines):
+ if prev_line is None:
+ prev_line = line
+ continue
+ if not title_part:
+ # find the title underline (at least 3 =)
+ if re.match("^[=]{3,}$", line.strip()):
+ title_part = prev_line
+ continue
+ prev_line = line
+ else: # have title, get body (stop on next title or EOF)
+ if re.match("^[=]{3,}$", line.strip()):
+ # we added the title in the previous iteration, pop it
+ # then bail out.
+ changelog_lines.pop()
+ break
+ # Don't generate subheadings (we'll keep the title though)
+ if re.match("^[-]{3,}$", line.strip()):
+ continue
+ changelog_lines.append(" " + line + '\n')
+ changelogs[name] = "".join(changelog_lines)
+
+ return changelogs
+
+
+ def load_spec_targets(self):
+ with open(TARGETS, "r") as f:
+ return yaml.load(f.read())
+
+
+ def load_git_version(self):
+ null = open(os.devnull, 'w')
+ cwd = os.path.dirname(os.path.abspath(__file__))
+ try:
+ git_branch = subprocess.check_output(
+ ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
+ stderr=null,
+ cwd=cwd,
+ ).strip().decode('UTF-8')
+ except subprocess.CalledProcessError:
+ git_branch = ""
+ try:
+ git_tag = subprocess.check_output(
+ ['git', 'describe', '--exact-match'],
+ stderr=null,
+ cwd=cwd,
+ ).strip().decode('UTF-8')
+ git_tag = "tag=" + git_tag
+ except subprocess.CalledProcessError:
+ git_tag = ""
+ try:
+ git_commit = subprocess.check_output(
+ ['git', 'rev-parse', '--short', 'HEAD'],
+ stderr=null,
+ cwd=cwd,
+ ).strip().decode('UTF-8')
+ except subprocess.CalledProcessError:
+ git_commit = ""
+ try:
+ dirty_string = "-this_is_a_dirty_checkout"
+ is_dirty = subprocess.check_output(
+ ['git', 'describe', '--dirty=' + dirty_string, "--all"],
+ stderr=null,
+ cwd=cwd,
+ ).strip().decode('UTF-8').endswith(dirty_string)
+ git_dirty = "dirty" if is_dirty else ""
+ except subprocess.CalledProcessError:
+ git_dirty = ""
+
+ git_version = "Unknown"
+ if git_branch or git_tag or git_commit or git_dirty:
+ git_version = ",".join(
+ s for s in
+ (git_branch, git_tag, git_commit, git_dirty,)
+ if s
+ ).encode("ascii").decode('ascii')
+ return {
+ "string": git_version,
+ "revision": git_commit
+ }
diff --git a/scripts/test-and-build.sh b/scripts/test-and-build.sh
new file mode 100755
index 00000000..710b03dd
--- /dev/null
+++ b/scripts/test-and-build.sh
@@ -0,0 +1,34 @@
+#! /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 Jenkins, this is then used to deploy to matrix.org).
+./scripts/generate-matrix-org-assets
diff --git a/specification/appendices/base64.rst b/specification/appendices/base64.rst
new file mode 100644
index 00000000..d046e0fc
--- /dev/null
+++ b/specification/appendices/base64.rst
@@ -0,0 +1,57 @@
+.. 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 whereever 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
new file mode 100644
index 00000000..fc89f031
--- /dev/null
+++ b/specification/appendices/identifier_grammar.rst
@@ -0,0 +1,285 @@
+.. Copyright 2016 Openmarket Ltd.
+.. Copyright 2017 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
+------------------
+
+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. The complete grammar is::
+
+ server_name = host [ ":" port]
+ port = *DIGIT
+
+where ``host`` is as defined by `RFC3986, section 3.2.2
+`_.
+
+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)
+
+
+Room Versions
+~~~~~~~~~~~~~
+
+Room versions are used to change properties of rooms that may not be compatible
+with other servers. For example, changing the rules for event authorization would
+cause older servers to potentially end up in a split-brain situation due to them
+not understanding the new rules.
+
+A room version is defined as a string of characters which MUST NOT exceed 32
+codepoints in length. Room versions MUST NOT be empty and SHOULD contain only
+the characters ``a-z``, ``0-9``, ``.``, and ``-``.
+
+Room versions are not intended to be parsed and should be treated as opaque
+identifiers. Room versions consisting only of the characters ``0-9`` and ``.``
+are reserved for future versions of the Matrix protocol.
+
+The complete grammar for a legal room version is::
+
+ room_version = 1*room_version_char
+ room_version_char = DIGIT
+ / %x61-7A ; a-z
+ / "-" / "."
+
+Examples of valid room versions are:
+
+* ``1`` (would be reserved by the Matrix protocol)
+* ``1.2`` (would be reserved by the Matrix protocol)
+* ``1.2-beta``
+* ``com.example.version``
+
+
+Common Identifier Format
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+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::
+
+ &localpart:domain
+
+where ``&`` represents a 'sigil' character; ``domain`` is the `server name`_ of
+the homeserver which allocated the identifier, and ``localpart`` is an
+identifier allocated by that homeserver.
+
+The sigil characters are as follows:
+
+* ``@``: User ID
+* ``!``: Room ID
+* ``$``: Event ID
+* ``+``: Group ID
+* ``#``: Room alias
+
+The precise grammar defining the allowable format of an identifier depends on
+the type of identifier.
+
+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-7F ; 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 implemention 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. An event ID has the format::
+
+ $opaque_id:domain
+
+The ``domain`` of a room/event 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.
+
+.. 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
diff --git a/specification/appendices/signing_json.rst b/specification/appendices/signing_json.rst
index 5536af5e..795d6669 100644
--- a/specification/appendices/signing_json.rst
+++ b/specification/appendices/signing_json.rst
@@ -86,13 +86,162 @@ insignificant whitespace, fractions, exponents and redundant character escapes
/ %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 base64 with the padding
-stripped. The resulting base64 signature is added to an object under 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.
diff --git a/specification/appendices/threat_model.rst b/specification/appendices/threat_model.rst
index 0dea62e0..9ad5fef8 100644
--- a/specification/appendices/threat_model.rst
+++ b/specification/appendices/threat_model.rst
@@ -65,7 +65,7 @@ making the chatroom unusable.
Threat: Banning users without necessary authorisation
+++++++++++++++++++++++++++++++++++++++++++++++++++++
-An attacker could attempt to ban a user from a chatroom with the necessary
+An attacker could attempt to ban a user from a chatroom without the necessary
authorisation.
Spoofing
@@ -134,7 +134,7 @@ 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
new file mode 100644
index 00000000..84860740
--- /dev/null
+++ b/specification/appendices/threepids.rst
@@ -0,0 +1,48 @@
+.. 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
index b4950eac..51280341 100644
--- a/specification/application_service_api.rst
+++ b/specification/application_service_api.rst
@@ -1,4 +1,5 @@
.. 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.
@@ -39,7 +40,7 @@ This version of the specification is generated from
Application Services
--------------------
Application services are passive and can only observe events from a given
-homeserver. They can inject events into rooms they are participating in.
+homeserver (HS). 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
@@ -68,8 +69,13 @@ Registration
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. An application
-service can also state whether they should be the only ones who
+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,
@@ -83,34 +89,83 @@ regular expressions and look like:
users:
- exclusive: true
- regex: @irc.freenode.net_.*
+ regex: "@_irc.freenode.net_.*"
+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. An example HS configuration required to pass
-traffic to the AS is:
+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:
- url:
- as_token:
- hs_token:
- sender_localpart:
+ 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:domain.com
namespaces:
- users: # Namespaces of users which should be delegated to the AS
- - exclusive:
- regex:
- - ...
- aliases: [] # Namespaces of room aliases which should be delegated to the AS
- rooms: [] # Namespaces of room ids which should be delegated to the AS
+ 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -143,6 +198,8 @@ 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 AS may have already processed the events.
+{{transactions_as_http_api}}
+
Querying
++++++++
@@ -163,14 +220,30 @@ this request (e.g. to join a room alias).
the application service about information about the entity such as room ID to
room alias mappings.
+{{query_user_as_http_api}}
-HTTP APIs
-+++++++++
+{{query_room_as_http_api}}
-This contains application service APIs which are used by the homeserver. All
-application services MUST implement these APIs. These APIs are defined below.
-{{application_service_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`_
@@ -182,32 +255,40 @@ 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. It would be an annoying amount of book-keeping to maintain tokens
-for every virtual user. It would be preferable if the application service could
-use the CS API with its own ``as_token`` instead, and specify the virtual user
-they wish to be acting on behalf of. For real users, this would require
-additional permissions granting the AS permission to masquerade as a matrix user.
+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 (``access_token``)
+ - Application service token (``as_token``)
- User ID in the AS namespace to act as.
Notes:
- - This will apply on all aspects of the CS API, except for Account Management.
+ - 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. This is done on purpose to allow application services to
- reuse client SDKs.
+ 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.
- /path?access_token=$token&user_id=$userid
+An example request would be::
+
+ GET /_matrix/client/%CLIENT_MAJOR_VERSION%/account/whoami?user_id=@_irc_user:example.org
+ Authorization: Bearer YourApplicationServiceTokenHere
- Query Parameters:
- access_token: The application service token
- user_id: The desired user ID to act as.
Timestamp massaging
+++++++++++++++++++
@@ -217,17 +298,17 @@ need to be able to adjust the ``origin_server_ts`` value to do this.
Inputs:
- Application service token (``as_token``)
- - Desired timestamp
+ - Desired timestamp (in milliseconds since the unix epoch)
+
Notes:
- This will only apply when sending events.
::
- /path?access_token=$token&ts=$timestamp
+ PUT /_matrix/client/r0/rooms/!somewhere:domain.com/send/m.room.message/txnId?ts=1534535223283
+ Authorization: Bearer YourApplicationServiceTokenHere
- Query Parameters added to the send event APIs only:
- access_token: The application service token
- ts: The desired timestamp
+ Content: The event to send, as per the Client-Server API.
Server admin style permissions
++++++++++++++++++++++++++++++
@@ -250,12 +331,13 @@ including the AS token on a ``/register`` request, along with a login type of
::
- /register?access_token=$as_token
+ POST /_matrix/client/%CLIENT_MAJOR_VERSION%/register
+ Authorization: Bearer YourApplicationServiceTokenHere
Content:
{
type: "m.login.application_service",
- username: ""
+ username: "_irc_example"
}
Application services which attempt to create users or aliases *outside* of
@@ -264,78 +346,28 @@ 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``.
-ID conventions
-~~~~~~~~~~~~~~
-.. TODO-spec
- - Giving HSes the freedom to namespace still feels like the Right Thing here.
- - Exposing a public API provides the consistency which was the main complaint
- against namespacing.
- - This may have knock-on effects for the AS registration API. E.g. why don't
- we let ASes specify the *URI* regex they want?
+Using ``/sync`` and ``/events``
++++++++++++++++++++++++++++++++
-This concerns the well-defined conventions for mapping 3P network IDs to matrix
-IDs, which we expect clients to be able to do by themselves.
+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``.
-User IDs
-++++++++
-Matrix users may wish to directly contact a virtual user, e.g. to send an email.
-The URI format is a well-structured way to represent a number of different ID
-types, including:
+Application service room directories
+++++++++++++++++++++++++++++++++++++
-- MSISDNs (``tel``)
-- Email addresses (``mailto``)
-- IRC nicks (``irc`` - https://tools.ietf.org/html/draft-butcher-irc-url-04)
-- XMPP (XEP-0032)
-- SIP URIs (RFC 3261)
+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.
-As a result, virtual user IDs SHOULD relate to their URI counterpart. This
-mapping from URI to user ID can be expressed in a number of ways:
-
-- Expose a C-S API on the HS which takes URIs and responds with user IDs.
-- Munge the URI with the user ID.
-
-Exposing an API would allow HSes to internally map user IDs however they like,
-at the cost of an extra round trip (of which the response can be cached).
-Munging the URI would allow clients to apply the mapping locally, but would force
-user X on service Y to always map to the same munged user ID. Considering the
-exposed API could just be applying this munging, there is more flexibility if
-an API is exposed.
-
-::
-
- GET /_matrix/app/%CLIENT_MAJOR_VERSION%/user?uri=$url_encoded_uri
-
- Returns 200 OK:
- {
- user_id:
- }
-
-Room Aliases
-++++++++++++
-We may want to expose some 3P network rooms so Matrix users can join them directly,
-e.g. IRC rooms. We don't want to expose every 3P network room though, e.g.
-``mailto``, ``tel``. Rooms which are publicly accessible (e.g. IRC rooms) can be
-exposed as an alias by the application service. Private rooms
-(e.g. sending an email to someone) should not
-be exposed in this way, but should instead operate using normal invite/join semantics.
-Therefore, the ID conventions discussed below are only valid for public rooms which
-expose room aliases.
-
-Matrix users may wish to join XMPP rooms (e.g. using XEP-0045) or IRC rooms. In both
-cases, these rooms can be expressed as URIs. For consistency, these "room" URIs
-SHOULD be mapped in the same way as "user" URIs.
-
-::
-
- GET /_matrix/app/%CLIENT_MAJOR_VERSION%/alias?uri=$url_encoded_uri
-
- Returns 200 OK:
- {
- alias:
- }
+{{appservice_room_directory_cs_http_api}}
Event fields
-++++++++++++
+~~~~~~~~~~~~
+
+.. TODO-TravisR: Fix this section to be a general "3rd party networks" section
+
We recommend that any events that originated from a remote network should
include an ``external_url`` field in their content to provide a way for Matrix
clients to link into the 'native' client from which the event originated.
diff --git a/specification/client_server_api.rst b/specification/client_server_api.rst
index bbb6982a..71c60097 100644
--- a/specification/client_server_api.rst
+++ b/specification/client_server_api.rst
@@ -37,15 +37,13 @@ This version of the specification is generated from
For the full historical changelog, see
https://github.com/matrix-org/matrix-doc/blob/master/changelogs/client_server.rst
-If this is an unstable snapshot, any changes since the last release may be
-viewed using ``git log``.
-
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.
+- `HEAD `_: Includes all changes since the latest versioned release.
+- `r0.3.0 `_
- `r0.2.0 `_
- `r0.1.0 `_
- `r0.0.1 `_
@@ -150,6 +148,13 @@ Some requests have unique error codes:
:``M_SERVER_NOT_TRUSTED``:
The client's request used a third party server, eg. ID 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.
+
.. _sect:txn_ids:
The client-server API typically uses ``HTTP PUT`` to submit requests with a
@@ -166,17 +171,32 @@ recommended.
{{versions_cs_http_api}}
+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.
+
+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:
+
+.. 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
+
+
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. An access token is typically obtained via the `Login`_ or
-`Registration`_ processes.
-
-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.
+parameter or through an Authorization Header of ``Bearer $access_token``.
+An access token is typically obtained via the `Login`_ or `Registration`_ processes.
.. NOTE::
@@ -185,6 +205,24 @@ return with a status of 401 and the error code, ``M_MISSING_TOKEN`` or
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -194,7 +232,7 @@ 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 ether case the client
+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
@@ -444,7 +482,7 @@ Password-based
:Type:
``m.login.password``
:Description:
- The client submits a username and secret password, both sent in plain-text.
+ 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:
@@ -452,7 +490,26 @@ To use this authentication type, clients should submit an auth dict as follows:
{
"type": "m.login.password",
- "user": "",
+ "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 whould submit:
+
+.. code:: json
+
+ {
+ "type": "m.login.password",
+ "identifier": {
+ "type": "m.id.user",
+ "user": ""
+ },
"password": "",
"session": ""
}
@@ -465,8 +522,11 @@ follows:
{
"type": "m.login.password",
- "medium": "",
- "address": "",
+ "identifier": {
+ "type": "m.id.thirdparty",
+ "medium": "",
+ "address": ""
+ },
"password": "",
"session": ""
}
@@ -512,8 +572,9 @@ To use this authentication type, clients should submit an auth dict as follows:
The ``nonce`` should be a random string generated by the client for the
request. The same ``nonce`` should be used if retrying the request.
-There are many ways a client may receive a ``token``, including via an email or
-from an existing logged in device.
+A client may receive a login ``token`` via some external service, such as email
+or SMS. Note that a login token is separate from an access token, the latter
+providing general authentication to various API endpoints.
The ``txn_id`` may be used by the server to disallow other devices from using
the token, thus providing "single use" tokens while still allowing the device
@@ -696,6 +757,78 @@ handle unknown login types:
}
+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 identifer 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": ""
+ }
+
Login
~~~~~
@@ -711,7 +844,10 @@ request as follows:
{
"type": "m.login.password",
- "user": "",
+ "identifier": {
+ "type": "m.id.user",
+ "user": ""
+ },
"password": ""
}
@@ -723,8 +859,10 @@ explicitly, as follows:
{
"type": "m.login.password",
- "medium": "",
- "address": "",
+ "identifier": {
+ "medium": "",
+ "address": ""
+ },
"password": ""
}
@@ -786,6 +924,11 @@ This is independent of any information kept by any Identity Servers.
{{administrative_contact_cs_http_api}}
+Current account information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+{{whoami_cs_http_api}}
+
Pagination
----------
@@ -880,6 +1023,8 @@ Where $streamtoken is an opaque token which can be used in another query to
get the next set of results. The "start" and "end" keys can only be omitted if
the complete dataset is provided in "chunk".
+.. _`filter`:
+
Filtering
---------
@@ -949,11 +1094,11 @@ You can visualise the range of events being returned as::
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. This involves specifying a
-timeout in the request which will 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.
+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::
@@ -1260,6 +1405,7 @@ The allowable state transitions of membership are::
----------------------------------------------+ +----------------------+
/ban
+{{list_joined_rooms_cs_http_api}}
Joining rooms
+++++++++++++
@@ -1329,20 +1475,30 @@ 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 is sent to every room the user is a member of,
- to update the ``displayname`` and ``avatar_url``.
+- 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.
@@ -1381,7 +1537,7 @@ have to wait in milliseconds before they can try again.
.. References
.. _`macaroon`: http://research.google.com/pubs/pub41892.html
-.. _`devices`: ../intro.html#devices
+.. _`devices`: ../index.html#devices
.. Links through the external API docs are below
.. =============================================
@@ -1431,8 +1587,14 @@ have to wait in milliseconds before they can try again.
.. |/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
+
+.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
+.. _`3PID Types`: ../appendices.html#pid-types
diff --git a/specification/events.rst b/specification/events.rst
index 91b837c6..c5e4a288 100644
--- a/specification/events.rst
+++ b/specification/events.rst
@@ -32,8 +32,11 @@ server-server API.
Size limits
-----------
-The total size of any event MUST NOT exceed 65 KB. There are additional
-restrictions on sizes per key:
+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.
@@ -67,3 +70,4 @@ prefixed with ``m.``
{{m_room_redaction_event}}
+.. _`Canonical JSON`: ../appendices.html#canonical-json
diff --git a/specification/feature_profiles.rst b/specification/feature_profiles.rst
index 7fc9696d..c6b8ef4c 100644
--- a/specification/feature_profiles.rst
+++ b/specification/feature_profiles.rst
@@ -42,6 +42,7 @@ Summary
`Server Side Search`_ Optional Optional Optional Optional Optional
`Server Administration`_ Optional Optional Optional Optional Optional
`Event Context`_ Optional Optional Optional Optional Optional
+ `Third Party Networks`_ Optional Optional Optional Optional Optional
===================================== ========== ========== ========== ========== ==========
*Please see each module for more details on what clients need to implement.*
@@ -57,6 +58,7 @@ Summary
.. _Server Side Search: `module:search`_
.. _Server Administration: `module:admin`_
.. _Event Context: `module:event-context`_
+.. _Third Party Networks: `module:third-party-networks`_
Clients
-------
diff --git a/specification/identity_service_api.rst b/specification/identity_service_api.rst
index 3be9d9eb..3b037caf 100644
--- a/specification/identity_service_api.rst
+++ b/specification/identity_service_api.rst
@@ -1,4 +1,6 @@
.. Copyright 2016 OpenMarket Ltd
+.. Copyright 2017 Kamax.io
+.. Copyright 2017 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.
@@ -52,6 +54,8 @@ necessarily provide evidence that they have validated associations, but claim to
have done so. Establishing the trustworthiness of an individual identity service
is left as an exercise for the client.
+3PID types are described in `3PID Types`_ Appendix.
+
Privacy
-------
@@ -63,6 +67,11 @@ 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).
+Status check
+------------
+
+{{ping_is_http_api}}
+
Key management
--------------
@@ -105,100 +114,20 @@ 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.
-Creating a session
+Email associations
~~~~~~~~~~~~~~~~~~
-A client makes a call to::
+{{email_associations_is_http_api}}
- POST https://my.id.server:8090/_matrix/identity/api/v1/validate/email/requestToken
+Phone number associations
+~~~~~~~~~~~~~~~~~~~~~~~~~
- client_secret=monkeys_are_GREAT&
- email=foo@bar.com&
- send_attempt=1
+{{phone_associations_is_http_api}}
-It may also optionally specify next_link. If next_link is specified, when the
-validation is completed, the identity service will redirect the user to that
-URL.
+General
+~~~~~~~
-This will create a new "session" on the identity service, identified by an
-``sid``.
-
-The identity service will send an email containing a token. If that token is
-presented to the identity service in the future, it indicates that that user was
-able to read the email for that email address, and so we validate ownership of
-the email address.
-
-We return the ``sid`` generated for this session to the caller, in a JSON object
-containing the ``sid`` key.
-
-If a send_attempt is specified, the server will only send an email if the
-send_attempt is a number greater than the most recent one which it has seen (or
-if it has never seen one), scoped to that email address + client_secret pair.
-This is to avoid repeatedly sending the same email in the case of request
-retries between the POSTing user and the identity service. The client should
-increment this value if they desire a new email (e.g. a reminder) to be sent.
-
-Note that Home Servers offer APIs that proxy this API, adding additional
-behaviour on top, for example, ``/register/email/requestToken`` is designed
-specifically for use when registering an account and therefore will inform
-the user if the email address given is already registered on the server.
-
-Validating ownership of an email
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A user may make either a ``GET`` or a ``POST`` request to
-``/_matrix/identity/api/v1/validate/email/submitToken`` with the following
-parameters (either as query parameters or URL-encoded POST parameters):
-- ``sid`` the sid for the session, generated by the ``requestToken`` call.
-- ``client_secret`` the client secret which was supplied to the ``requestToken`` call.
-- ``token`` the token generated by the ``requestToken`` call, and emailed to the user.
-
-If these three values are consistent with a set generated by a ``requestToken``
-call, ownership of the email address is considered to have been validated. This
-does not publish any information publicly, or associate the email address with
-any Matrix user ID. Specifically, calls to ``/lookup`` will not show a binding.
-
-Otherwise, an error will be returned.
-
-Checking non-published 3pid ownership
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A client can check whether ownership of a 3pid was validated by making an
-HTTP GET request to ``/_matrix/identity/api/v1/3pid/getValidated3pid``, passing
-the ``sid`` and ``client_secret`` as query parameters from the ``requestToken``
-call.
-
-It will return something of either the form::
-
- {"medium": "email", "validated_at": 1457622739026, "address": "foo@bar.com"}
-
-or::
-
- {"errcode": "M_SESSION_NOT_VALIDATED", "error": "This validation session has not yet been completed"}
-
-If the ``sid`` and ``client_secret`` were not recognised, or were not correct,
-an error will be returned.
-
-Publishing a validated association
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-An association between a session and a Matrix user ID can be published by making
-a URL-encoded HTTP POST request to ``/_matrix/identity/api/v1/3pid/bind`` with
-the following parameters::
-
- sid=sid&
- client_secret=monkeys_are_GREAT&
- mxid=@foo:bar.com
-
-If the session is still valid, this will publish an association between the
-3pids validated on that session and the passed Matrix user ID. Future calls
-to ``/lookup`` for any of the session's 3pids will return this association.
-
-If the 3pid has not yet been validated, the HTTP request will be rejected, and
-the association will not be established.
-
-If the ``sid`` and ``client_secret`` were not recognised, or were not correct,
-an error will be returned.
+{{associations_is_http_api}}
Invitation Storage
------------------
@@ -207,56 +136,38 @@ An identity service 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.
-If one makes a ``POST`` request to ``/_matrix/identity/api/v1/store-invite`` with the following URL-encoded POST parameters:
-
-- ``medium`` (string, required): The literal string ``email``.
-- ``address`` (string, required): The email address of the invited user.
-- ``room_id`` (string, required): The Matrix room ID to which the user is invited.
-- ``sender`` (string, required): The matrix user ID of the inviting user.
-
-An arbitrary number of other parameters may also be specified. These may be used in the email generation described below.
-
-The service will look up whether the 3pid is bound to a Matrix user ID. If it is, the request will be rejected with a 400 status code.
-
-If the medium is something other than the literal string ``email``, the request will be rejected with a 400 status code.
-
-Otherwise, the service will then generate a random string called ``token``, and an ephemeral public key.
-
-The service also generates a ``display_name`` for the inviter, which is a redacted version of ``address`` which does not leak the full contents of the ``address``.
-
-The service records persistently all of the above information.
-
-It also generates an email containing all of this data, sent to the ``address`` parameter, notifying them of the invitation.
-
-The response body is then populated as the JSON-encoded dictionary containing the following fields:
-- ``token`` (string): The generated token.
-- ``public_keys`` ([string]): A list of [server's long-term public key, generated ephemeral public key].
-- ``display_name`` (string): The generated (redacted) display_name.
-
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 which looks roughly as below::
POST https://bar.com:8448/_matrix/federation/v1/3pid/onbind
Content-Type: application/json
{
- "invites": [{
- "mxid": "@foo:bar.com",
- "token": "abc123",
- "signatures": {
- "my.id.server": {
- "ed25519:0": "def987"
- }
- }
- }],
-
- "medium": "email",
- "address": "foo@bar.com",
- "mxid": "@foo:bar.com"
+ "medium": "email",
+ "address": "foo@bar.baz",
+ "mxid": "@alice:example.tld",
+ "invites": [
+ {
+ "medium": "email",
+ "address": "foo@bar.baz",
+ "mxid": "@alice:example.tld",
+ "room_id": "!something:example.tld",
+ "sender": "@bob:example.tld",
+ "signed": {
+ "mxid": "@alice:example.tld",
+ "signatures": {
+ "vector.im": {
+ "ed25519:0": "somesignature"
+ }
+ },
+ "token": "sometoken"
+ }
+ }
+ ]
}
Where the signature is produced using a long-term private key.
-Also, the generated ephemeral public key will be listed as valid on requests to ``/_matrix/identity/api/v1/pubkey/ephemeral/isvalid``.
+{{store_invite_is_http_api}}
Ephemeral invitation signing
----------------------------
@@ -264,20 +175,7 @@ Ephemeral invitation signing
To aid clients who may not be able to perform crypto themselves, the identity service 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.
-The identity service will happily sign invitation details with a request-specified ed25519 private key for you, if you want it to. It takes URL-encoded POST parameters:
-- mxid (string, required)
-- token (string, required)
-- private_key (string, required): The unpadded base64-encoded private key.
+{{invitation_signing_is_http_api}}
-It will look up ``token`` which was stored in a call to ``store-invite``, and fetch the sender of the invite. It will then respond with JSON which looks something like::
-
- {
- "mxid": "@foo:bar.com",
- "sender": "@baz:bar.com",
- "signatures" {
- "my.id.server": {
- "ed25519:0": "def987"
- }
- },
- "token": "abc123"
- }
+.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
+.. _`3PID Types`: ../appendices.html#pid-types
diff --git a/specification/index.rst b/specification/index.rst
index ae6611c0..36c89958 100644
--- a/specification/index.rst
+++ b/specification/index.rst
@@ -27,17 +27,394 @@ Voice over IP (VoIP) signalling, Internet of Things (IoT) communication, and bri
together existing communication silos - providing the basis of a new open real-time
communication ecosystem.
-`Introduction to Matrix `_ provides a full introduction to Matrix and the spec.
+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 following APIs are documented in this specification:
+The specification consists of the following parts:
{{apis}}
-`Appendices `_ with supplemental information not specific to
-one of the above APIs are also available.
+The `Appendices `_ contain supplemental information not specific to
+one of the above APIs.
+
+Introduction to the Matrix APIs
+-------------------------------
+.. WARNING::
+ The Matrix specification is still evolving: the APIs are not yet frozen
+ and this document is in places a work in progress or stale. We have made every
+ effort to clearly flag areas which are still being finalised.
+ We're publishing it at this point because it's complete enough to be more than
+ useful and provide a canonical reference to how Matrix is evolving. Our end
+ goal is to mirror WHATWG's `Living Standard
+ `_.
+
+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 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
+------------
+
+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' 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:domain.com }
+ | ^
+ | |
+ [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 | | domain.com |
+ +------------------+ +------------------+
+ | ^
+ | [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, domain.com |
+ | Members: |
+ | - @alice:matrix.org |
+ | - @bob:domain.com |
+ | Messages: |
+ | - @alice:matrix.org |
+ | Content: { JSON object } |
+ |...................................|
+
+Federation maintains *shared data structures* per-room between multiple home
+servers. 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.
+
+
+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:domain.com !aaabaa:matrix.org
+ | ^
+ | |
+ _______V____________________|____
+ | domain.com |
+ | 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?
Specification Versions
----------------------
@@ -54,3 +431,9 @@ The specification for each API is versioned in the form ``rX.Y.Z``.
* 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/intro.rst b/specification/intro.rst
deleted file mode 100644
index 5d0587f2..00000000
--- a/specification/intro.rst
+++ /dev/null
@@ -1,609 +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.
-
-.. contents:: Table of Contents
-.. sectnum::
-
-.. 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.
-
-Introduction
-------------
-.. WARNING::
- The Matrix specification is still evolving: the APIs are not yet frozen
- and this document is in places a work in progress or stale. We have made every
- effort to clearly flag areas which are still being finalised.
- We're publishing it at this point because it's complete enough to be more than
- useful and provide a canonical reference to how Matrix is evolving. Our end
- goal is to mirror WHATWG's `Living Standard
- `_.
-
-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 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.
-
-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 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 the `Identifier Grammar`_ section 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 the `Identifier Grammar`_ section 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:domain.com }
- | ^
- | |
- [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 | | domain.com |
- +------------------+ +------------------+
- | ^
- | [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, domain.com |
- | Members: |
- | - @alice:matrix.org |
- | - @bob:domain.com |
- | Messages: |
- | - @alice:matrix.org |
- | Content: { JSON object } |
- |...................................|
-
-Federation maintains *shared data structures* per-room between multiple home
-servers. 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.
-
-
-Room Aliases
-++++++++++++
-
-Each room can also have multiple "Room Aliases", which look like::
-
- #room_alias:domain
-
-See the `Identifier Grammar`_ section 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:domain.com !aaabaa:matrix.org
- | ^
- | |
- _______V____________________|____
- | domain.com |
- | 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?
-
-
-Identifier Grammar
-------------------
-
-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. The complete grammar is::
-
- server_name = dns_name [ ":" port]
- dns_name = host
- port = *DIGIT
-
-where ``host`` is as defined by `RFC3986, section 3.2.2
-`_.
-
-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)
-
-
-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::
-
- &localpart:domain
-
-where ``&`` represents a 'sigil' character; ``domain`` is the `server name`_ of
-the homeserver which allocated the identifier, and ``localpart`` is an
-identifier allocated by that homeserver.
-
-The sigil characters are as follows:
-
-* ``@``: User ID
-* ``!``: Room ID
-* ``$``: Event ID
-* ``#``: Room alias
-
-The precise grammar defining the allowable format of an identifier depends on
-the type of identifier.
-
-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 ensure that the identifier can be used as-is in as wide a number of
- situations as possible, without requiring escaping. For instance, allowing
- "%" or "/" would make it harder to use a user ID in a URI. "*" is used as a
- wildcard in some APIs (notably the filter API), so it also 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-7E
-
-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 implemention 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. An event ID has the format::
-
- $opaque_id:domain
-
-The ``domain`` of a room/event 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.
-
-.. TODO-spec
- What is the grammar for the opaque part? https://matrix.org/jira/browse/SPEC-389
-
-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
-
-
-License
--------
-
-The Matrix specification is licensed under the `Apache License, Version 2.0
-`_.
diff --git a/specification/modules/cas_login.rst b/specification/modules/cas_login.rst
index 4c3b826a..5de98057 100644
--- a/specification/modules/cas_login.rst
+++ b/specification/modules/cas_login.rst
@@ -18,7 +18,7 @@ CAS-based client login
.. _module:cas_login:
`Central Authentication Service
-`_
+`_
(CAS) is a web-based single sign-on protocol.
An overview of the process, as used in Matrix, is as follows:
@@ -98,9 +98,9 @@ check for certain user attributes in the response. Any required attributes
should be configured by the server administrator.
Once the ticket has been validated, the server MUST map the CAS ``user_id``
-to a valid `Matrix user identifier <../intro.html#user-identifiers>`_. The
+to a valid `Matrix user identifier <../index.html#user-identifiers>`_. The
guidance in `Mapping from other character sets
-<../intro.html#mapping-from-other-character-sets>`_ may be useful.
+<../index.html#mapping-from-other-character-sets>`_ may be useful.
If the generated user identifier represents a new user, it should be registered
as a new user.
diff --git a/specification/modules/end_to_end_encryption.rst b/specification/modules/end_to_end_encryption.rst
index 48416da4..fa461cc2 100644
--- a/specification/modules/end_to_end_encryption.rst
+++ b/specification/modules/end_to_end_encryption.rst
@@ -17,6 +17,551 @@ End-to-End Encryption
.. _module:e2e:
-End to end encryption is being worked on and will be coming soon.
+Matrix optionally supports end-to-end encryption, allowing rooms to be created
+whose conversation contents is not decryptable or interceptable on any of the
+participating homeservers.
-You can read about what's underway at http://matrix.org/speculator/spec/drafts%2Fe2e/client_server/unstable.html#end-to-end-encryption.
+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 keys using this algorithm are objects with the properties ``key`` (giving
+the Base64-encoded 32-byte Curve25519 public key), and ``signatures`` (giving a
+signature for the key object, as described in `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|_ requests 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`_
+
+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, the basic process for device verification is for Alice to verify
+that the public Ed25519 signing key she received via ``/keys/query`` for Bob's
+device corresponds to the private key in use by Bob's device. For now, it is
+recommended that clients provide mechanisms by which the user can see:
+
+1. The public part of their device's Ed25519 signing key, encoded using
+ `unpadded Base64`_.
+
+2. The list of devices in use for each user in a room, along with the public
+ Ed25519 signing key for each device, again encoded using unpadded Base64.
+
+Alice can then meet Bob in person, or contact him via some other trusted
+medium, and ask him to read out the Ed25519 key shown on his device. She
+compares this with the value shown for his device on her client.
+
+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/git/olm/about/docs/signing.rst.
+
+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.
+
+``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}}
+
+Key management API
+~~~~~~~~~~~~~~~~~~
+
+{{keys_cs_http_api}}
+
+
+.. anchor for link from /sync api spec
+.. |device_lists_sync| replace:: End-to-end encryption
+.. _device_lists_sync:
+
+Extensions to /sync
+~~~~~~~~~~~~~~~~~~~
+
+This module adds an optional ``device_lists`` property to the |/sync|_
+response, as specified below. The server need only populate this property for
+an incremental ``/sync`` (ie, one where the ``since`` parameter was
+specified). The client is expected to use |/keys/query|_ or |/keys/changes|_
+for the equivalent functionality after an initial sync, as documented in
+`Tracking the device list for a user`_.
+
+It also adds a ``one_time_keys_count`` property. Note the spelling difference
+with the ``one_time_key_counts`` property in the |/keys/upload|_ response.
+
+.. todo: generate this from a swagger definition?
+
+.. device_lists: { changed: ["@user:server", ... ]},
+
+============ =========== =====================================================
+Parameter Type Description
+============ =========== =====================================================
+device_lists DeviceLists Optional. Information on e2e device updates. Note:
+ only present on an incremental sync.
+|device_otk| {string: Optional. For each key algorithm, the number of
+ integer} unclaimed one-time keys currently held on the server
+ for this device.
+============ =========== =====================================================
+
+``DeviceLists``
+
+========= ========= =============================================
+Parameter Type Description
+========= ========= =============================================
+changed [string] List of users who have updated their device identity keys,
+ or who now share an encrypted room with the client since
+ the previous sync response.
+left [string] List of users with whom we do not share any encrypted rooms
+ anymore since the previous sync response.
+========= ========= =============================================
+
+.. NOTE::
+
+ For optimal performance, Alice should be added to ``changed`` in Bob's sync only
+ when she adds a new device, or when Alice and Bob now share a room but didn't
+ share any room previously. However, for the sake of simpler logic, a server
+ may add Alice to ``changed`` when Alice and Bob share a new room, even if they
+ previously already shared a room.
+
+Example response:
+
+.. code:: json
+
+ {
+ "next_batch": "s72595_4483_1934",
+ "rooms": {"leave": {}, "join": {}, "invite": {}},
+ "device_lists": {
+ "changed": [
+ "@alice:example.com",
+ ],
+ "left": [
+ "@bob:example.com",
+ ],
+ },
+ "device_one_time_keys_count": {
+ "curve25519": 10,
+ "signed_curve25519": 20
+ }
+ }
+
+.. References
+
+.. _ed25519: http://ed25519.cr.yp.to/
+.. _curve25519: https://cr.yp.to/ecdh.html
+.. _`Olm specification`: http://matrix.org/docs/spec/olm.html
+.. _`Megolm specification`: http://matrix.org/docs/spec/megolm.html
+
+.. _`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/guest_access.rst b/specification/modules/guest_access.rst
index 4e04aa0d..d579da83 100644
--- a/specification/modules/guest_access.rst
+++ b/specification/modules/guest_access.rst
@@ -50,6 +50,8 @@ The following API endpoints are allowed to be accessed by guest accounts for
retrieving events:
* `GET /rooms/:room_id/state <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state>`_
+* `GET /rooms/:room_id/context/:event_id <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-context-eventid>`_
+* `GET /rooms/:room_id/event/:event_id <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-event-eventid>`_
* `GET /rooms/:room_id/state/:event_type/:state_key <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state-eventtype-statekey>`_
* `GET /rooms/:room_id/messages <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages>`_
* `GET /rooms/:room_id/initialSync <#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync>`_
diff --git a/specification/modules/history_visibility.rst b/specification/modules/history_visibility.rst
index 476ea889..84435edb 100644
--- a/specification/modules/history_visibility.rst
+++ b/specification/modules/history_visibility.rst
@@ -82,7 +82,7 @@ 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 ``joineded`` to
+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
diff --git a/specification/modules/ignore_users.rst b/specification/modules/ignore_users.rst
new file mode 100644
index 00000000..56a410d1
--- /dev/null
+++ b/specification/modules/ignore_users.rst
@@ -0,0 +1,62 @@
+.. 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
index 342050a7..079a4801 100644
--- a/specification/modules/instant_messaging.rst
+++ b/specification/modules/instant_messaging.rst
@@ -46,6 +46,8 @@ Usage of this event is discouraged for several reasons:
{{m_room_avatar_event}}
+{{m_room_pinned_events_event}}
+
m.room.message msgtypes
~~~~~~~~~~~~~~~~~~~~~~~
@@ -54,6 +56,60 @@ 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.
+
+.. Note::
+ A future iteration of the specification will support more powerful and extensible
+ message formatting options, such as the proposal `MSC1225 `_.
+
{{msgtype_events}}
@@ -295,3 +351,4 @@ Clients should sanitise **all displayed keys** for unsafe HTML to prevent Cross-
Scripting (XSS) attacks. This includes room names and topics.
.. _`E2E module`: `module:e2e`_
+.. _`Matrix Content (MXC) URI`: `module:content`_
\ No newline at end of file
diff --git a/specification/identity_servers.rst b/specification/modules/openid.rst
similarity index 62%
rename from specification/identity_servers.rst
rename to specification/modules/openid.rst
index db82865e..63406719 100644
--- a/specification/identity_servers.rst
+++ b/specification/modules/openid.rst
@@ -1,4 +1,4 @@
-.. 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.
@@ -12,11 +12,13 @@
.. See the License for the specific language governing permissions and
.. limitations under the License.
-Identity Servers
-================
-.. NOTE::
- This section is a work in progress.
+OpenID
+======
-.. TODO-doc Dave
- - 3PIDs and identity server, functions
+.. _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/push.rst b/specification/modules/push.rst
index 9bb65b96..1972fa17 100644
--- a/specification/modules/push.rst
+++ b/specification/modules/push.rst
@@ -124,7 +124,7 @@ 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 kinds of rule
+have extra keys depending on the value of ``kind``. The different kinds of rule
in descending order of priority are:
Override Rules ``override``
@@ -369,6 +369,41 @@ Definition:
}
+``.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",
+ "value": true
+ }
+ ]
+ }
+
+
Default Content Rules
^^^^^^^^^^^^^^^^^^^^^
@@ -428,7 +463,47 @@ Definition:
"value": false
}
]
- },
+ }
+
+``.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"
+ }
+ ],
+ "actions": [
+ "notify",
+ {
+ "set_tweak": "sound",
+ "value": "default"
+ },
+ {
+ "set_tweak": "highlight",
+ "value": false
+ },
+ {
+ "kind": "event_match",
+ "key": "type",
+ "pattern": "m.room.encrypted"
+ }
+ ]
+ }
``.m.rule.room_one_to_one``
```````````````````````````
@@ -489,6 +564,37 @@ Definition:
]
}
+``.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 1:1 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",
+ {
+ "set_tweak": "highlight",
+ "value": false
+ }
+ ]
+ }
+
Conditions
++++++++++
@@ -523,6 +629,21 @@ rule determines its behaviour. The following conditions are defined:
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.
+
Push Rules: API
~~~~~~~~~~~~~~~
@@ -622,3 +743,5 @@ 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/unstable.html
diff --git a/specification/modules/report_content.rst b/specification/modules/report_content.rst
new file mode 100644
index 00000000..5eca69cd
--- /dev/null
+++ b/specification/modules/report_content.rst
@@ -0,0 +1,35 @@
+.. 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/send_to_device.rst b/specification/modules/send_to_device.rst
index dd85a992..232becae 100644
--- a/specification/modules/send_to_device.rst
+++ b/specification/modules/send_to_device.rst
@@ -12,11 +12,12 @@
.. See the License for the specific language governing permissions and
.. limitations under the License.
+.. _module:to_device:
+.. _`to-device`:
+
Send-to-Device messaging
========================
-.. _module:to_device:
-
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.
@@ -35,10 +36,13 @@ 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 to /sync`_. Clients should
+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
diff --git a/specification/modules/server_acls.rst b/specification/modules/server_acls.rst
new file mode 100644
index 00000000..2b2d8f35
--- /dev/null
+++ b/specification/modules/server_acls.rst
@@ -0,0 +1,70 @@
+.. 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/stickers.rst b/specification/modules/stickers.rst
new file mode 100644
index 00000000..346b0d84
--- /dev/null
+++ b/specification/modules/stickers.rst
@@ -0,0 +1,53 @@
+.. 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
index f0a7fa52..f965c2e8 100644
--- a/specification/modules/tags.rst
+++ b/specification/modules/tags.rst
@@ -1,4 +1,5 @@
.. 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.
@@ -17,42 +18,48 @@ Room Tagging
.. _module:tagging:
-Users can add tags to rooms. Tags are short strings used to label rooms, e.g.
-"work", "family". A room may have multiple tags. Tags are only visible to the
-user that set them but are shared across all their devices.
+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 in a ``/sync``.
+``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 ``m.tag`` can also be received in a ``/events`` response or in the
-``account_data`` section of a room in ``/initialSync``. ``m.tag``
-events appearing in ``/events`` will have a ``room_id`` with the room
-the tags are for.
-
-Each tag has an associated JSON object with information about the tag, e.g how
+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 string. The string
-are compared lexicographically by unicode codepoint to determine which should
-displayed first. So a room with a tag with an ``order`` key of ``"apples"``
-would appear before a room with a tag with an ``order`` key of ``"oranges"``.
-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.
+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 name of a tag should be human readable. When displaying tags for a room a
-client should display this human readable name. When adding a tag for a room
-a client may offer a list to choose from that includes all the tags that the
-user has previously set on any of their rooms.
+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 functionnality 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.
Two special names are listed in the specification:
+The following tags are defined in the ``m.*`` namespace:
-* ``m.favourite``
-* ``m.lowpriority``
+* ``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_tag_event}}
diff --git a/specification/modules/third_party_networks.rst b/specification/modules/third_party_networks.rst
new file mode 100644
index 00000000..cd4ce414
--- /dev/null
+++ b/specification/modules/third_party_networks.rst
@@ -0,0 +1,20 @@
+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/proposals.rst b/specification/proposals.rst
new file mode 100644
index 00000000..371850ab
--- /dev/null
+++ b/specification/proposals.rst
@@ -0,0 +1,6 @@
+Tables of Tracked Proposals
+---------------------------
+
+This file is autogenerated by a jenkins build process
+
+View the current live version `at https://matrix.org/docs/spec/proposals `_
diff --git a/specification/proposals_intro.rst b/specification/proposals_intro.rst
new file mode 100644
index 00000000..cc4d5d22
--- /dev/null
+++ b/specification/proposals_intro.rst
@@ -0,0 +1,213 @@
+.. title:: Proposals for Spec Changes to Matrix
+
+.. contents:: Table of Contents
+.. sectnum::
+
+Proposals for Spec Changes to Matrix
+------------------------------------
+
+The process for submitting a Matrix Spec Change (MSC) Proposal is as follows:
+
+- Produce a publicly-accessible proposal describing your change:
+
+ - Please use Google Docs, or an equivalent system capable of collaborative
+ editing, with versioned history, suggestions ('track changes'), threaded
+ comments, and good mobile support. Please ensure the document is
+ world-commentable or -editable.
+ - We do not use Github issues (or Etherpad) for the design process of the
+ proposal, as the document review/commenting capabilities aren't good
+ enough.
+ - We also don't jump straight to PRing against the spec itself, as it's much
+ faster to iterate on a proposal in freeform document form than in the
+ terse and formal structure of the spec.
+ - In the proposal, please 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.
+ - A `template with suggested headers
+ `_
+ is available.
+
+- Make a new issue at https://github.com/matrix-org/matrix-doc/issues, whose
+ description should list the metadata as per below. Use the github search
+ function to attempt to locate any related github issues, and link any that
+ are found in the body of the new issue.
+- Gather feedback as widely as possible from the community and core team on
+ the proposal.
+
+ - The aim is to get maximum consensus on the trade-offs chosen to get an
+ optimal solution.
+ - A good place to ask for feedback on a specific proposal is
+ `#matrix-spec:matrix.org `_.
+ However, authors/shepherds are welcome to use an alternative room if they
+ prefer - please advertise it in #matrix-spec:matrix.org though and link
+ to it on the github issue. N.B. 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);
+ #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. Ideally the author would 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. A shepherd is
+ typically a neutral party from the core team or an experienced member of
+ the community.
+
+- Once the proposal has sufficient consensus and passed review, you **must**
+ show an implementation to prove that it works well in practice, before a
+ spec PR will be accepted. Iterate on the proposal if needed.
+- Finally, please make a new spec PR which includes the changes as
+ implemented against
+ https://github.com/matrix-org/matrix-doc/tree/master/specification. This
+ will then be reviewed and hopefully merged! Please sign off the spec PR as
+ per the `CONTRIBUTING.rst
+ `_
+ guidelines.
+
+Final decisions on review are made by the Matrix core team
+(+matrix:matrix.org), acting on behalf of the whole Matrix community.
+
+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 IP. The Matrix core team pledges
+to act as a neutral custodian for Matrix on behalf of the whole ecosystem,
+just as it has since Matrix's inception in May 2014.
+
+For clarity: the Matrix ecosystem is anyone who uses the Matrix protocol. That
+includes client users, server admins, client developers, bot developers,
+bridge and AS 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 the core team 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 quality and utility of the Matrix spec.
+
+The guiding principles of the overall project are being worked on as part of
+the upcoming governance proposal, but could be something like:
+
+* Supporting the whole long-term ecosystem rather than individual stakeholder gain
+* Openness rather than proprietariness
+* Collaboration rather than competition
+* Accessibility rather than elitism
+* Transparency rather than stealth
+* Empathy rather than contrariness
+* Pragmatism rather than perfection
+* Proof rather than conjecture
+
+The above directions are intended to be simple and pragmatic rather than
+exhaustive, and aim to provide guidelines until we have a formal spec
+governance process in place that covers the whole Matrix community. In order
+to get Matrix out of beta as quickly as possible, as of May 2018 we are
+prioritising spec and reference implementation development over writing formal
+governance, but a formal governance document will follow as rapidly as
+possible.
+
+The process for handling proposals is described in the following diagram. Note
+that the lifetime of a proposal is tracked through the corresponding labels for
+each stage in the `matrix-doc issue tracker
+`_.
+
+::
+
+ + +
+ Proposals | Spec PRs | Other States
+ +-------+ | +------+ | +----------+
+ | |
+ | |
+ +----------+ | +---------+ | +---------+
+ | | | | | | | |
+ | Proposal | | +------> Spec PR | | | Blocked |
+ | WIP | | | | Missing | | | |
+ | | | | | | | +---------+
+ +----+-----+ | | +----+----+ |
+ | | | | |
+ | | | | | +-----------+
+ +--------v----------+ | | | | | |
+ | | | | +---------v--------+ | | Abandoned |
+ | Proposal | | | | | | | |
+ | Ready for Review | | | | Spec PR | | +-----------+
+ | | | | | Ready for Review | |
+ +----------+--------+ | | | | | +-----------+
+ | | | +---------+--------+ | | |
+ | | | | | | Obsolete |
+ +------v----+ | | | | | |
+ | | | | +-----v-----+ | +-----------+
+ | Proposal | | | | | |
+ | In Review | | | | Spec PR | |
+ | | | | | In Review | | +----------+
+ +----+------+ | | | | | | |
+ | | | +-----+-----+ | | Rejected |
+ | | | | | | |
+ +------v--------+ | | | | +----------+
+ | | | | | |
+ | Proposal | | | +----v----+ |
+ | Passed Review | | | | | |
+ | | | | | Merged! | |
+ +-------+-------+ | | | | |
+ | | | +---------+ |
+ | | | |
+ +---------------+ |
+ | |
+ + +
+
+Lifetime States
+---------------
+
+=========================== =======================================================
+Proposal WIP A proposal document which is still work-in-progress but is being shared to incorporate feedback
+Proposal Ready for Review A proposal document which is now ready and waiting for review by the core team and community
+Proposal In Review A proposal document which is currently in review
+Proposal Passed Review A proposal document which has passed review as worth implementing and then being added to the spec
+Spec PR Missing A proposal which has been implemented and has been used in the wild for a few months but hasn't yet been added to the spec
+Spec PR Ready for Review A proposal which has been PR'd against the spec and is awaiting review
+Spec PR In Review A proposal which has been PR'd against the spec and is in review
+Merged A proposal whose PR has merged into the spec!
+Blocked A proposal which is temporarily blocked on some external factor (e.g. being blocked on another proposal first being approved)
+Abandoned A proposal where the author/shepherd has not been responsive for a few months
+Obsolete A proposal which has been overtaken by other proposals
+Rejected A proposal which is not going to be incorporated into Matrix
+=========================== =======================================================
+
+
+Proposal Tracking
+-----------------
+
+This is a living document generated from the list of proposals at
+`matrix-doc/issues `_ on
+GitHub.
+
+We use labels and some metadata in the issues' descriptions to generate this
+page. Labels are assigned by the core team whilst triaging the issues based
+on those which exist in the matrix-doc repo already.
+
+Other metadata:
+
+- the MSC (Matrix Spec Change) number is taken from the github issue ID. This
+ is carried for the lifetime of the proposal, including the PR creation
+ phase. N.B. They are not in chronological order!
+- Please use the github issue title to set the title.
+- Please link to the proposal document by adding a "Documentation: " line
+ in the issue description.
+- 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 issue, but can be overriden by
+ adding a "Date: yyyy-mm-dd" line in the issue description.
+- Updated Date is taken from github.
+- Author is the creator of the github issue, but can be overriden 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
index 29a41bf7..e4a9d6ea 100644
--- a/specification/push_gateway.rst
+++ b/specification/push_gateway.rst
@@ -67,4 +67,9 @@ This describes the format used by "HTTP" pushers to send notifications of
events to Push Gateways. If the endpoint returns an HTTP error code, the
homeserver SHOULD retry for a reasonable amount of time using exponential backoff.
+When pushing notifications for events, the hoemserver is expected to include all of
+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.
+
{{push_notifier_push_http_api}}
diff --git a/specification/server_server_api.rst b/specification/server_server_api.rst
index 1f283722..439b35f9 100644
--- a/specification/server_server_api.rst
+++ b/specification/server_server_api.rst
@@ -1,4 +1,6 @@
.. Copyright 2016 OpenMarket Ltd
+.. 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.
@@ -15,15 +17,19 @@
Federation API
==============
+.. WARNING::
+ This API is unstable and will change without warning or discussion while
+ we work towards a r0 release (scheduled for August 2018).
+
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 request historic messages from each other, and 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 GETs and PUTs 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.
+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:
@@ -71,38 +77,53 @@ Server Discovery
Resolving Server Names
~~~~~~~~~~~~~~~~~~~~~~
-Each matrix homeserver is identified by a server name consisting of a DNS name
+Each matrix homeserver is identified by a server name consisting of a hostname
and an optional TLS port.
.. code::
- server_name = dns_name [ ":" tls_port]
- dns_name =
+ server_name = hostname [ ":" tls_port]
tls_port = *DIGIT
.. **
If the port is present then the server is discovered by looking up an AAAA or
-A record for the DNS name and connecting to the specified TLS port. If the port
+A record for the hostname and connecting to the specified TLS port. If the port
is absent then the server is discovered by looking up a ``_matrix._tcp`` SRV
-record for the DNS name. If this record does not exist then the server is
-discovered by looking up an AAAA or A record on the DNS name and taking the
+record for the hostname. If this record does not exist then the server is
+discovered by looking up an AAAA or A record on the hostname and taking the
default fallback port number of 8448.
Homeservers may use SRV records to load balance requests between multiple TLS
endpoints or to failover to another endpoint if an endpoint fails.
+If the DNS name is a literal IP address, the port specified or the fallback
+port should be used.
+
+When making requests to servers, use the DNS name of the target server in the
+``Host`` header, regardless of the host given in the SRV record. For example,
+if making a request to ``example.org``, and the SRV record resolves to ``matrix.
+example.org``, the ``Host`` header in the request should be ``example.org``. The
+port number for target server should not appear in the ``Host`` header.
+
+Server implementation
+~~~~~~~~~~~~~~~~~~~~~~
+
+{{version_ss_http_api}}
+
Retrieving Server Keys
~~~~~~~~~~~~~~~~~~~~~~
-Version 2
-+++++++++
+.. NOTE::
+ There was once a "version 1" of the key exchange. It has been removed from the
+ specification due to lack of significance. It may be reviewed `here
+ `_.
-Each homeserver publishes its public keys under ``/_matrix/key/v2/server/``.
-Homeservers query for keys by either getting ``/_matrix/key/v2/server/``
+Each homeserver publishes its public keys under ``/_matrix/key/v2/server/{keyId}``.
+Homeservers query for keys by either getting ``/_matrix/key/v2/server/{keyId}``
directly or by querying an intermediate notary server using a
-``/_matrix/key/v2/query`` API. Intermediate notary servers query the
-``/_matrix/key/v2/server/`` API on behalf of another server and sign the
-response with their own key. A server may query multiple notary servers to
+``/_matrix/key/v2/query/{serverName}/{keyId}`` API. Intermediate notary servers
+query the ``/_matrix/key/v2/server/{keyId}`` API on behalf of another server and
+sign the response with their own key. A server may query multiple notary servers to
ensure that they all report the same public keys.
This approach is borrowed from the `Perspectives Project`_, but modified to
@@ -111,476 +132,501 @@ avoiding a single trust-root since each server is free to pick which notary
servers they trust and can corroborate the keys returned by a given notary
server by querying other servers.
-.. _Perspectives Project: http://perspectives-project.org/
+.. _Perspectives Project: https://web.archive.org/web/20170702024706/https://perspectives-project.org/
Publishing Keys
-^^^^^^^^^^^^^^^
++++++++++++++++
Homeservers publish the allowed TLS fingerprints and signing keys in a JSON
object at ``/_matrix/key/v2/server/{key_id}``. The response contains a list of
``verify_keys`` that are valid for signing federation requests made by the
-server and for signing events. It contains a list of ``old_verify_keys``
-which are only valid for signing events. Finally the response contains a list
-of TLS certificate fingerprints to validate any connection made to the server.
+homeserver and for signing events. It contains a list of ``old_verify_keys`` which
+are only valid for signing events. Finally the response contains a list of TLS
+certificate fingerprints to validate any connection made to the homeserver.
-A server may have multiple keys active at a given time. A server may have any
-number of old keys. It is recommended that servers return a single JSON
-response listing all of its keys whenever any ``key_id`` is requested to reduce
-the number of round trips needed to discover the relevant keys for a server.
-However a server may return a different responses for a different ``key_id``.
+{{keys_server_ss_http_api}}
-The ``tls_certificates`` contain a list of hashes of the X.509 TLS certificates
-currently used by the server. The list must include SHA-256 hashes for every
-certificate currently in use by the server. These fingerprints are valid until
-the millisecond POSIX timestamp in ``valid_until_ts``.
-
-The ``verify_keys`` can be used to sign requests and events made by the server
-until the millisecond POSIX timestamp in ``valid_until_ts``. If a homeserver
-receives an event with a ``origin_server_ts`` after the ``valid_until_ts`` then
-it should request that ``key_id`` for the originating server to check whether
-the key has expired.
-
-The ``old_verify_keys`` can be used to sign events with an ``origin_server_ts``
-before the ``expired_ts``. The ``expired_ts`` is a millisecond POSIX timestamp
-of when the originating server stopped using that key.
-
-Intermediate notary servers should cache a response for half of its remaining
-life time to avoid serving a stale response. Originating servers should avoid
-returning responses that expire in less than an hour to avoid repeated requests
-for an about to expire certificate. Requesting servers should limit how
-frequently they query for certificates to avoid flooding a server with requests.
-
-If a server goes offline intermediate notary servers should continue to return
-the last response they received from that server so that the signatures of old
-events sent by that server can still be checked.
-
-==================== =================== ======================================
- Key Type Description
-==================== =================== ======================================
-``server_name`` String DNS name of the homeserver.
-``verify_keys`` Object Public keys of the homeserver for
- verifying digital signatures.
-``old_verify_keys`` Object The public keys that the server used
- to use and when it stopped using them.
-``signatures`` Object Digital signatures for this object
- signed using the ``verify_keys``.
-``tls_fingerprints`` Array of Objects Hashes of X.509 TLS certificates used
- by this this server encoded as base64.
-``valid_until_ts`` Integer POSIX timestamp when the list of valid
- keys should be refreshed.
-==================== =================== ======================================
-
-
-.. code:: json
-
- {
- "old_verify_keys": {
- "ed25519:auto1": {
- "expired_ts": 922834800000,
- "key": "Base+64+Encoded+Old+Verify+Key"
- }
- },
- "server_name": "example.org",
- "signatures": {
- "example.org": {
- "ed25519:auto2": "Base+64+Encoded+Signature"
- }
- },
- "tls_fingerprints": [
- {
- "sha256": "Base+64+Encoded+SHA-256-Fingerprint"
- }
- ],
- "valid_until_ts": 1052262000000,
- "verify_keys": {
- "ed25519:auto2": {
- "key": "Base+64+Encoded+Signature+Verification+Key"
- }
- }
- }
Querying Keys Through Another Server
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+++++++++++++++++++++++++++++++++++++
-Servers may offer a query API ``_matrix/key/v2/query/`` for getting the keys
-for another server. This API can be used to GET at list of JSON objects for a
-given server or to POST a bulk query for a number of keys from a number of
-servers. Either way the response is a list of JSON objects containing the
-JSON published by the server under ``_matrix/key/v2/server/`` signed by
-both the originating server and by this server.
+Servers may query another server's keys through a notary server. The notary
+server may be another homeserver. The notary server will retrieve keys from
+the queried servers through use of the ``/_matrix/key/v2/server/{keyId}``
+API. The notary server will additionally sign the response from the queried
+server before returning the results.
-The ``minimum_valid_until_ts`` is a millisecond POSIX timestamp indicating
-when the returned certificate will need to be valid until to be useful to the
-requesting server. This can be set using the maximum ``origin_server_ts`` of
-an batch of events that a requesting server is trying to validate. This allows
-an intermediate notary server to give a prompt cached response even if the
-originating server is offline.
+Notary servers can return keys for servers that are offline or having issues
+serving their own keys by using cached responses. Keys can be queried from
+multiple servers to mitigate against DNS spoofing.
-This API can return keys for servers that are offline be using cached responses
-taken from when the server was online. Keys can be queried from multiple
-servers to mitigate against DNS spoofing.
+{{keys_query_ss_http_api}}
-Requests:
+Authentication
+--------------
+
+Request Authentication
+~~~~~~~~~~~~~~~~~~~~~~
+
+Every HTTP request made by a homeserver is authenticated using public key
+digital signatures. The request method, target and body are signed by wrapping
+them in a JSON object and signing it using the JSON signing algorithm. The
+resulting signatures are added as an Authorization header with an auth scheme
+of ``X-Matrix``. Note that the target field should include the full path
+starting with ``/_matrix/...``, including the ``?`` and any query parameters if
+present, but should not include the leading ``https:``, nor the destination
+server's hostname.
+
+Step 1 sign JSON:
.. code::
- GET /_matrix/key/v2/query/${server_name}/${key_id}/?minimum_valid_until_ts=${minimum_valid_until_ts} HTTP/1.1
-
- POST /_matrix/key/v2/query HTTP/1.1
- Content-Type: application/json
-
{
- "server_keys": {
- "$server_name": {
- "$key_id": {
- "minimum_valid_until_ts": $posix_timestamp
- }
- }
- }
- }
-
-
-Response:
-
-.. code::
-
- HTTP/1.1 200 OK
- Content-Type: application/json
- {
- "server_keys": [
- # List of responses with same format as /_matrix/key/v2/server
- # signed by both the originating server and this server.
- ]
- }
-
-Version 1
-+++++++++
-.. WARNING::
- Version 1 of key distribution is obsolete
-
-
-Homeservers publish their TLS certificates and signing keys in a JSON object
-at ``/_matrix/key/v1``.
-
-==================== =================== ======================================
- Key Type Description
-==================== =================== ======================================
-``server_name`` String DNS name of the homeserver.
-``verify_keys`` Object Public keys of the homeserver for
- verifying digital signatures.
-``signatures`` Object Digital signatures for this object
- signed using the ``verify_keys``.
-``tls_certificate`` String The X.509 TLS certificate used by this
- this server encoded as base64.
-==================== =================== ======================================
-
-.. code:: json
-
- {
- "server_name": "example.org",
+ "method": "GET",
+ "uri": "/target",
+ "origin": "origin.hs.example.com",
+ "destination": "destination.hs.example.com",
+ "content": ,
"signatures": {
- "example.org": {
- "ed25519:auto": "Base+64+Encoded+Signature"
+ "origin.hs.example.com": {
+ "ed25519:key1": "ABCDEF..."
}
- },
- "tls_certificate": "Base+64+Encoded+DER+Encoded+X509+TLS+Certificate"
- "verify_keys": {
- "ed25519:auto": "Base+64+Encoded+Signature+Verification+Key"
}
- }
+ }
-When fetching the keys for a server the client should check that the TLS
-certificate in the JSON matches the TLS server certificate for the connection
-and should check that the JSON signatures are correct for the supplied
-``verify_keys``
+Step 2 add Authorization header:
+
+.. code::
+
+ GET /target HTTP/1.1
+ Authorization: X-Matrix origin=origin.example.com,key="ed25519:key1",sig="ABCDEF..."
+ Content-Type: application/json
+
+
+
+
+Example python code:
+
+.. code:: python
+
+ def authorization_headers(origin_name, origin_signing_key,
+ destination_name, request_method, request_target,
+ content=None):
+ request_json = {
+ "method": request_method,
+ "uri": request_target,
+ "origin": origin_name,
+ "destination": destination_name,
+ }
+
+ if content_json is not None:
+ request["content"] = content
+
+ signed_json = sign_json(request_json, origin_name, origin_signing_key)
+
+ authorization_headers = []
+
+ for key, sig in signed_json["signatures"][origin_name].items():
+ authorization_headers.append(bytes(
+ "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"" % (
+ origin_name, key, sig,
+ )
+ ))
+
+ return ("Authorization", authorization_headers)
+
+Response Authentication
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Responses are authenticated by the TLS server certificate. A homeserver should
+not send a request until it has authenticated the connected server to avoid
+leaking messages to eavesdroppers.
+
+Client TLS Certificates
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Requests are authenticated at the HTTP layer rather than at the TLS layer
+because HTTP services like Matrix are often deployed behind load balancers that
+handle the TLS and these load balancers make it difficult to check TLS client
+certificates.
+
+A homeserver may provide a TLS client certificate and the receiving homeserver
+may check that the client certificate matches the certificate of the origin
+homeserver.
Transactions
------------
-.. WARNING::
- This section may be misleading or inaccurate.
The transfer of EDUs and PDUs between homeservers is performed by an exchange
of Transaction messages, which are encoded as JSON objects, passed over an HTTP
PUT request. A Transaction is meaningful only to the pair of homeservers that
exchanged it; they are not globally-meaningful.
-Each transaction has:
- - An opaque transaction ID.
- - A timestamp (UNIX epoch time in milliseconds) generated by its origin
- server.
- - An origin and destination server name.
- - A list of "previous IDs".
- - A list of PDUs and EDUs - the actual message payload that the Transaction
- carries.
-
-Transaction Fields
-~~~~~~~~~~~~~~~~~~
-
-==================== =================== ======================================
- Key Type Description
-==================== =================== ======================================
-``origin`` String DNS name of homeserver making this
- transaction.
-``origin_server_ts`` Integer Timestamp in milliseconds on
- originating homeserver when this
- transaction started.
-``previous_ids`` List of Strings List of transactions that were sent
- immediately prior to this transaction.
-``pdus`` List of Objects List of persistent updates to rooms.
-``edus`` List of Objects List of ephemeral messages.
-==================== =================== ======================================
-
-.. code:: json
-
- {
- "transaction_id":"916d630ea616342b42e98a3be0b74113",
- "ts":1404835423000,
- "origin":"red",
- "prev_ids":["e1da392e61898be4d2009b9fecce5325"],
- "pdus":[...],
- "edus":[...]
- }
-
-The ``prev_ids`` field contains a list of previous transaction IDs that the
-``origin`` server has sent to this ``destination``. Its purpose is to act as a
-sequence checking mechanism - the destination server can check whether it has
-successfully received that Transaction, or ask for a re-transmission if not.
-
-The ``pdus`` field of a transaction is a list, containing zero or more PDUs.[*]
-Each PDU is itself a JSON object containing a number of keys, the exact details
-of which will vary depending on the type of PDU. Similarly, the ``edus`` field
-is another list containing the EDUs. This key may be entirely absent if there
-are no EDUs to transfer.
-
-(* Normally the PDU list will be non-empty, but the server should cope with
-receiving an "empty" transaction, as this is useful for informing peers of other
-transaction IDs they should be aware of. This effectively acts as a push
-mechanism to encourage peers to continue to replicate content.)
+{{transactions_ss_http_api}}
PDUs
----
-All PDUs have:
+Each PDU contains a single Room Event which the origin server wants to send to
+the destination.
-- An ID to identify the PDU itself
-- A room ID that it relates to
-- A declaration of their type
-- A list of other PDU IDs that have been seen recently in that room (regardless
- of which origin sent them)
+The ``prev_events`` field of a PDU identifies the "parents" of the event, and
+thus establishes a partial ordering on events within the room by linking them
+into a Directed Acyclic Graph (DAG). The sending server should populate this
+field with all of the events in the room for which it has not yet seen a
+child - thus demonstrating that the event comes after all other known events.
+For example, consider a room whose events form the DAG shown below. A server
+creating a new event in this room should populate the new event's
+``prev_events`` field with ``E4`` and ``E5``, since neither event yet has a child::
-Required PDU Fields
-~~~~~~~~~~~~~~~~~~~
+ E1
+ ^
+ |
+ +-> E2 <-+
+ | |
+ E3 E5
+ ^
+ |
+ E4
-==================== ================== =======================================
- Key Type Description
-==================== ================== =======================================
-``context`` String Room identifier
-``user_id`` String The ID of the user sending the PDU
-``origin`` String DNS name of homeserver that created
- this PDU
-``pdu_id`` String Unique identifier for PDU on the
- originating homeserver
-``origin_server_ts`` Integer Timestamp in milliseconds on origin
- homeserver when this PDU was created.
-``pdu_type`` String PDU event type
-``content`` Object The content of the PDU.
-``prev_pdus`` List of (String, The originating homeserver, PDU ids and
- String, Object) hashes of the most recent PDUs the
- Triplets homeserver was aware of for the room
- when it made this PDU
-``depth`` Integer The maximum depth of the previous PDUs
- plus one
-``is_state`` Boolean True if this PDU is updating room state
-==================== ================== =======================================
+The ``auth_events`` field of a PDU identifies the set of events which give the
+sender permission to send the event. The ``auth_events`` for the
+``m.room.create`` event in a room is empty; for other events, it should be the
+following subset of the room state:
-.. code:: json
+- The ``m.room.create`` event.
+- The current ``m.room.power_levels`` event, if any.
+- The current ``m.room.join_rules`` event, if any.
+- The sender's current ``m.room.member`` event, if any.
- {
- "context":"#example:green.example.com",
- "origin":"green.example.com",
- "pdu_id":"a4ecee13e2accdadf56c1025af232176",
- "origin_server_ts":1404838188000,
- "pdu_type":"m.room.message",
- "prev_pdus":[
- ["blue.example.com","99d16afbc8",
- {"sha256":"abase64encodedsha256hashshouldbe43byteslong"}]
- ],
- "hashes":{"sha256":"thishashcoversallfieldsincasethisisredacted"},
- "signatures":{
- "green.example.com":{
- "ed25519:key_version:":"these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus"
- }
- },
- "is_state":false,
- "content": {...}
- }
+{{definition_ss_pdu}}
-In contrast to Transactions, it is important to note that the ``prev_pdus``
-field of a PDU refers to PDUs that any origin server has sent, rather than
-previous IDs that this ``origin`` has sent. This list may refer to other PDUs
-sent by the same origin as the current one, or other origins.
+Authorization of PDUs
+~~~~~~~~~~~~~~~~~~~~~
-Because of the distributed nature of participants in a Matrix conversation, it
-is impossible to establish a globally-consistent total ordering on the events.
-However, by annotating each outbound PDU at its origin with IDs of other PDUs
-it has received, a partial ordering can be constructed allowing causality
-relationships to be preserved. A client can then display these messages to the
-end-user in some order consistent with their content and ensure that no message
-that is semantically in reply of an earlier one is ever displayed before it.
+Whenever a server receives an event from a remote server, the receiving server
+must check that the event is allowed by the authorization rules. These rules
+depend on the state of the room at that event.
-State Update PDU Fields
-~~~~~~~~~~~~~~~~~~~~~~~
+Definitions
++++++++++++
-PDUs fall into two main categories: those that deliver Events, and those that
-synchronise State. For PDUs that relate to State synchronisation, additional
-keys exist to support this:
+Required Power Level
+ A given event type has an associated *required power level*. This is given by
+ the current ``m.room.power_levels`` event. The event type is either listed
+ explicitly in the ``events`` section or given by either ``state_default`` or
+ ``events_default`` depending on if the event is a state event or not.
-======================== ============ =========================================
- Key Type Description
-======================== ============ =========================================
-``state_key`` String Combined with the ``pdu_type`` this
- identifies the which part of the room
- state is updated
-``required_power_level`` Integer The required power level needed to
- replace this update.
-``prev_state_id`` String The PDU id of the update this replaces.
-``prev_state_origin`` String The homeserver of the update this
- replaces.
-``user_id`` String The user updating the state.
-======================== ============ =========================================
+Invite Level, Kick Level, Ban Level, Redact Level
+ The levels given by the ``invite``, ``kick``, ``ban``, and ``redact``
+ properties in the current ``m.room.power_levels`` state. Each defaults to 50
+ if unspecified.
-.. code:: json
+Target User
+ For an ``m.room.member`` state event, the user given by the ``state_key`` of
+ the event.
- {...,
- "is_state":true,
- "state_key":TODO-doc
- "required_power_level":TODO-doc
- "prev_state_id":TODO-doc
- "prev_state_origin":TODO-doc
- }
+.. _`authorization rules`:
+Rules
++++++
+
+The rules governing whether an event is authorized depend solely on the
+state of the room at the point in the room graph at which the new event is to
+be inserted. The types of state events that affect authorization are:
+
+- ``m.room.create``
+- ``m.room.member``
+- ``m.room.join_rules``
+- ``m.room.power_levels``
+
+Servers should not create new events that reference unauthorized events.
+However, any event that does reference an unauthorized event is not itself
+automatically considered unauthorized.
+
+Unauthorized events that appear in the event graph do *not* have any effect on
+the state of the room.
+
+.. Note:: This is in contrast to redacted events which can still affect the
+ state of the room. For example, a redacted ``join`` event will still
+ result in the user being considered joined.
+
+The rules are as follows:
+
+1. If type is ``m.room.create``, allow if and only if it has no
+ previous events - *i.e.* it is the first event in the room.
+
+2. If type is ``m.room.member``:
+
+ a. 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 user's current membership state is ``invite`` or ``join``,
+ allow.
+
+ #. If the ``join_rule`` is ``public``, allow.
+
+ #. Otherwise, reject.
+
+ b. If ``membership`` is ``invite``:
+
+ i. 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.
+
+ c. 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.
+
+ d. 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.
+
+ e. Otherwise, the membership is unknown. Reject.
+
+3. If the ``sender``'s current membership state is not ``join``, reject.
+
+4. If the event type's *required power level* is greater than the ``sender``'s power
+ level, reject.
+
+5. If type is ``m.room.power_levels``:
+
+ a. If there is no previous ``m.room.power_levels`` event in the room, allow.
+
+ b. For each of the keys ``users_default``, ``events_default``,
+ ``state_default``, ``ban``, ``redact``, ``kick``, ``invite``, as well as
+ each entry being changed under the ``events`` or ``users`` keys:
+
+ i. If the current value is higher than the ``sender``'s current power level,
+ reject.
+
+ #. If the new value is higher than the ``sender``'s current power level,
+ reject.
+
+ c. 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.
+
+ d. Otherwise, allow.
+
+6. 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 ``sender`` of the event being redacted is the same as the
+ ``sender`` of the ``m.room.redaction``, allow.
+
+ #. Otherwise, reject.
+
+7. Otherwise, allow.
+
+.. NOTE::
+
+ Some consequences of these rules:
+
+ * Unless you are a member of the room, the only permitted operations (apart
+ from the intial create/join) are: joining a public room; accepting or
+ rejecting an invitation to a room.
+
+ * To unban somebody, you must have power level greater than or equal to both
+ the kick *and* ban levels, *and* greater than the target user's power
+ level.
+
+.. TODO-spec
+
+ I think there is some magic about 3pid invites too.
+
+Retrieving event authorization information
+++++++++++++++++++++++++++++++++++++++++++
+
+The homeserver may be missing event authorization information, or wish to check
+with other servers to ensure it is receiving the correct auth chain. These APIs
+give the homeserver an avenue for getting the information it needs.
+
+{{event_auth_ss_http_api}}
EDUs
----
EDUs, by comparison to PDUs, do not have an ID, a room ID, or a list of
-"previous" IDs. The only mandatory fields for these are the type, origin and
-destination homeserver names, and the actual nested content.
+"previous" IDs. They are intended to be non-persistent data such as user
+presence, typing notifications, etc.
-======================== ============ =========================================
- Key Type Description
-======================== ============ =========================================
-``edu_type`` String The type of the ephemeral message.
-``content`` Object Content of the ephemeral message.
-======================== ============ =========================================
+{{definition_ss_edu}}
-.. code:: json
+Room State Resolution
+---------------------
- {
- "edu_type":"m.presence",
- "origin":"blue",
- "destination":"orange",
- "content":{...}
- }
+The *state* of a room is a map of ``(event_type, state_key)`` to
+``event_id``. Each room starts with an empty state, and each state event which
+is accepted into the room updates the state of that room.
+
+Where each event has a single ``prev_event``, it is clear what the state of the
+room after each event should be. However, when two branches in the event graph
+merge, the state of those branches might differ, so a *state resolution*
+algorithm must be used to determine the resultant state.
+
+For example, consider the following event graph (where the oldest event, E0,
+is at the top)::
+
+ E0
+ |
+ E1
+ / \
+ E2 E4
+ | |
+ E3 |
+ \ /
+ E5
-Protocol URLs
--------------
+Suppose E3 and E4 are both ``m.room.name`` events which set the name of the
+room. What should the name of the room be at E5?
+
+Servers should follow the following recursively-defined algorithm to determine
+the room state at a given point on the DAG.
+
+State resolution algorithm
+~~~~~~~~~~~~~~~~~~~~~~~~~~
.. WARNING::
- This section may be misleading or inaccurate.
+ This section documents the state resolution algorithm as implemented by
+ Synapse as of December 2017 (and therefore the de-facto Matrix protocol).
+ However, this algorithm is known to have some problems.
-All these URLs are name-spaced within a prefix of::
+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:
- /_matrix/federation/v1/...
+* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
-For active pushing of messages representing live activity "as it happens"::
+* 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``.
- PUT .../send//
- Body: JSON encoding of a single Transaction
- Response: TODO-doc
+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 transaction_id path argument will override any ID given in the JSON body.
-The destination name will be set to that of the receiving server itself. Each
-embedded PDU in the transaction body will be processed.
+The *resolution* of a set of states is defined as follows. The resolved state
+is built up in a number of passes; here we use :math:`R` to refer to the
+results of the resolution so far.
+
+* Start by setting :math:`R` to the union of the states to be resolved,
+ excluding any *conflicting* events.
+
+* First we resolve conflicts between ``m.room.power_levels`` events. If there
+ is no conflict, this step is skipped, otherwise:
+
+ * Assemble all the ``m.room.power_levels`` events from the states to
+ be resolved into a list.
+
+ * Sort the list by ascending ``depth`` then descending ``sha1(event_id)``.
+
+ * Add the first event in the list to :math:`R`.
+
+ * For each subsequent event in the list, check that the event would be
+ allowed by the `authorization rules`_ for a room in state :math:`R`. If the
+ event would be allowed, then update :math:`R` with the event and continue
+ with the next event in the list. If it would not be allowed, stop and
+ continue below with ``m.room.join_rules`` events.
+
+* Repeat the above process for conflicts between ``m.room.join_rules`` events.
+
+* Repeat the above process for conflicts between ``m.room.member`` events.
+
+* No other events affect the authorization rules, so for all other conflicts,
+ just pick the event with the highest depth and lowest ``sha1(event_id)`` that
+ passes authentication in :math:`R` and add it to :math:`R`.
+
+A *conflict* occurs between states where those states have different
+``event_ids`` for the same ``(state_type, state_key)``. The events thus
+affected are said to be *conflicting* events.
-To fetch all the state of a given room::
+Backfilling and retrieving missing events
+-----------------------------------------
- GET .../state//
- Response: JSON encoding of a single Transaction containing multiple PDUs
+Once a homeserver has joined a room, it receives all the events emitted by
+other homeservers in that room, and is thus aware of the entire history of the
+room from that moment onwards. Since users in that room are able to request the
+history by the ``/messages`` client API endpoint, it's possible that they might
+step backwards far enough into history before the homeserver itself was a
+member of that room.
-Retrieves a snapshot of the entire current state of the given room. The
-response will contain a single Transaction, inside which will be a list of PDUs
-that encode the state.
+To cover this case, the federation API provides a server-to-server analog of
+the ``/messages`` client API, allowing one homeserver to fetch history from
+another. This is the ``/backfill`` API.
+To request more history, the requesting homeserver picks another homeserver
+that it thinks may have more (most likely this should be a homeserver for
+some of the existing users in the room at the earliest point in history it
+has currently), and makes a ``/backfill`` request.
-To fetch a particular event::
+Similar to backfilling a room's history, a server may not have all the events
+in the graph. That server may use the ``/get_missing_events`` API to acquire
+the events it is missing.
- GET .../event//
- Response: JSON encoding of a partial Transaction containing the event
+.. TODO-spec
+ Specify (or remark that it is unspecified) how the server handles divergent
+ history. DFS? BFS? Anything weirder?
-Retrieves a single event. The response will contain a partial Transaction,
-having just the ``origin``, ``origin_server_ts`` and ``pdus`` fields; the
-event will be encoded as the only PDU in the ``pdus`` list.
+{{backfill_ss_http_api}}
+Retrieving events
+-----------------
-To backfill events on a given room::
+In some circumstances, a homeserver may be missing a particular event or information
+about the room which cannot be easily determined from backfilling. These APIs provide
+homeservers with the option of getting events and the state of the room at a given
+point in the timeline.
- GET .../backfill//
- Query args: v, limit
- Response: JSON encoding of a single Transaction containing multiple PDUs
+{{events_ss_http_api}}
-Retrieves a sliding-window history of previous PDUs that occurred on the given
-room. Starting from the PDU ID(s) given in the "v" argument, the PDUs that
-preceded it are retrieved, up to a total number given by the "limit" argument.
-
-
-To stream events all the events::
-
- GET .../pull/
- Query args: origin, v
- Response: JSON encoding of a single Transaction consisting of multiple PDUs
-
-Retrieves all of the transactions later than any version given by the "v"
-arguments.
-
-
-To make a query::
-
- GET .../query/
- Query args: as specified by the individual query types
- Response: JSON encoding of a response object
-
-Performs a single query request on the receiving homeserver. The Query Type
-part of the path specifies the kind of query being made, and its query
-arguments have a meaning specific to that kind of query. The response is a
-JSON-encoded object whose meaning also depends on the kind of query.
-
-
-To join a room::
-
- GET .../make_join//
- Response: JSON encoding of a join proto-event
-
- PUT .../send_join//
- Response: JSON encoding of the state of the room at the time of the event
-
-Performs the room join handshake. For more information, see "Joining Rooms"
-below.
Joining Rooms
-------------
-When a new user wishes to join room that the user's homeserver already knows
+When a new user wishes to join a room that the user's homeserver already knows
about, the homeserver can immediately determine if this is allowable by
-inspecting the state of the room, and if it is acceptable, it can generate,
-sign, and emit a new ``m.room.member`` state event adding the user into that
-room. When the homeserver does not yet know about the room it cannot do this
+inspecting the state of the room. If it is acceptable, it can generate, sign,
+and emit a new ``m.room.member`` state event adding the user into that room.
+When the homeserver does not yet know about the room it cannot do this
directly. Instead, it must take a longer multi-stage handshaking process by
which it first selects a remote homeserver which is already participating in
-that room, and uses it to assist in the joining process. This is the remote
+that room, and use it to assist in the joining process. This is the remote
join handshake.
This handshake involves the homeserver of the new member wishing to join
@@ -621,308 +667,176 @@ homeservers, though most in practice will use just two.
<---------- join response
The first part of the handshake usually involves using the directory server to
-request the room ID and join candidates. This is covered in more detail on the
-directory server documentation, below. In the case of a new user joining a
-room as a result of a received invite, the joining user's homeserver could
-optimise this step away by picking the origin server of that invite message as
-the join candidate. However, the joining server should be aware that the origin
-server of the invite might since have left the room, so should be prepared to
-fall back on the regular join flow if this optimisation fails.
+request the room ID and join candidates through the |/query/directory|_
+API endpoint. In the case of a new user joining a room as a result of a received
+invite, the joining user's homeserver could optimise this step away by picking
+the origin server of that invite message as the join candidate. However, the
+joining server should be aware that the origin server of the invite might since
+have left the room, so should be prepared to fall back on the regular join flow
+if this optimisation fails.
Once the joining server has the room ID and the join candidates, it then needs
to obtain enough information about the room to fill in the required fields of
the ``m.room.member`` event. It obtains this by selecting a resident from the
-candidate list, and requesting the ``make_join`` endpoint using a ``GET``
-request, specifying the room ID and the user ID of the new member who is
-attempting to join.
+candidate list, and using the ``GET /make_join`` endpoint. The resident server
+will then reply with enough information for the joining server to fill in the
+event.
-The resident server replies to this request with a JSON-encoded object having a
-single key called ``event``; within this is an object whose fields contain some
-of the information that the joining server will need. Despite its name, this
-object is not a full event; notably it does not need to be hashed or signed by
-the resident homeserver. The required fields are:
-
-==================== ======== ============
- Key Type Description
-==================== ======== ============
-``type`` String The value ``m.room.member``
-``auth_events`` List An event-reference list containing the
- authorization events that would allow this member
- to join
-``content`` Object The event content
-``depth`` Integer (this field must be present but is ignored; it
- may be 0)
-``origin`` String The name of the resident homeserver
-``origin_server_ts`` Integer A timestamp added by the resident homeserver
-``prev_events`` List An event-reference list containing the immediate
- predecessor events
-``room_id`` String The room ID of the room
-``sender`` String The user ID of the joining member
-``state_key`` String The user ID of the joining member
-==================== ======== ============
-
-The ``content`` field itself must be an object, containing:
-
-============== ====== ============
- Key Type Description
-============== ====== ============
-``membership`` String The value ``join``
-============== ====== ============
-
-The joining server now has sufficient information to construct the real join
-event from these protoevent fields. It copies the values of most of them,
-adding (or replacing) the following fields:
-
-==================== ======= ============
- Key Type Description
-==================== ======= ============
-``event_id`` String A new event ID specified by the joining homeserver
-``origin`` String The name of the joining homeserver
-``origin_server_ts`` Integer A timestamp added by the joining homeserver
-==================== ======= ============
-
-This will be a true event, so the joining server should apply the event-signing
-algorithm to it, resulting in the addition of the ``hashes`` and ``signatures``
-fields.
+The joining server is expected to add or replace the ``origin``, ``origin_server_ts``,
+and ``event_id`` on the templated event received by the resident server. This
+event is then signed by the joining server.
To complete the join handshake, the joining server must now submit this new
-event to an resident homeserver, by using the ``send_join`` endpoint. This is
-invoked using the room ID and the event ID of the new member event.
+event to a resident homeserver, by using the ``PUT /send_join`` endpoint.
The resident homeserver then accepts this event into the room's event graph,
-and responds to the joining server with the full set of state for the newly-
-joined room. This is returned as a two-element list, whose first element is the
-integer 200, and whose second element is an object which contains the
-following keys:
+and responds to the joining server with the full set of state for the
+newly-joined room. The resident server must also send the event to other servers
+participating in the room.
-============== ===== ============
- Key Type Description
-============== ===== ============
-``auth_chain`` List A list of events giving the authorization chain for this
- join event
-``state`` List A complete list of the prevailing state events at the
- instant just before accepting the new ``m.room.member``
- event
-============== ===== ============
+{{joins_ss_http_api}}
.. TODO-spec
- (paul) I don't really understand why the full auth_chain events are given
here. What purpose does it serve expanding them out in full, when surely
they'll appear in the state anyway?
-Backfilling
------------
+Inviting to a room
+------------------
-Once a homeserver has joined a room, it receives all the events emitted by
-other homeservers in that room, and is thus aware of the entire history of the
-room from that moment onwards. Since users in that room are able to request the
-history by the ``/messages`` client API endpoint, it's possible that they might
-step backwards far enough into history before the homeserver itself was a
-member of that room.
+When a user on a given homeserver invites another user on the same homeserver,
+the homeserver may sign the membership event itself and skip the process defined
+here. However, when a user invites another user on a different homeserver, a request
+to that homeserver to have the event signed and verified must be made.
-To cover this case, the federation API provides a server-to-server analog of
-the ``/messages`` client API, allowing one homeserver to fetch history from
-another. This is the ``/backfill`` API.
+{{invites_ss_http_api}}
-To request more history, the requesting homeserver picks another homeserver
-that it thinks may have more (most likely this should be a homeserver for some
-of the existing users in the room at the earliest point in history it has
-currently), and makes a ``/backfill`` request. The parameters of this request
-give an event ID that the requesting homeserver wishes to obtain, and a number
-specifying how many more events of history before that one to return at most.
+Leaving Rooms (Rejecting Invites)
+---------------------------------
-The response to this request is an object with the following keys:
+Normally homeservers can send appropriate ``m.room.member`` events to have users
+leave the room, or to reject local invites. Remote invites from other homeservers
+do not involve the server in the graph and therefore need another approach to
+reject the invite. Joining the room and promptly leaving is not recommended as
+clients and servers will interpret that as accepting the invite, then leaving the
+room rather than rejecting the invite.
-==================== ======== ============
- Key Type Description
-==================== ======== ============
-``pdus`` List A list of events
-``origin`` String The name of the resident homeserver
-``origin_server_ts`` Integer A timestamp added by the resident homeserver
-==================== ======== ============
+Similar to the `Joining Rooms`_ handshake, the server which wishes to leave the
+room starts with sending a ``/make_leave`` request to a resident server. In the
+case of rejecting invites, the resident server may be the server which sent the
+invite. After receiving a template event from ``/make_leave``, the leaving server
+signs the event and replaces the ``event_id`` with it's own. This is then sent to
+the resident server via ``/send_leave``. The resident server will then send the
+event to other servers in the room.
-The list of events given in ``pdus`` is returned in reverse chronological
-order; having the most recent event first (i.e. the event whose event ID is
-that requested by the requestor in the ``v`` parameter).
+{{leaving_ss_http_api}}
-.. TODO-spec
- Specify (or remark that it is unspecified) how the server handles divergent
- history. DFS? BFS? Anything weirder?
+Third-party invites
+-------------------
+
+When an user wants to invite another user in a room but doesn't know the Matrix
+ID to invite, they can do so using a third-party identifier (e.g. an e-mail or a
+phone number).
+
+This identifier and its bindings to Matrix IDs are verified by an identity server
+implementing the `Identity Service API`_.
+
+Cases where an association exists for a third-party identifier
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the third-party identifier is already bound to a Matrix ID, a lookup request
+on the identity server will return it. The invite is then processed by the inviting
+homeserver as a standard ``m.room.member`` invite event. This is the simplest case.
+
+Cases where an association doesn't exist for a third-party identifier
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the third-party identifier isn't bound to any Matrix ID, the inviting
+homeserver will request the identity server to store an invite for this identifier
+and to deliver it to whoever binds it to its Matrix ID. It will also send a
+``m.room.third_party_invite`` event in the room to specify a display name, a token
+and public keys the identity server provided as a response to the invite storage
+request.
+
+When a third-party identifier with pending invites gets bound to a Matrix ID,
+the identity server will send a POST request to the ID's homeserver as described
+in the `Invitation Storage`_ section of the Identity Service API.
+
+The following process applies for each invite sent by the identity server:
+
+The invited homeserver will create a ``m.room.member`` invite event containing
+a special ``third_party_invite`` section containing the token and a signed object,
+both provided by the identity server.
+
+If the invited homeserver is in the room the invite came from, it can auth the
+event and send it.
+
+However, if the invited homeserver isn't in the room the invite came from, it
+will need to request the room's homeserver to auth the event.
+
+{{third_party_invite_ss_http_api}}
+
+Verifying the invite
+++++++++++++++++++++
+
+When a homeserver receives a ``m.room.member`` invite event for a room it's in
+with a ``third_party_invite`` object, it must verify that the association between
+the third-party identifier initially invited to the room and the Matrix ID that
+claims to be bound to it has been verified without having to rely on a third-party
+server.
+
+To do so, it will fetch from the room's state events the ``m.room.third_party_invite``
+event for which the state key matches with the value for the ``token`` key in the
+``third_party_invite`` object from the ``m.room.member`` event's content to fetch the
+public keys initially delivered by the identity server that stored the invite.
+
+It will then use these keys to verify that the ``signed`` object (in the
+``third_party_invite`` object from the ``m.room.member`` event's content) was
+signed by the same identity server.
+
+Since this ``signed`` object can only be delivered once in the POST request
+emitted by the identity server upon binding between the third-party identifier
+and the Matrix ID, and contains the invited user's Matrix ID and the token
+delivered when the invite was stored, this verification will prove that the
+``m.room.member`` invite event comes from the user owning the invited third-party
+identifier.
+
+Public Room Directory
+---------------------
+
+To compliment the `Client-Server API`_'s room directory, homeservers need a
+way to query the public rooms for another server. This can be done by making
+a request to the ``/publicRooms`` endpoint for the server the room directory
+should be retrieved for.
+
+{{public_rooms_ss_http_api}}
-Authentication
---------------
+Typing Notifications
+--------------------
-Request Authentication
-~~~~~~~~~~~~~~~~~~~~~~
+When a server's users send typing notifications, those notifications need to
+be sent to other servers in the room so their users are aware of the same
+state. Receiving servers should verify that the user is in the room, and is
+a user belonging to the sending server.
-Every HTTP request made by a homeserver is authenticated using public key
-digital signatures. The request method, target and body are signed by wrapping
-them in a JSON object and signing it using the JSON signing algorithm. The
-resulting signatures are added as an Authorization header with an auth scheme
-of X-Matrix. Note that the target field should include the full path starting with
-``/_matrix/...``, including the ``?`` and any query parameters if present, but
-should not include the leading ``https:``, nor the destination server's
-hostname.
-
-Step 1 sign JSON:
-
-.. code::
-
- {
- "method": "GET",
- "uri": "/target",
- "origin": "origin.hs.example.com",
- "destintation": "destination.hs.example.com",
- "content": { JSON content ... },
- "signatures": {
- "origin.hs.example.com": {
- "ed25519:key1": "ABCDEF..."
- }
- }
- }
-
-Step 2 add Authorization header:
-
-.. code::
-
- GET /target HTTP/1.1
- Authorization: X-Matrix origin=origin.example.com,key="ed25519:key1",sig="ABCDEF..."
- Content-Type: application/json
-
- { JSON content ... }
-
-
-Example python code:
-
-.. code:: python
-
- def authorization_headers(origin_name, origin_signing_key,
- destination_name, request_method, request_target,
- content_json=None):
- request_json = {
- "method": request_method,
- "uri": request_target,
- "origin": origin_name,
- "destination": destination_name,
- }
-
- if content_json is not None:
- request["content"] = content_json
-
- signed_json = sign_json(request_json, origin_name, origin_signing_key)
-
- authorization_headers = []
-
- for key, sig in signed_json["signatures"][origin_name].items():
- authorization_headers.append(bytes(
- "X-Matrix origin=%s,key=\"%s\",sig=\"%s\"" % (
- origin_name, key, sig,
- )
- ))
-
- return ("Authorization", authorization_headers)
-
-Response Authentication
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Responses are authenticated by the TLS server certificate. A homeserver should
-not send a request until it has authenticated the connected server to avoid
-leaking messages to eavesdroppers.
-
-Client TLS Certificates
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Requests are authenticated at the HTTP layer rather than at the TLS layer
-because HTTP services like Matrix are often deployed behind load balancers that
-handle the TLS and these load balancers make it difficult to check TLS client
-certificates.
-
-A homeserver may provide a TLS client certificate and the receiving homeserver
-may check that the client certificate matches the certificate of the origin
-homeserver.
-
-Server-Server Authorization
----------------------------
-
-.. TODO-doc
- - PDU signing (see the Event signing section earlier)
- - State conflict resolution (see below)
-
-State Conflict Resolution
--------------------------
-.. NOTE::
- This section is a work in progress.
-
-.. TODO-doc
- - How do conflicts arise (diagrams?)
- - How are they resolved (incl tie breaks)
- - How does this work with deleting current state
- - How do we reject invalid federation traffic?
-
- [[TODO(paul): At this point we should probably have a long description of how
- State management works, with descriptions of clobbering rules, power levels, etc
- etc... But some of that detail is rather up-in-the-air, on the whiteboard, and
- so on. This part needs refining. And writing in its own document as the details
- relate to the server/system as a whole, not specifically to server-server
- federation.]]
+{{definition_ss_event_schemas_m_typing}}
Presence
--------
The server API for presence is based entirely on exchange of the following
EDUs. There are no PDUs or Federation Queries involved.
-Performing a presence update and poll subscription request::
+Servers should only send presence updates for users that the receiving server
+would be interested in. This can include the receiving server sharing a room
+with a given user, or a user on the receiving server has added one of the
+sending server's users to their presence list.
- EDU type: m.presence
-
- Content keys:
- push: (optional): list of push operations.
- Each should be an object with the following keys:
- user_id: string containing a User ID
- presence: "offline"|"unavailable"|"online"|"free_for_chat"
- status_msg: (optional) string of free-form text
- last_active_ago: milliseconds since the last activity by the user
-
- poll: (optional): list of strings giving User IDs
-
- unpoll: (optional): list of strings giving User IDs
-
-The presence of this combined message is two-fold: it informs the recipient
-server of the current status of one or more users on the sending server (by the
-``push`` key), and it maintains the list of users on the recipient server that
-the sending server is interested in receiving updates for, by adding (by the
-``poll`` key) or removing them (by the ``unpoll`` key). The ``poll`` and
-``unpoll`` lists apply *changes* to the implied list of users; any existing IDs
-that the server sent as ``poll`` operations in a previous message are not
-removed until explicitly requested by a later ``unpoll``.
-
-On receipt of a message containing a non-empty ``poll`` list, the receiving
-server should immediately send the sending server a presence update EDU of its
-own, containing in a ``push`` list the current state of every user that was in
-the original EDU's ``poll`` list.
-
-Sending a presence invite::
-
- EDU type: m.presence_invite
-
- Content keys:
- observed_user: string giving the User ID of the user whose presence is
- requested (i.e. the recipient of the invite)
- observer_user: string giving the User ID of the user who is requesting to
- observe the presence (i.e. the sender of the invite)
-
-Accepting a presence invite::
-
- EDU type: m.presence_accept
-
- Content keys - as for m.presence_invite
-
-Rejecting a presence invite::
-
- EDU type: m.presence_deny
-
- Content keys - as for m.presence_invite
+Clients may define lists of users that they are interested in via "Presence
+Lists" through the `Client-Server API`_. When users are added to a presence
+list, a ``m.presence_invite`` EDU is sent to them. The user may then accept
+or deny their involvement in the list by sending either an ``m.presence_accept``
+or ``m.presence_deny`` EDU back.
.. TODO-doc
- Explain the timing-based round-trip reduction mechanism for presence
@@ -930,50 +844,51 @@ Rejecting a presence invite::
- Explain the zero-byte presence inference logic
See also: docs/client-server/model/presence
-Profiles
+{{definition_ss_event_schemas_m_presence}}
+
+{{definition_ss_event_schemas_m_presence_invite}}
+
+{{definition_ss_event_schemas_m_presence_accept}}
+
+{{definition_ss_event_schemas_m_presence_deny}}
+
+
+Receipts
--------
-The server API for profiles is based entirely on the following Federation
-Queries. There are no additional EDU or PDU types involved, other than the
-implicit ``m.presence`` and ``m.room.member`` events (see section below).
+Receipts are EDUs used to communicate a marker for a given event. Currently the
+only kind of receipt supported is a "read receipt", or where in the event graph
+the user has read up to.
-Querying profile information::
+Read receipts for events events that a user sent do not need to be sent. It is
+implied that by sending the event the user has read up to the event.
- Query type: profile
+{{definition_ss_event_schemas_m_receipt}}
- Arguments:
- user_id: the ID of the user whose profile to return
- field: (optional) string giving a field name
+Querying for information
+------------------------
- Returns: JSON object containing the following keys:
- displayname: string of free-form text
- avatar_url: string containing an HTTP-scheme URL
+Queries are a way to retrieve information from a homeserver about a resource,
+such as a user or room. The endpoints here are often called in conjunction with
+a request from a client on the client-server API in order to complete the call.
-If the query contains the optional ``field`` key, it should give the name of a
-result field. If such is present, then the result should contain only a field
-of that name, with no others present. If not, the result should contain as much
-of the user's profile as the homeserver has available and can make public.
+There are several types of queries that can be made. The generic endpoint to
+represent all queries is described first, followed by the more specific queries
+that can be made.
-Directory
----------
+{{query_ss_http_api}}
-The server API for directory queries is also based on Federation Queries.
+OpenID
+------
-Querying directory information::
+Third party services can exchange an access token previously generated by the
+`Client-Server API` for information about a user. This can help verify that a
+user is who they say they are without granting full access to the user's account.
- Query type: directory
+Access tokens generated by the OpenID API are only good for the OpenID API and
+nothing else.
- Arguments:
- room_alias: the room alias to query
-
- Returns: JSON object containing the following keys:
- room_id: string giving the underlying room ID the alias maps to
- servers: list of strings giving the join candidates
-
-The list of join candidates is a list of server names that are likely to hold
-the given room; these are servers that the requesting server may wish to use as
-resident servers as part of the remote join handshake. This list may or may not
-include the server answering the query.
+{{openid_ss_http_api}}
Send-to-device messaging
------------------------
@@ -997,7 +912,47 @@ the following EDU::
messages: The messages to send. A map from user ID, to a map from device ID
to message body. The device ID may also be *, meaning all known devices
- for the user.
+ for the user
+
+
+Content Repository
+------------------
+
+Attachments to events (images, files, etc) are uploaded to a homeserver via the
+Content Repository described in the `Client-Server API`_. When a server wishes
+to serve content originating from a remote server, it needs to ask the remote
+server for the media.
+
+Servers should use the server described in the Matrix Content URI, which has the
+format ``mxc://{ServerName}/{MediaID}``. Servers should use the download endpoint
+described in the `Client-Server API`_, being sure to use the ``allow_remote``
+parameter (set to ``false``).
+
+
+Server Access Control Lists (ACLs)
+----------------------------------
+
+Server ACLs and their purpose are described in the `Server ACLs`_ section of the
+Client-Server API.
+
+When a remote server makes a request, it MUST be verified to be allowed by the
+server ACLs. If the server is denied access to a room, the receiving server
+MUST reply with a 403 HTTP status code and an ``errcode`` of ``M_FORBIDDEN``.
+
+The following endpoint prefixes MUST be protected:
+
+* ``/_matrix/federation/v1/send`` (on a per-PDU basis)
+* ``/_matrix/federation/v1/make_join``
+* ``/_matrix/federation/v1/make_leave``
+* ``/_matrix/federation/v1/send_join``
+* ``/_matrix/federation/v1/send_leave``
+* ``/_matrix/federation/v1/invite``
+* ``/_matrix/federation/v1/state``
+* ``/_matrix/federation/v1/state_ids``
+* ``/_matrix/federation/v1/backfill``
+* ``/_matrix/federation/v1/event_auth``
+* ``/_matrix/federation/v1/query_auth``
+* ``/_matrix/federation/v1/get_missing_events``
Signing Events
@@ -1129,7 +1084,7 @@ Servers can then transmit the entire event or the event with the non-essential
keys removed. If the entire event is present, receiving servers can then check
the event by computing the SHA-256 of the event, excluding the ``hash`` object.
If the keys have been redacted, then the ``hash`` object is included when
-calculating the SHA-256 instead.
+calculating the SHA-256 hash instead.
New hash functions can be introduced by adding additional keys to the ``hash``
object. Since the ``hash`` object cannot be redacted a server shouldn't allow
@@ -1143,4 +1098,12 @@ that are too long.
[[TODO(markjh) We might want to allow the server to omit the output of well
known hash functions like SHA-256 when none of the keys have been redacted]]
+.. |/query/directory| replace:: ``/query/directory``
+.. _/query/directory: #get-matrix-federation-v1-query-directory
+
+.. _`Invitation storage`: ../identity_service/unstable.html#invitation-storage
+.. _`Identity Service API`: ../identity_service/unstable.html
+.. _`Client-Server API`: ../client_server/unstable.html
+.. _`Inviting to a room`: #inviting-to-a-room
.. _`Canonical JSON`: ../appendices.html#canonical-json
+.. _`Unpadded Base64`: ../appendices.html#unpadded-base64
diff --git a/specification/targets.yaml b/specification/targets.yaml
index 7d415fb4..acf4b6ac 100644
--- a/specification/targets.yaml
+++ b/specification/targets.yaml
@@ -2,9 +2,6 @@ targets:
index:
files:
- index.rst
- intro:
- files:
- - intro.rst
client_server:
files:
- client_server_api.rst
@@ -32,9 +29,16 @@ targets:
appendices:
files:
- appendices.rst
+ - appendices/base64.rst
- appendices/signing_json.rst
+ - appendices/identifier_grammar.rst
+ - appendices/threepids.rst
- appendices/threat_model.rst
- appendices/test_vectors.rst
+ proposals:
+ files:
+ - proposals_intro.rst
+ - proposals.rst
groups: # reusable blobs of files when prefixed with 'group:'
modules:
- modules/instant_messaging.rst
@@ -58,6 +62,12 @@ groups: # reusable blobs of files when prefixed with 'group:'
- modules/event_context.rst
- modules/cas_login.rst
- modules/dm.rst
+ - modules/ignore_users.rst
+ - modules/stickers.rst
+ - modules/report_content.rst
+ - modules/third_party_networks.rst
+ - modules/openid.rst
+ - modules/server_acls.rst
title_styles: ["=", "-", "~", "+", "^", "`", "@", ":"]
diff --git a/supporting-docs/examples/application-services.rst b/supporting-docs/examples/application-services.rst
deleted file mode 100644
index fae3613d..00000000
--- a/supporting-docs/examples/application-services.rst
+++ /dev/null
@@ -1,129 +0,0 @@
-Application Services
-====================
-
-This file contains examples of some application service
-
-IRC Bridge
-----------
-Pre-conditions:
- - Server admin stores the AS token "T_a" on the homeserver.
- - Homeserver has a token "T_h".
- - Homeserver has the domain "hsdomain.com"
-
-1. Application service registration
-
-::
-
- AS -> HS: Registers itself with the homeserver
- POST /register
- {
- url: "https://someapp.com/matrix",
- as_token: "T_a",
- namespaces: {
- users: [
- {
- "exclusive": true,
- "regex": "@irc\.freenode\.net/.*"
- }
- ],
- aliases: [
- {
- "exclusive": true,
- "regex": "#irc\.freenode\.net/.*"
- }
- ]
- }
- }
-
- Returns 200 OK:
- {
- hs_token: "T_h"
- }
-
-2. IRC user "Bob" says "hello?" on "#matrix" at timestamp 1421416883133:
-
-::
-
- - AS stores message as potential scrollback.
- - Nothing happens as no Matrix users are in the room.
-
-3. Matrix user "@alice:hsdomain.com" wants to join "#matrix":
-
-::
-
- User -> HS: Request to join "#irc.freenode.net/#matrix:hsdomain.com"
-
- HS -> AS: Room Query "#irc.freenode.net/#matrix:hsdomain.com"
- GET /rooms/%23irc.freenode.net%2F%23matrix%3Ahsdomain.com?access_token=T_h
- [Starts blocking]
- AS -> HS: Creates room. Gets room ID "!aasaasasa:hsdomain.com".
- AS -> HS: Sets room name to "#matrix".
- AS -> HS: Sends message as ""@irc.freenode.net/Bob:hsdomain.com"
- PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
- ?access_token=T_a
- &user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
- &ts=1421416883133
- {
- body: "hello?"
- msgtype: "m.text"
- }
- HS -> AS: User Query "@irc.freenode.net/Bob:hsdomain.com"
- GET /users/%40irc.freenode.net%2FBob%3Ahsdomain.com?access_token=T_h
- [Starts blocking]
- AS -> HS: Creates user using CS API extension.
- POST /register?access_token=T_a
- {
- type: "m.login.application_service",
- user: "irc.freenode.net/Bob"
- }
- AS -> HS: Set user display name to "Bob".
- [Finishes blocking]
- [Finished blocking]
-
- - HS sends room information back to client.
-
-4. @alice:hsdomain.com says "hi!" in this room:
-
-::
-
- User -> HS: Send message "hi!" in room !aasaasasa:hsdomain.com
-
- - HS sends message.
- - HS sees the room ID is in the AS namespace and pushes it to the AS.
-
- HS -> AS: Push event
- PUT /transactions/1?access_token=T_h
- {
- events: [
- {
- content: {
- body: "hi!",
- msgtype: "m.text"
- },
- origin_server_ts: ,
- user_id: "@alice:hsdomain.com",
- room_id: "!aasaasasa:hsdomain.com",
- type: "m.room.message"
- }
- ]
- }
-
- - AS passes this through to IRC.
-
-
-5. IRC user "Bob" says "what's up?" on "#matrix" at timestamp 1421418084816:
-
-::
-
- IRC -> AS: "what's up?"
- AS -> HS: Send message via CS API extension
- PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
- ?access_token=T_a
- &user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
- &ts=1421418084816
- {
- body: "what's up?"
- msgtype: "m.text"
- }
-
- - HS modifies the user_id and origin_server_ts on the event and sends it.
diff --git a/supporting-docs/guides/2015-08-14-getting_involved.md b/supporting-docs/guides/2015-08-14-getting_involved.md
deleted file mode 100644
index ff6637c7..00000000
--- a/supporting-docs/guides/2015-08-14-getting_involved.md
+++ /dev/null
@@ -1,141 +0,0 @@
----
-layout: post
-title: Getting involved
-categories: guides
----
-
-# How can I get involved?
-Matrix is an ecosystem consisting of several apps written by lots of people. We at Matrix.org have written one server and a few clients, and people in the community have also written several clients, servers, and Application Services. We are collecting [a list of all known Matrix-apps](https://matrix.org/blog/try-matrix-now/).
-
-|
-
-You have a few options when it comes to getting involved: if you just want to use Matrix, you can [register an account on a public server using a public webclient](#reg). If you have a virtual private server (VPS) or similar, you might want to [run a server and/or client yourself](#run). If you want to look under the hood, you can [checkout the code and modify it - or write your own client or server](#checkout). Or you can write an [Application Service](#as), for example a bridge to an existing ecosystem.
-
-|
-
-We very much welcome [contributions](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst) to any of our projects, which you can find in our [github space](https://github.com/matrix-org/).
-
-|
-
-
-
-## Access Matrix via a public webclient/server
-
-The easiest thing to do if you want to just have a play, is to use the [Riot.im
-webclient](https://riot.im). You can use it as a guest, or register for an
-account. For details of other clients, see
-[https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now).
-
-
-
-## Run a server and/or client yourself
-
-You can clone our open source projects and run clients and servers yourself. Here is how:
-
-### Running your own client:
-
-You can run your own Matrix client; there are [numerous clients
-available](https://matrix.org/blog/try-matrix-now/). You can easily [run your
-own copy](https://github.com/vector-im/vector-web#getting-started) of the
-Riot.im web client.
-
-### Running your own homeserver:
-
-One of the core features of Matrix is that anyone can run a homeserver and join the federated network on equal terms (there is no hierarchy). If you want to set up your own homeserver, please see the relevant docs of the server you want to run. If you want to run Matrix.org's reference homeserver, please consult the [readme of the Synapse project](https://github.com/matrix-org/synapse/blob/master/README.rst).
-
-|
-
-Note that Synapse comes with a bundled Matrix.org webclient - but you can tell it to use your [development checkout snapshot instead](https://github.com/matrix-org/matrix-angular-sdk#matrix-angular-sdk) (or to not run a webclient at all via the "web_client: false" config option).
-
-|
-
-
-
-## Checkout our code - or write your own
-
-As described above, you can clone our code and [run a server and/or client yourself](#run). Infact, all the code that we at Matrix.org write is available from [our github](http://github.com/matrix-org) - and other servers and clients may also be open sourced - see [our list of all known Matrix-apps](https://matrix.org/blog/try-matrix-now/).
-
-|
-
-You can also implement your own client or server - after all, Matrix is at its core "just" a specification of a protocol.
-
-|
-
-### Write your own client:
-
-The [client-server API
-spec](http://matrix.org/docs/spec/client_server/latest.html) describes what API
-calls are available to clients, and there is a [HOWTO
-guide](http://matrix.org/docs/guides/client-server.html) which provides an
-introduction to using the API along with some common operations. A quick
-step-by-step guide would include:
-
-|
-
-1. Get a user either by registering your user in an existing client or running the [new-user script](https://github.com/matrix-org/synapse/blob/master/scripts/register_new_matrix_user) if you are running your own Synapse homeserver.
-
-2. Assuming the homeserver you are using allows logins by password, log in via the login API:
-
- ```
- curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/login"
- ```
-
-3. If successful, this returns the following, including an `access_token`:
-
- {
- "access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
- "home_server": "localhost",
- "user_id": "@example:localhost"
- }
-
-4. This ``access_token`` will be used for authentication for the rest of your API calls. Potentially the next step you want is to make a call to the sync API and get the last few events from each room your user is in:
-
- ```
- curl -XGET "http://localhost:8008/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN"
- ```
-
-5. The above will return something like this:
-
- {
- "next_batch": "s72595_4483_1934",
- "rooms": {
- "join": {
- "!726s6s6q:example.com": {
- "state": {
- "events": [
- ...
- ]
- },
- "timeline": {
- "events": [
- ...
- ]
- }
- },
- ...
- }
- }
- }
-
-
- You can then use the "next_batch" token to start listening for new events like so:
-
- ```
- curl -XGET "http://localhost:8008/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN&since=s72595_4483_1934"
- ```
-
-6. This request will block waiting for an incoming event, timing out after several seconds if there is no event. This ensures that you only get new events. Now you have the initial room states, and a stream of events - a good client should be able to process all these events and present them to the user. And potentially you might want to add functionality to generate events as well (such as messages from the user, for example)!
-
-### Write your own server:
-
-We are still working on the server-server spec, so the best thing to do if you are interested in writing a server, is to come talk to us in [#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org).
-
-If you are interested in how federation works, please see the [Server-Server API spec](http://matrix.org/docs/spec/server_server/unstable.html).
-
-|
-
-
-
-## Write an Application Service:
-
-Information about Application services and how they can be used can be found in the [Application services](./application_services.html) document! (This is based on Kegan's excellent blog posts, but now lives here so it can be kept up-to-date!)
diff --git a/supporting-docs/guides/2015-08-19-faq.md b/supporting-docs/guides/2015-08-19-faq.md
deleted file mode 100644
index d0c7f8b3..00000000
--- a/supporting-docs/guides/2015-08-19-faq.md
+++ /dev/null
@@ -1,657 +0,0 @@
----
-layout: post
-title: FAQ
-date: 2015-08-19 16:58:07
-categories: guides
----
-
-
-# FAQ
-{:.no_toc}
-
-Categories
-----------
-{:.no_toc}
-
-[General](#general)
-
-[Quick Start](#quick-start)
-
-[Standard](#standard)
-
-[Servers](#servers)
-
-[Clients](#clients)
-
-|
-
-FAQ Content
------------
-{:.no_toc}
-
-
-* TOC
-{:toc .toc}
-
-### General
-
-##### What is Matrix?
-
-Matrix is an open standard for interoperable, decentralised,
-real-time communication over IP. It can be used to power Instant
-Messaging, VoIP/WebRTC signalling, Internet of Things communication - or anywhere
-you need a standard HTTP API for publishing and subscribing to
-data whilst tracking the conversation history.
-
-|
-
-Matrix defines the standard, and provides open source reference implementations
-of Matrix-compatible Servers, Clients, Client SDKs and Application Services
-to help you create new communication solutions or extend the capabilities
-and reach of existing ones.
-
-##### What is Matrix's Mission?
-
-Matrix's initial goal is to fix the problem of fragmented IP communications:
-letting users message and call each other without having to care what app
-the other user is on - making it as easy as sending an email.
-
-|
-
-The longer term goal is for Matrix to act as a generic HTTP messaging and data
-synchronisation system for the whole web - allowing people, services and devices
-to easily communicate with each other, empowering users to own and control their
-data and select the services and vendors they want to use.
-
-##### What does Matrix provide?
-
-Matrix provides:
-
-- [Open Standard](/docs/spec) HTTP APIs for transferring JSON messages (e.g. instant messages, WebRTC signalling), including:
- - [Client\<-\>Server API](/docs/spec#client-server-api-v1) - defines how Matrix compatible clients communicate with Matrix homeservers.
- - [Server\<-\>Server API](/docs/spec#federation-api) - defines how Matrix homeservers exchange messages and synchronise history with each other.
- - [Application Service API](/docs/spec/#application-service-api) - defines how to extend the functionality of Matrix with 'integrations' and bridge to other networks.
- - [Modules](/docs/spec/#modules) - specifies features that must be implemented by particular classes of clients.
-- Open source reference implementations of:
- - Clients (Web (React), iOS, Android)
- - Client SDKs (Javascript, Web (React), iOS, Android)
- - Homeservers (Synapse)
- - Application Services (bridges to IRC, Slack, Skype, Lync and more...)
-- The actual ecosystem and community of everyone running Matrix servers and services
-- Loads of 3rd party contributions of clients, SDKs, servers and services.
-
-You can find the full list of Matrix enabled projects at [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now).
-
-##### What does this mean for users?
-
-The aim is to provide an analogous ecosystem to email - one where you
-can communicate with pretty much anyone, without caring what app or
-server they are using, using whichever app & server you chose to use,
-and use a neutral identity system like an e-mail address or phone
-number to discover people to talk to.
-
-##### What kind of company is Matrix.org?
-
-Matrix is an open initiative which acts as a neutral custodian of the
-Matrix standard. It's not actually incorporated anywhere at the moment
-but we are looking at the best legal structure for the future (and as
-of October 2015 we have hopefully found one). Whatever the legal
-structure, we are committed to keeping the Matrix project open.
-
-##### Who is funding Matrix.org?
-
-Most of the current core contributors to Matrix work at
-[Amdocs](http://amdocs.com), who have kindly given us permission to work
-on Matrix as an independent non-profit initiative. Other contributors
-are funded by their own employers or donate their own time to the project.
-
-##### Who is building Matrix?
-
-The core team is ~10 people with extensive experience in building custom
-VoIP and Messaging apps for mobile network operators. Most of us have
-day jobs at [Amdocs](http://amdocs.com) or [OpenMarket](http://openmarket.com),
-but there are an increasing number of contributors from other companies and
-folks all over the internet.
-
-##### Why are you called Matrix?
-
-We are called Matrix because we provide a structure in which all
-communication can be matrixed together.
-
-|
-
-No, it's nothing to do with the film (although you could go and build virtual
-worlds on top of Matrix if you wanted :)
-
-##### Why have you released this as open source?
-
-We believe that any open standard defining interoperable communication
-needs to be justified, demonstrated and validated with transparent open
-source implementations. For Matrix to achieve its mission of making all
-communications services interoperable we believe it needs to be truly
-open, giving people access to take all the code we produce and to use
-and build on top of it.
-
-##### What do you mean by open?
-
-Matrix is an open standard, meaning that we have freely published the
-details for how to communicate interoperably using the Matrix set of
-HTTP APIs. We encourage anyone and everyone to use the APIs and build
-their own projects which implement them and so benefit from
-interoperability with the rest of the Matrix ecosystem. We also
-ensure the standard is not encumbered by any known patent licensing
-requirements.
-
-|
-
-Matrix is also open source, meaning that we have released the source
-code of the reference servers, clients and services to the public domain
-under the [Apache Licence v2](http://www.apache.org/licenses/LICENSE-2.0.html), to
-encourage anyone and everyone to run their own servers and clients, and
-enhance them and contribute their enhancements as they see fit.
-
-##### What does federated mean?
-
-Federation allows separate deployments of a communication service to
-communicate with each other - for instance a mail server run by Google
-federates with a mail server run by Microsoft when you send email from
-@gmail.com to @hotmail.com.
-
-|
-
-Federation is different to interoperability, as interoperable clients
-may simply be running on the same deployment - whereas in federation the
-deployments themselves are exchanging data in a compatible manner.
-
-|
-
-Matrix provides open federation - meaning that anyone on the internet
-can join into the Matrix ecosystem by deploying their own server.
-
-##### How is this like e-mail?
-
-When email was first set up in the early ‘80s, companies like Compuserve
-and AT&T and Sprint set up isolated email communities which only allowed
-you to exchange mail with users on the same system. If you got your
-email from one service and your friend from another, then you couldn't
-message each other. This is basically the situation we're in today with
-VoIP and IM.
-
-##### Why has no-one done this before?
-
-There have been several attempts before including SIP, XMPP and RCS.
- All of these have had some level of success, but many different
-technological/usability/economic factors have ended up limiting their
-success. Unfortunately, we've not ended up in a world where everyone
-has a SIP URI or Jabber ID on their business card, or a phone that
-actually uses RCS.
-
-##### What is the difference between Matrix and IRC?
-
-We love IRC. In fact, as of today the core Matrix team still uses it as
-our primary communication tool. Between us we've written IRCds, IRC bots
-and admined dreamforge, UnrealIRCd, epona, ircservices and several
-others. That said, it has some limitations that Matrix seeks to improve
-on:
-
-- Text only
-- No history
-- No multiple-device support
-- No presence support
-- Fragmented identity model
-- No open federation
-- No standard APIs, just a rather limited TCP line protocol
-- Non-standardised federation protocol
-- No built-in end-to-end encryption
-- Disruptive net-splits
-- Non-extensible
-
-[IRCv3](http://ircv3.net) exists and is addressing some of these issues;
-this is great news and we wish them well. It's almost a contradiction
-in terms to get competitive between openly interoperable communication
-projects - we look forward to increasing the richness of Matrix\<-\>IRC
-bridges as the project progresses.
-
-##### What is the difference between Matrix and XMPP?
-
-The Matrix team used XMPP (Openfire, ejabberd, spectrum, asmack,
-XMPPFramework) for IM before starting to experiment with open HTTP APIs
-as an alternative in around 2012. The main issues with XMPP that
-drove us in this direction were:
-
-- Not particularly web-friendly - you can't easily speak XMPP from a
- web browser. (N.B. Nowadays you have options like XMPP-FTW and
- Stanza.io that help loads with letting browsers talk XMPP).
-- Single logical server per MUC is a single point of control and
- availability. (MUCs can be distributed over multiple physical
- servers, but they still sit behind a single logical JID and domain.
- FMUC improves this with a similar approach to Matrix, but as of Oct
- 2015 there are no open source implementations.)
-- History synchronisation is very much a second class citizen feature
-- Bridging to other protocols and defragmenting existing communication
- apps and networks is very much a second class citizen feature
-- Stanzas aren't framed or reliably delivered without extensions. (See
- [wiki.xmpp.org](http://wiki.xmpp.org/web/Myths#Myth_Four:_XMPP_is_unreliable_without_a_bunch_of_extensions.)
- for an XMPP take on this)
-- Multiple device support is limited. (Apparently Carbons and MAM help
- with this)
-- Baseline feature set is so minimal that fragmentation of features
- between clients and servers is common, especially as interoperability
- profiles for features have fallen behind (as of July 2015)
-- No strong identity system (i.e. no standard E2E PKI, unless you
- count X.509 certs, which [are
- questionable](http://www.thoughtcrime.org/blog/ssl-and-the-future-of-authenticity/))
-- Not particularly well designed for mobile use cases: push;
- bandwidth-efficient transports. (Since the time of writing a [Push
- XEP has appeared](http://xmpp.org/extensions/xep-0357.html), and
- [wiki.xmpp.org](http://wiki.xmpp.org/web/Myths#Myth_Three:_It.27s_too_bandwidth-inefficient_for_mobile.)
- claims that XMPP runs "fine" over a 9600bps + 30s latency link.)
-
-The whole subject of XMPP vs Matrix seems to bring out the worst in
-people. Rather than fighting over which open interoperable communication
-standard works the best, we should just collaborate and bridge everything
-together. The more federation and interoperability the better.
-
-|
-
-We think of Matrix and XMPP as being quite different; at its core
-Matrix can be thought of as an eventually consistent global JSON db with
-an HTTP API and pubsub semantics - whilst XMPP can be thought of as a
-message passing protocol. You can use them both to build chat systems;
-you can use them both to build pubsub systems; each comes with different
-tradeoffs. Matrix has a deliberately extensive 'kitchen sink' baseline
-of functionality; XMPP has a deliberately minimal baseline set of
-functionality. If XMPP does what you need it to do, then we're genuinely
-happy for you :) Meanwhile, rather than competing, an XMPP Bridge like
-[Skaverat's xmpptrix beta](https://github.com/SkaveRat/xmpptrix) or
-[jfred's matrix-xmpp-bridge](https://github.com/jfrederickson/matrix-xmpp-bridge)
-or Matrix.org's own [purple-matrix](https://github.com/matrix-org/purple-matrix/)
-has potential to let both environments coexist and make the most of each
-other's benefits.
-
-##### What is the difference between Matrix and PSYC?
-
-PSYC is a open federated messaging protocol loosely inspired by IRC. In
-version 1 it was a standalone protocol, and in version 2 it is being
-reutilised as a messaging layer on top of GNUnet. We honestly don't
-know that much about it, beyond trying to use psycd as an XMPP\<-\>IRC
-bridge in 2010. Matrix differentiates primarily by providing simple HTTP
-APIs rather than the more exotic compact line protocol in PSYC v1 or the
-comprehensive GNUnet stack in v2, and Matrix focuses more on decentralised
-conversation history rather than just decentralised chat servers.
-On the other hand, Matrix doesn't provide the metadata protection
-guarantees that GNUnet/PSYC aims for.
-
-|
-
-See [http://about.psyc.eu/Matrix](http://about.psyc.eu/Matrix) for
-PSYC's views on Matrix.
-
-##### What is the difference between Matrix and Tox?
-
-Tox.im looks to be a very cool clone of Skype - a fully decentralised
-peer-to-peer network. Matrix is deliberately not a 'pure' peer-to-peer
-system; instead each user has a well-defined homeserver which stores
-his data and that he can depend upon. Matrix provides HTTP APIs;
-Tox.im provides C APIs. As of October 2015 Tox doesn't seem to have an
-answer yet for decentralised conversation history storage.
-
-##### How does Matrix compare with something like Trillian or Pidgin?
-
-Trillian and Pidgin and similar aggregating IM clients merge all your IM
-activity into a single app. However, your history and
-identity is still fragmented across the networks. People can't find you
-easily, and your history is fragmented (other than on the device
-where the client runs). And rather than being able to chose the right
-app for the job when communicating with people, you are pushed towards
-relying on a specific aggregation app.
-
-Matrix lets you get the best of both worlds by linking to all the
-different networks (XMPP, AIM, ICQ, Lync, Skype etc) on the serverside,
-using bridges which can be run by anyone. Matrix then provides a simple
-standard HTTP API to access any of these networks, and lets you choose
-whichever client you prefer (either as a 'native' Matrix client or using
-a non-Matrix client from one of the networks which has been bridged in).
-
-##### What Matrix compliant apps are there?
-
-Quite a few, ranging from the glossy mass-market to the geeky command-line. There's even an emacs macro. Check out [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now) for the current
-list of Matrix enabled projects.
-
-##### What bridges to other networks are available?
-
-The number of 'bridges' which integrate existing communication networks into
-Matrix are growing on a daily basis - both written by the Matrix core team
-and contributed by the wider community. The full list can be seen at
-[https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now), but the core ones as of Oct 2015 include:
-
- * [matrix-appservice-irc](https://github.com/matrix-org/matrix-appservice-irc) - an increasingly comprehensive Matrix\<-\>IRC bridge
- * [matrix-appservice-verto](https://github.com/matrix-org/matrix-appservice-verto) - links from Matrix to FreeSWITCH via the Verto protocol
- * [matrix-appservice-slack](https://github.com/matrix-org/matrix-appservice-slack) - a basic bridge to Slack
- * [node-purple](https://github.com/matrix-org/node-purple) - lets you access any of the 20+ protocols supported by
- [libpurple](https://developer.pidgin.im/wiki/WhatIsLibpurple), including
- Skype, Lync, XMPP, etc)
- * [matrix-appservice-bridge](https://github.com/matrix-org/matrix-appservice-bridge) - a general NodeJS framework for writing bridges
-
-Writing new bridges is incredibly fun and easy - see the [matrix-appservice-bridge HOWTO](https://github.com/matrix-org/matrix-appservice-bridge/blob/master/HOWTO.md)
-for an example of how to write a fully functional Slack bridge in less than 100 lines of code!
-
-##### Why do you think existing apps will ever join this officially?
-
-We firmly believe it is what is right for the consumer. As people begin
-to use interoperable communications tools, service providers will see the
-benefit and compete on quality of service, security and features rather
-than relying on locking people into their walled garden. We believe as
-soon as users see the availability and benefits of interoperable
-services they will demand it.
-
-##### Why aren't you doing this through the IETF? or W3C? or 3GPP?
-
-We do recognise the advantages of working with existing standards
-bodies. We have been focused on writing code and getting it out, and the standard has been evolving rapidly since initial release in September 2014.
-Once the standard has matured sufficiently it may well be appropriate to work with an official
-standard body to maintain it going forwards.
-
-### Quick Start
-
-##### How do I get an account and get started?
-
-The quickest way is to pick a client from [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now) and sign up.
-Please note that you can point clients to access any homeserver - you don't have to use matrix.org,
-although as of day 1, matrix.org is the only communal homeserver
-available.
-
-##### What can I actually do with this?
-
-A typical client provides a simple chatroom interface to Matrix -
-letting the user interact with users and rooms anywhere within the
-Matrix federation. Text and image messages are supported, and basic
-voice-only VoIP calling via WebRTC is supported in one-to-one rooms.
-(As of October 2015, experimental multi-way calling is also available
-on Riot.im).
-
-##### How do I connect my homeserver to the public Matrix network?
-
-See
-[http://github.com/matrix-org/synapse](http://github.com/matrix-org/synapse)
-for details
-
-##### How do I Matrix-enable my existing app?
-
-If your app doesn't have any communication capability already, you'll want
-to use one of the Matrix client SDKs to add it in. These come in different
-levels of sophistication - ranging from a simple HTTP API wrapper (like matrix-js-sdk, matrix-ios-sdk or matrix-android-sdk)
-through to reusable UI components (like matrix-react-sdk and matrix-ios-kit). Pick
-the one for your platform, or a 3rd party one if none of the above work for you,
-and get plugging it in. You'll probably also want to read the [Client-Server API
-HOWTO](http://matrix.org/docs/howtos/client-server.html) too.
-
-If you already have communication infrastructure set up (XMPP, custom HTTP, or whatever),
-then you'll want to run a bridge to expose it to the wider Matrix ecosystem.
-See [matrix-appservice-bridge HOWTO](https://github.com/matrix-org/matrix-appservice-bridge/blob/master/HOWTO.md) for a
-guide of how to write bridges using the matrix-appservice-bridge framework, or co-opt one
-from the list at [https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now).
-[Application Service API](/docs/spec/#application-service-api) gives the details of the API
-that bridges have to implement.
-
-##### How can I write a client on Matrix?
-
-See the [Client-Server API
-HOWTO](http://matrix.org/docs/howtos/client-server.html) and the [API
-docs](/docs/api) and [the Spec](/docs/spec) for all the details you need
-to write a client.
-
-##### How can I help out with this?
-
-Come say hi on [\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org)! Install synapse and tell us how you get on. Critique the spec. Write
-clients. Write bridges! Run bridges! Nose around in [Jira](https://matrix.org/jira) and
-send us some pull requests on github to fix some bugs or add some features! You could even
-try to write a homeserver (but be warned, Matrix's architecture makes homeservers orders of
-magnitude harder than clients or bridges.)
-
-See [CONTRIBUTING.rst](http://github.com/matrix-org/synapse/tree/master/CONTRIBUTING.rst) for
-full details on how to contribute to the project. All are welcome!
-
-##### Where can I get support?
-
-[\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org) aka \#matrix on irc.freenode.is your best bet.
-
-##### How do I register custom matrix event types?
-
-We're not yet managing a registry of custom matrix event types. If you
-have any particularly good ones you want to tell the world about, please
-let us know on #matrix-dev:matrix.org.
-
-##### How mature is this?
-
-We started working on Matrix in July 2014, and opened it to the
-public in September 2014. We got all the core features in place in December 2014
-and entered beta, and since then have been iterating away on the beta refining the
-architecture and APIs, fixing bugs and scalability, and adding new features, clients,
-bridges etc.
-
-As of October 2015 (synapse 0.10) it's good for serious experimentation and
-non-production services and can absolutely be used in the real world. However, we're
-still in beta and we'll want to freeze the spec and implement clustering and other
-nice features before we really declare it ready for production.
-
-### Standard
-
-##### What is a client?
-
-Users in Matrix use one or more clients to communicate. This could be any combination of a web client, a command line client, a mobile client - or embedded clients built into existing apps. It could even be a piece of hardware (e.g. a drone) that is Matrix enabled.
-
-##### Can I use Matrix without installing a Matrix client?
-
-Sure. An ever increasing number of protocols are being bridged into Matrix, so if you use something like IRC on Freenode you may well be indirectly benefiting from Matrix, as others may be connected into the IRC channel via Matrix.
-
-##### What is a home server?
-
-A user's clients connect to a single homeserver, which stores the communication history and account information for that user, and shares data with the wider Matrix ecosystem by synchronising communication history with other homeservers.
-
-##### What is a MXID?
-
-Matrix user IDs (MXID) are unique user IDs. They are in the format ```@username:homeserver.tld``` (this format is used to avoid confusing them with email addresses). They are intended to be fairly hidden (although right now they are not) - instead you will find and identify other users via 3PIDs.
-
-##### What is a 3PID?
-
-Third-party IDs (3PIDs) are IDs from other systems or contexts, such as email addresses, social network accounts and phone numbers.
-
-##### What is an identity server?
-
-Users in Matrix are identified internally via their matrix user ID (MXID). However, existing 3rd party ID (3PID) namespaces such as email addresses or phone numbers should be used publically to identify Matrix users, at least for invitation purposes. 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) 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 MXIDs 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.
-
-|
-
-The precise architecture of identity servers is currently in flux and subject to change as we work to fully decentralise them.
-
-##### Where do my conversations get stored?
-
-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*.
-
-##### What are 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, essentially stripping the event of all keys that are not required by the protocol. Redacting an event cannot be undone, allowing server owners to also delete the offending content from the databases.
-
-##### How do you do VoIP calls on Matrix?
-
-Voice (and video) over Matrix uses the WebRTC 1.0 standard to transfer call media (i.e. the actual voice and video traffic). Matrix is used to signal the establishment and termination of the call by sending call events, like any other event. Currently calls are only supported in rooms with exactly two participants - however, one of those participants may be a conferencing bridge. We're looking at better ways to do group calling.
-
-##### Can I log into other homeservers with my username and password?
-
-Currently, no. We are looking at options for decentralising or migrating user accounts between multiple servers, and might add this feature at a later stage.
-
-##### Why Apache Licence?
-
-The Apache Licence is a permissive licence. We want the Matrix protocol itself to be free and open, but people are free to create both free and commercial apps and services that uses the protocol. In our opinion, any Matrix-service only enhances the Matrix ecosystem.
-
-##### Can I write a Matrix homeserver?
-
-Yes. Matrix is just a spec, so implementations of the spec are very welcome! It should be noted that as of October 2015, changes are still being made to the spec, so if you want to write a Matrix homeserver, it is strongly recommended that you chat to the Matrix.org devs in [\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org) first! You can also read about the [Federation API here](https://matrix.org/docs/spec/server_server/unstable.html).
-
-##### How secure is this?
-
-Server-server traffic is mandatorily TLS from the outset. Server-client traffic mandates transport layer encryption other than for tinkering. Servers maintain a public/private key pair, and sign the integrity of all messages in the context of the historical conversation, preventing tampering. Server keys are distributed using a [Perspectives](https://perspectives-project.org/)-style system.
-
-End-to-end encryption is now available in the various [Riot.im](https://Riot.im) builds! This allows you to encrypt both 1:1 and group chats to protect user data stored on servers, using the [Olm](https://matrix.org/git/olm) cryptographic ratchet implementation. Read more on the [blog post](https://matrix.org/blog/2016/11/21/matrixs-olm-end-to-end-encryption-security-assessment-released-and-implemented-cross-platform-on-riot-at-last/) that announced the feature!
-
-Privacy of metadata is not currently protected from server administrators - a malicious homeserver administrator can see who is talking to who and when, but not what is being said (once E2E encryption is enabled). See [this presentation from Jardin Entropique](http://matrix.org/~matthew/2015-06-26%20Matrix%20Jardin%20Entropique.pdf) for a more comprehensive discussion of privacy in Matrix.
-
-##### What is Perspectives?
-
-Rather than relying on Certificate Authorities (CAs) as in traditional SSL, a [Perspectives](https://perspectives-project.org/)-style system uses a more decentralized model for verifying keys. Perspectives uses notary servers to verify that the same key is seen across the network, making a man-in-the-middle attack much harder since an attacker must insert itself into multiple places. For federation in Matrix, each Home Server acts as a notary. When one Home Server connects to another Home Server that uses a key that it doesn't recognize, it contacts other Home Servers to ensure that they all see the same key from that Home Server.
-
-##### Why HTTP? Doesn't HTTP suck?
-
-HTTP is indeed not the most efficient transport, but it is ubiquitous, very well understood and has numerous implementations on almost every platform and language. It also has a simple upgrade path to HTTP/2, which is relatively bandwidth and round-trip efficient.
-
-It has thus been chosen as the mandatory baseline of the exchange, but it is still entirely possible to use more fancy protocols for communication between clients and server (see for example this [websocket transport draft](https://github.com/matrix-org/matrix-doc/blob/master/drafts/websockets.rst)), and it's also possible in the future that negotiation of more efficient protocols will be added for the federation between servers, with HTTP+JSON remaining as the compability baseline.
-
-### Servers
-
-##### What is Synapse?
-
-Synapse is a reference "homeserver" implementation of Matrix from the core development team at matrix.org, written in Python 2/Twisted. It is intended to showcase the concept of Matrix and let folks see the spec in the context of a codebase and let you run your own homeserver and generally help bootstrap the ecosystem.
-
-##### How do I join the global Matrix federation?
-
-You can download and run one of the available Matrix servers - please see [this guide](http://matrix.org/docs/guides/getting_involved.html#run) for details!
-
-##### What ports do I have to open up to join the global Matrix federation?
-
-We recommend servers use port 8448 for server\<-\>server HTTPS traffic. Look at ["Setting up Federation"](https://github.com/matrix-org/synapse#setting-up-federation) in the Synapse readme file for details.
-
-Client\<-\>Server traffic can talk directly to Synapse via port 8448, but as by default Synapse creates a self-signed TLS certificate this can cause problems for clients which can't easily trust self-signed certificates (e.g. most web browsers). Instead, you can proxy access to Synapse's HTTP listener on port 8008 via an existing HTTPS proxy with a valid certificate (e.g. an nginx listening on port 443), or you can point Synapse at a valid X.509 signed TLS certificate. In future, Synapse will probably use letsencrypt to autogenerate valid certificates rather than self-signed ones during installation, simplifying this process enormously.
-
-You can also put Synapse entirely behind an existing TLS load balancer and not expose port 8448 at all. In this situation, Synapse will need to be configured to share the same *public* TLS certificate as the load balancer (as Synapse uses the public certificate for identity in other areas too, and it has to match the certificate that other servers see when they connect).
-
-##### How do I run my own homeserver?
-
-Follow the instructions for the homeserver you want to run. If you want to run Synapse, the reference homeserver from Matrix.org, follow [these instructions](https://github.com/matrix-org/synapse#synapse-installation).
-
-##### Can I run my own identity server?
-
-Yes - the reference implementation is
-[sydent](https://github.com/matrix-org/sydent) and you can run your own ID server cluster that tracks 3rd party to Matrix ID mappings. This won't be very useful right now, though, and we don't recommend it.
-
-If you want your server to participate in the global replicated Matrix ID
-service then please get in touch with us. Meanwhile, we are looking at
-ways of decentralising the 'official' Matrix identity service so that
-identity servers are 100% decentralised and can openly federate with
-each other. **N.B. that you can use Matrix without ever using the
-identity service - it exists only to map 3rd party IDs (e.g. email
-addresses) to matrix IDs to aid user discovery**.
-
-##### What are Synapse's platform requirements?
-
-Synapse will use as much RAM as you give it in order to cache conversations in RAM to avoid hitting the database. For small deployments (<50 active users) around 512MB of RAM is probably okay. You can configure the amount of RAM used by synapse with the event_cache_size config parameter - the more events in the cache, the more RAM required. Synapse itself requires relatively little diskspace other than for logging (which as of October 2015 is quite verbose for debugging purposes), but as it caches the content of all the file attachments (images, videos etc) viewed by its users, you may need to size storage appropriately. Synapse is currently effectively single threaded, and will never use more than 1 core.
-
-|
-
-For better performance, one should back Synapse with a Postgres database rather than the default SQLite - see [https://github.com/matrix-org/synapse/tree/master/README.rst#using-postgresql](https://github.com/matrix-org/synapse/tree/master/README.rst#using-postgresql) for details.
-
-##### Why is Synapse in Python/Twisted?
-
-This is because both provide a mature and well known event-driven async IO framework for writing serverside code. Whilst this has been okay for our initial experimentation and proof of concept, it's likely that future homeserver work will be written in a more strongly typed language (e.g. Go).
-
-##### Why aren't you using an ORM layer like SqlAlchemy in Synapse?
-
-Synapse is *very* database dependent (as of Oct 2015; this is improving in the near future however), and we like having the flexibility to sculpt our own queries.
-
-##### Will Synapse share my chat data with other servers in the federation?
-
-Data is only shared between servers of participating users of a room. If all users in a room are on your server, no data is shared with other servers.
-
-### Clients
-
-##### Where can I find a mobile app?
-
-Riot is available for Android and iOS.
-
-The iOS version can be downloaded from the [Apple store](https://itunes.apple.com/us/app/vector.im/id1083446067).
-
-The Android version can be downloaded from the [Google Play store](https://play.google.com/store/apps/details?id=im.vector.alpha) or [F-Droid](https://f-droid.org/repository/browse/?fdid=im.vector.alpha). If you are not sure which one to choose, install Riot from the [Google Play store](https://play.google.com/store/apps/details?id=im.vector.alpha).
-
-For the Android app, you can also install the latest development version
-built by [Jenkins](http://matrix.org/jenkins/job/VectorAndroidDevelop). Use it at your own risk and only if you know what you are doing.
-
-##### I installed Riot via F-Droid, why is it draining my battery?
-
-The F-Droid release of Riot does not use [Google Cloud Messaging](https://developers.google.com/cloud-messaging/). This allows users that do not have or want Google Services installed to use Riot.
-
-The drawback is that Riot has to pull for new messages, which can drain your battery. To counter this, you can change the delay between polls in the settings. Higher delay means better battery life (but may delay receiving messages). You can also disable the background sync entirely (which means that you won't get any notifications at all).
-
-If you don't mind using Google Services, you might be better off installing the [Google Play store](https://play.google.com/store/apps/details?id=im.vector.alpha) version.
-
-##### Where can I find a web app?
-
-You can use [Riot.im](https://Riot.im) - a glossy web client written on top of [matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk).
-
-You can also run Vector, the code that Riot.im uses, on your own server. It's a static web application, just download the [last release](https://github.com/vector-im/vector-web/) and unpack it.
-
-##### Where can I find a desktop client?
-
-You can use the desktop build of [Riot.im](https://riot.im/desktop.html).
-
-There are also other desktop clients - check the list of clients on [matrix.org](http://matrix.org/docs/projects/try-matrix-now.html#clients).
-
-##### Why can't end-to-end encryption be turned off?
-
-When encryption is enabled in a room, a flag is set in the room state, so that
-all clients know to encrypt any messages they send. The room state stores
-information about the room like the topic, the avatar, and the membership list.
-
-Imagine if encryption could be turned off the same way as it is turned
-on. Anyone with admin rights in the room could clear the flag and then messages
-would start being transmitted unencrypted. It would be very easy for a user to
-miss the change in configuration, and accidentally send a sensitive message
-without encryption.
-
-Worse yet, anyone with sysadmin access to a server could also clear the flag
-(remember that the main reason for using e2e encryption is that we don't trust
-the sysadmins), and could then easily read any sensitive content which was
-sent.
-
-The solution we have taken for now is to make clients ignore any requests to
-disable encryption. We might experiment with ways to improve this in the future
-- for instance, by alerting the user next time they try to send a message in
-the room if encryption has been disabled.
-
-|
-
-### QUESTIONS TO BE ANSWERED!
-
-This FAQ is a constant work in progress - patches and pull requests are *very* welcome to help us improve it. Some of the frequent questions where we need to write an answer include:
-
- * How do I rename servers?
- * How do I change the TLS key of my server?
- * How do I maintain my synapse's DB (e.g. prune old conversations)?
- * How do I maintain my synapse's content repository (e.g. prune old content)?
- * Why is the spec so big, especially relative to the XMPP baseline spec?
- * How do I contribute to the spec?
- * What is the privacy policy on Matrix.org?
- * How precisely does E2E work?
- * How does Matrix actually work architecturally?
- * What IOT use cases are there for Matrix?
- * Why is are the Matrix reference implementations written in so many different languages?
- * How does push work?
- * What's on the roadmap?
- * How can I use Matrix to talk on Freenode or other IRC networks?
- * Where can I learn more about Matrix? (link to PDFs of other presentations etc)
- * Why don't you use websockets?
- * Why is synapse so resource intensive immediately after federating for the first time?
- * \[your question goes here...\]
-
-|
-
-Any other questions? Please contact us in
-[\#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org).
diff --git a/supporting-docs/guides/2015-08-21-application_services.md b/supporting-docs/guides/2015-08-21-application_services.md
deleted file mode 100644
index 7e52fc85..00000000
--- a/supporting-docs/guides/2015-08-21-application_services.md
+++ /dev/null
@@ -1,146 +0,0 @@
----
-layout: post
-title: Application services
-categories: guides
----
-
-# Application services
-
-Application services are distinct modules which which sit alongside a homeserver providing arbitrary extensible functionality decoupled from the homeserver implementation. Just like the rest of Matrix, they communicate via HTTP using JSON. Application services function in a very similar way to traditional clients, but they are given much more power than a normal client. They can reserve entire namespaces of room aliases and user IDs for their own purposes. They can silently monitor events in rooms, or any events directed at any user ID. This power allows application services to have extremely useful abilities which overall enhance the end user experience.
-
-|
-
-One of the main use cases for application services is protocol bridges. Our Matrix server on Matrix.org links in to various IRC channels and networks. This functionality was initially implemented as a simple bot which resided as a user on the Matrix rooms we wanted to link to freenode channels (#matrix, #matrix-dev, #openwebrtc and #vuc etc). There was nothing special about this bot; it is just treated as a client. However, as we started to rely on it more and more though, we realised that there were things that were impossible for simple client-side bots to do by themselves - for example, the bot could not reserve the virtual user IDs it wanted to create, and could not lazily bridge arbitrary IRC rooms on-the-fly - and this spurred the development of Application Services.
-
-|
-
-### Some of the features of the IRC application service we have since implemented include:
-
-- Specific channel-to-matrix room bridging : This is what the original IRC bot did. You can specify specific channels and specific room IDs, and messages will be bridged.
-- Dynamic channel-to-matrix room bridging : This allows Matrix users to join any channel on an IRC network, rather than being forced to use one of the specific channels configured.
-- Two-way PM support : IRC users can PM the virtual "M-" users and private Matrix rooms will be created. Likewise, Matrix users can invite the virtual "@irc_Nick:domain" user IDs to a room and a PM to the IRC nick will be made.
-- IRC nick changing support: Matrix users are no longer forced to use "M-" nicks and can change them by sending "!nick" messages directly to the bridge.
-- Ident support: This allows usernames to be authenticated for virtual IRC clients, which means IRC bans can be targeted at the Matrix user rather than the entire application service.
-
-|
-
-### The use of the Application Services API means:
-
-- The bot can reserve user IDs. This prevents humans from registering for @irc_... user IDs which would then clash with the operation of the bot.
-- The bot can reserve room aliases. This prevents humans from register for #irc_... aliases which would then clash with the operation of the bot.
-- The bot can trivially manage hundreds of users. Events are pushed to the application service directly. If you tried to do this as a client-side bot, you would need one event stream connection per virtual user.
-- The bot can lazily create rooms on demand. This means Matrix users can join non-existent room aliases and have the application service quickly track an IRC channel and create a room with that alias, allowing the join request to succeed.
-
-|
-
-### Implementation details:
-
-- Written in Node.js, designed to be run using forever.
-- Built on the generic matrix-appservice-node framework.
-- Supports sending metrics in statsd format.
-- Uses matrix-appservice-node to provide a standardised interface when writing application services, rather than an explicit web framework (though under the hood matrix-appservice-node is using Express).
-
-|
-
-At present, the IRC application service is in beta, and is being run on #matrix and #matrix-dev. If you want to give it a go, check it out on Github!
-
-|
-
-# What Application services can do for you
-Application services have enormous potential for creating new and exciting ways to transform and enhance the core Matrix protocol. For example, you could aggregate information from multiple rooms into a summary room, or create throwaway virtual user accounts to proxy messages for a fixed user ID on-the-fly. As you may expect, all of this power assumes a high degree of trust between application services and homeservers. Only homeserver admins can allow an application service to link up with their homeserver, and the application service is in no way federated to other homeservers. You can think of application services as additional logic on the homeserver itself, without messing around with the book-keeping that homeservers have to do. This makes adding useful functionality very easy.
-
-|
-
-### Example
-
-The application service (AS) API itself uses webhooks to communicate from the homeserver to the AS:
-
-- Room Alias Query API : The homeserver hits a URL on your application server to see if a room alias exists.
-- User Query API : The homeserver hits a URL on your application server to see if a user ID exists.
-- Push API : The homeserver hits a URL on your application server to notify you of new events for your users and rooms.
-
-A very basic application service may want to log all messages in rooms which have an alias starting with "#logged_" (side note: logging won't work if these rooms are using end-to-end encryption).
-
-Here's an example of a very basic application service using Python (with Flask and Requests) which logs room activity:
-
- # app_service.py:
-
- import json, requests # we will use this later
- from flask import Flask, jsonify, request
- app = Flask(__name__)
-
- @app.route("/transactions/<transaction>", methods=["PUT"])
- def on_receive_events(transaction):
- events = request.get_json()["events"]
- for event in events:
- print "User: %s Room: %s" % (event["user_id"], event["room_id"])
- print "Event Type: %s" % event["type"]
- print "Content: %s" % event["content"]
- return jsonify({})
-
- if __name__ == "__main__":
- app.run()
-
-Set your new application service running on port 5000 with:
-
- python app_service.py
-
-The homeserver needs to know that the application service exists before it will send requests to it. This is done via a registration YAML file which is specified in Synapse's main config file e.g. homeserver.yaml. The server admin needs to add the application service registration configuration file as an entry to this file.
-
- # homeserver.yaml
- app_service_config_files:
- - "/path/to/appservice/registration.yaml"
-
-NB: Note the "-" at the start; this indicates a list element. The registration file registration.yaml should look like:
-
- # registration.yaml
-
- # An ID which is unique across all application services on your homeserver. This should never be changed once set.
- id: "something-good"
-
- # this is the base URL of the application service
- url: "http://localhost:5000"
-
- # This is the token that the AS should use as its access_token when using the Client-Server API
- # This can be anything you want.
- as_token: wfghWEGh3wgWHEf3478sHFWE
-
- # This is the token that the HS will use when sending requests to the AS.
- # This can be anything you want.
- hs_token: ugw8243igya57aaABGFfgeyu
-
- # this is the local part of the desired user ID for this AS (in this case @logging:localhost)
- sender_localpart: logging
- namespaces:
- users: []
- rooms: []
- aliases:
- - exclusive: false
- regex: "#logged_.*"
-
-**You will need to restart the homeserver after editing the config file before it will take effect.**
-
-|
-
-To test everything is working correctly, go ahead and explicitly create a room with the alias "#logged_test:localhost" and send a message into the room: the HS will relay the message to the AS by PUTing to /transactions/<tid> and you should see your AS print the event on the terminal. This will monitor any room which has an alias prefix of "#logged_", but it won't lazily create room aliases if they don't already exist. This means it will only log messages in the room you created before: #logged_test:localhost. Try joining the room "#logged_test2:localhost" without creating it, and it will fail. Let's fix that and add in lazy room creation:
-
- @app.route("/rooms/<alias>")
- def query_alias(alias):
- alias_localpart = alias.split(":")[0][1:]
- requests.post(
- # NB: "TOKEN" is the as_token referred to in registration.yaml
- "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token=TOKEN",
- json.dumps({
- "room_alias_name": alias_localpart
- }),
- headers={"Content-Type":"application/json"}
- )
- return jsonify({})
-
-This makes the application service lazily create a room with the requested alias whenever the HS queries the AS for the existence of that alias (when users try to join that room), allowing any room with the alias prefix #logged_ to be sent to the AS. Now try joining the room "#logged_test2:localhost" and it will work as you'd expect. You can see that if this were a real bridge, the AS would have checked for the existence of #logged_test2 in the remote network, and then lazily-created it in Matrix as required.
-
-|
-
-Application services are powerful components which extend the functionality of homeservers, but they are limited. They can only ever function in a "passive" way. For example, you cannot implement an application service which censors swear words in rooms, because there is no way to prevent the event from being sent. Aside from the fact that censoring will not work when using end-to-end encryption, all federated homeservers would also need to reject the event in order to stop developing an inconsistent event graph. To "actively" monitor events, another component called a "Policy Server" is required, which is beyond the scope of this post. Also, Application Services can result in a performance bottleneck, as all events on the homeserver must be ordered and sent to the registered application services. If you are bridging huge amounts of traffic, you may be better off having your bridge directly talk the Server-Server federation API rather than the simpler Application Service API.
-
-I hope this demonstrates how easy it is to create an application service, along with a few ideas of the kinds of things you can do with them. Obvious uses include build protocol bridges, search engines, invisible bots, etc. For more information on the AS HTTP API, check out the new Application Service API section in the spec, or the raw drafts and spec in https://github.com/matrix-org/matrix-doc/.
diff --git a/supporting-docs/guides/2016-01-01-index.md b/supporting-docs/guides/2016-01-01-index.md
deleted file mode 100644
index 10ba5a91..00000000
--- a/supporting-docs/guides/2016-01-01-index.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-layout: default
-categories: guides
----
-
-
-
Guides
-
-
Here is a collection of guides that might help you get involved with Matrix.
-
First, there is the Getting Involved guide, which explains various ways of getting started with Matrix, and the FAQ, which tries to answer all your questions relating to Matrix.
-
The (updated) Client-Server API guide explains in detail how to use the CS API, which is useful if you want to write a client (or modify an existing one) - or if you're just interested in how it works "under the hood".
-
If you were using the old v1 CS API, there is also the v1 migration guide which justs lists the changes from v1 to r0.
-
Let's Encrypt Matrix explains how to use Let's Encrypt's certificates with your Synapse installation. This guide was written by William A Stevens.
-
The Application services guide introduces and explains Application services, and what they can be used for.
diff --git a/supporting-docs/guides/2016-01-05-code_of_conduct.md b/supporting-docs/guides/2016-01-05-code_of_conduct.md
deleted file mode 100644
index b2ad373a..00000000
--- a/supporting-docs/guides/2016-01-05-code_of_conduct.md
+++ /dev/null
@@ -1,85 +0,0 @@
----
-layout: post
-version: v1.0
-title: Code of Conduct
-categories: guides
----
-
-
-
-# Matrix Code of Conduct
-
-This code of conduct outlines our expectations for participants within the Matrix community, as well as steps for reporting unacceptable behaviour. We are committed to providing a welcoming and inspiring community for all, and expect our code of conduct to be honoured. Anyone who violates this code of conduct may be banned from the community.
-
-This applies to conversation in the #matrix* rooms (#matrix:matrix.org, #matrix-dev:matrix.org, #matrix-spam:matrix.org) and commits and comments relating to any project in the [matrix-org](https://github.com/matrix-org) github space.
-
-Our open source community strives to:
-
-* **Be friendly and patient.**
-* **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.
-* **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary language.
-* **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behaviour and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one.
-* **Be careful in the words that we choose**: Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary behaviour aren't acceptable.
-* **Try to understand why we disagree**: Disagreements, both social and technical, happen all the time. It is important that we resolve disagreements and differing views constructively. Remember that we’re different. The strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping to resolve issues and learning from mistakes.
-
-|
-
-## Definitions
-
-Harassment includes, but is not limited to:
-
-- Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, race, age, regional discrimination, political or religious affiliation
-- Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment
-- Deliberate misgendering. This includes deadnaming or persistently using a pronoun that does not correctly reflect a person's gender identity. You must address people by the name they give you when not addressing them by their username or handle
-- Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop
-- Threats of violence, both physical and psychological
-- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm
-- Deliberate intimidation
-- Stalking or following
-- Harassing photography or recording, including logging online activity for harassment purposes
-- Sustained disruption of discussion
-- Unwelcome sexual attention, including gratuitous or off-topic sexual images or behaviour
-- Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others
-- Continued one-on-one communication after requests to cease
-- Deliberate “outing” of any aspect of a person’s identity without their consent except as necessary to protect others from intentional abuse
-- Publication of non-harassing private communication
-
-|
-
-We will not act on complaints regarding:
-
-- Good faith and non-malicious conduct whose object is to ameliorate the conditions of disadvantaged individuals or groups including those that are disadvantaged because of race, national or ethnic origin, colour, religion, sex, age or mental or physical disability.
-- Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “I’m not discussing this with you”
-- Refusal to explain or debate social justice concepts
-- Communicating in a ‘tone’ you don’t find congenial
-- Criticizing racist, sexist, cissexist, or otherwise oppressive behaviour or assumptions
-
-|
-
-### Diversity Statement
-
-We encourage everyone to participate and are committed to building a community for all. Although we will fail at times, we seek to treat everyone both as fairly and equally as possible. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
-
-Although this list cannot be exhaustive, we explicitly honour diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected
-characteristics above, including participants with disabilities.
-
-|
-
-### Reporting Issues
-
-If you experience or witness unacceptable behaviour — or have any other concerns — please report it by contacting us via abuse@matrix.org. All reports will be handled with discretion. In your report please include:
-
-- Your contact information.
-- Names (usernames and nicks, real names, and/or pseudonyms) of any individuals involved. If there are additional witnesses, please
-include them as well. Your account of what occurred, and if you believe the incident is ongoing.
-- The date and time of the incident (or start of incident).
-- Any additional information that may be helpful.
-
-After filing a report, a representative will contact you personally, review the incident, follow up with any additional questions, and make a decision as to how to respond. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. If the complaint originates from a member of the response team, it will be handled by a different member of the response team. We will respect confidentiality requests for the purpose of protecting victims of abuse.
-
-|
-
-### Attribution & Acknowledgements
-
-This Code of Conduct is based on the [TODO Group](https://twitter.com/todogroup)'s [Open Code of Conduct template](https://github.com/todogroup/opencodeofconduct), but with some modifications.
-
diff --git a/supporting-docs/guides/2016-03-15-lets-encrypt.rst b/supporting-docs/guides/2016-03-15-lets-encrypt.rst
deleted file mode 100644
index 6c6b0ae2..00000000
--- a/supporting-docs/guides/2016-03-15-lets-encrypt.rst
+++ /dev/null
@@ -1,40 +0,0 @@
----
-layout: post
-title: Let's Encrypt Matrix
-categories: guides
----
-
-====================
-Let's Encrypt Matrix
-====================
-
-Let's Encrypt is a free Certificate Authority that makes it easy to secure your server's internet traffic. This makes it really easy to secure your Matrix homeserver, and this guide will explain exactly how you do this. Guide written by William A Stevens - thanks!
-
-0: Prerequisites
-================
-* Install Synapse_.
-* Install (or Download) `Let's Encrypt`_
-
-1: Get certificates
-===================
-When executing the Let's Encrypt client, it will ask for the domain name of your server, and your email address. The domain list can include multiple names and should include any domain you want to access the server from.
-
-Also, the certificates will be in a folder under /etc/letsencrypt (see below) and owned by root. These files should be copied to the same directory as the synapse install and owned by the user synapse is run as.
-
-::
-
-# cd (path to synapse)
-# ./letsencrypt-auto certonly --standalone
-# sudo cp /etc/letsencrypt/live/(your domain name)/* .
-# sudo chown (user synapse runs as) *.pem
-
-A note about renewal
---------------------
-These certificates will expire in 3 months. To renew certificates, just repeat this step.
-
-2: Install Certificates
-=======================
-At the top of your homeserver.yaml there should be two keys, ```tls_certificate_path``` and ```tls_private_key_path```. These should be changed so that instead of pointing to the default keys, they now point to the Let's Encrypt keys. ```tls_certificate_path``` should point to the ```fullchain.pem``` in the synapse install directory. ```tls_private_key_path``` should point to the ```privkey.pem``` in the synapse install directory. ```tls_dh_params_path``` can stay the same as before.
-
-.. _Synapse: https://github.com/matrix-org/synapse/blob/master/README.rst#synapse-installation
-.. _Let's Encrypt: https://letsencrypt.readthedocs.org/en/latest/using.html#installation
diff --git a/supporting-docs/guides/2016-05-05-client-server-migrating-from-v1.rst b/supporting-docs/guides/2016-05-05-client-server-migrating-from-v1.rst
deleted file mode 100644
index e35191a2..00000000
--- a/supporting-docs/guides/2016-05-05-client-server-migrating-from-v1.rst
+++ /dev/null
@@ -1,114 +0,0 @@
----
-layout: post
-title: Migrating from Client Server API v1
-categories: guides
----
-
-Migrating from client-server API v1
-===================================
-
-This guide assists developers of API clients that target the ``v1`` version of
-the API to migrate their code to the later ``r0``. It does not aim to introduce
-new concepts that were added in ``r0`` except where these replace things that
-were removed since ``v1``.
-
-Updated Version In Path
-=======================
-
-The new version of the API is ``r0``; this should be used in paths where
-``v1`` used to appear. Additionally, the ``/api`` path component has now been
-removed. API endpoint paths are now::
-
- POST /_matrix/client/r0/register
- GET /_matrix/client/r0/login
- etc...
-
-New Registration and Login Endpoints
-====================================
-
-The ``r0`` version of the ``/register`` and ``/login`` endpoints is different
-to the ``v1`` version. See the updated API documentation for details on how the
-new API works. In brief, the changes are that the new version returns extra
-information in the form of the ``params`` object, and that a sequence of
-multiple calls may be statefully chained together by the ``session`` parameter.
-
-Additionally, whereas in ``v1`` the client performed a ``GET`` request to
-discover the list of supported flows for ``/register``, in ``r0`` this is done
-by sending a ``POST`` request with an empty data body. The ``/login`` endpoint
-continues to use the ``GET`` method as before.
-
-Deprecated Endpoints
-====================
-
-The following endpoints are now deprecated and replaced by the ``/sync`` API::
-
- /initialSync
- /events
- /rooms/:roomId/initialSync
-
-The new ``/sync`` API takes an optional ``since`` parameter to distinguish the
-initial sync from subsequent updates for more events.
-
-The return value takes a different structure to that from the previous
-``/initialSync`` API. For full details see the API documentation, but the
-following summary may be useful to compare with ``v1``:
-
- * ``/initialSync`` returned a ``state`` key containing the most recent state
- in the room, whereas the new ``/sync`` API's ``state`` corresponds to the
- room state at the start of the returned timeline. This makes it easier for
- clients to represent state changes that occur within the region of returned
- timeline.
-
- * In ``/events``, if more events occurred since the ``since`` token than the
- ``limit`` parameter allowed, then events from the start of this range were
- returned and the client had to perform another fetch to incrementally obtain
- more of them. In the ``/sync`` API the result always contains the most
- recent events, creating a gap if this would be more events than the
- requested limit. If this occurs then the client can use the ``prev_batch``
- token as a reference to obtaining more.
-
- * The ``state`` contained in the response to a ``/sync`` request that has a
- ``since`` parameter will contain only keys that have changed since the
- basis given in the ``since`` parameter, rather than containing a full set
- values.
-
-The ``/initialSync`` API allowed a parameter called ``limit`` to limit the
-number of events returned. To apply this limit to the new ``/sync`` API, you
-can supply an ad-hoc filter::
-
- GET .../sync?filter={"room":{"timeline":{"limit:$limit}}}
-
-There is no direct replacement for the per-room ``/rooms/:roomId/initialSync``
-endpoint, but the behaviour can be recreated by applying an ad-hoc filter using
-the ``filter`` parameter to ``/sync`` that selects only the required room ID::
-
- GET .../sync?filter={"room":{"rooms":[$room_id]}}
-
-However, the way that the new ``/sync`` API works should remove any need to do
-this kind of query, in the situations where the ``v1`` API needed it.
-Specifically, on joining a new room the initial information about that room is
-sent in the next ``/sync`` batch, so it should not be necessary to query that
-one room specially.
-
-The following endpoint is deprecated and has no direct replacement::
-
- /events/:eventId
-
-However, if the client knows the room ID of the room that the event is in, it
-can use the ``/rooms/:roomId/context/:eventId`` request to fetch the event
-itself. By giving the ``limit`` parameter of ``0`` the client can save using
-extra bandwidth by actually returning additional context events around the
-requested one.
-
-Removed POST Endpoint
-=====================
-
-The room message sending API endpoint in ``v1`` accepted both ``PUT`` and
-``POST`` methods, where the client could specify a message ID in the ``PUT``
-path for de-duplication purposes, or have the server allocate one during
-``POST``. In ``r0`` this latter form no longer exists. Clients will now have
-to generate these IDs locally.
-
-The following URLs have therefore been removed::
-
- POST .../rooms/:roomId/send/:messageType
diff --git a/supporting-docs/guides/2016-05-05-client-server.rst b/supporting-docs/guides/2016-05-05-client-server.rst
deleted file mode 100644
index 36d5d5af..00000000
--- a/supporting-docs/guides/2016-05-05-client-server.rst
+++ /dev/null
@@ -1,394 +0,0 @@
----
-layout: post
-title: Client Server API
-categories: guides
----
-
-
-.. TODO kegan
- Room config (specifically: message history,
- public rooms).
-
-How to use the client-server API
-================================
-
-.. NOTE::
- The git version of this document is ``{% project_version %}``
-
-This guide focuses on how the client-server APIs *provided by the reference
-homeserver* can be used. Since this is specific to a homeserver
-implementation, there may be variations in relation to registering/logging in
-which are not covered in extensive detail in this guide.
-
-If you haven't already, get a homeserver up and running on
-``https://localhost:8448``.
-
-
-Accounts
-========
-Before you can send and receive messages, you must **register** for an account.
-If you already have an account, you must **login** into it.
-
-.. NOTE::
- `Try out the fiddle`__
-
- .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/register_login
-
-Registration
-------------
-The aim of registration is to get a user ID and access token which you will need
-when accessing other APIs::
-
- curl -XPOST -d '{"username":"example", "password":"wordpass", "auth": {"type":"m.login.dummy"}}' "https://localhost:8448/_matrix/client/r0/register"
-
- {
- "access_token": "QGV4YW1wbGU6bG9jYWxob3N0.AqdSzFmFYrLrTmteXc",
- "home_server": "localhost",
- "user_id": "@example:localhost"
- }
-
-NB: If a ``user`` is not specified, one will be randomly generated for you.
-If you do not specify a ``password``, you will be unable to login to the account
-if you forget the ``access_token``.
-
-Implementation note: The matrix specification does not enforce how users
-register with a server. It just specifies the URL path and absolute minimum
-keys. The reference homeserver uses a username/password to authenticate user,
-but other homeservers may use different methods. This is why you need to
-specify the ``type`` of method.
-
-Login
------
-The aim when logging in is to get an access token for your existing user ID::
-
- curl -XGET "https://localhost:8448/_matrix/client/r0/login"
-
- {
- "flows": [
- {
- "type": "m.login.password"
- }
- ]
- }
-
- curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "https://localhost:8448/_matrix/client/r0/login"
-
- {
- "access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
- "home_server": "localhost",
- "user_id": "@example:localhost"
- }
-
-Implementation note: Different homeservers may implement different methods for
-logging in to an existing account. In order to check that you know how to login
-to this homeserver, you must perform a ``GET`` first and make sure you
-recognise the login type. If you do not know how to login, you can
-``GET /login/fallback`` which will return a basic webpage which you can use to
-login. The reference homeserver implementation support username/password login,
-but other homeservers may support different login methods (e.g. OAuth2).
-
-
-Communicating
-=============
-
-In order to communicate with another user, you must **create a room** with that
-user and **send a message** to that room.
-
-.. NOTE::
- `Try out the fiddle`__
-
- .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/create_room_send_msg
-
-Creating a room
----------------
-If you want to send a message to someone, you have to be in a room with them. To
-create a room::
-
- curl -XPOST -d '{"room_alias_name":"tutorial"}' "https://localhost:8448/_matrix/client/r0/createRoom?access_token=YOUR_ACCESS_TOKEN"
-
- {
- "room_alias": "#tutorial:localhost",
- "room_id": "!asfLdzLnOdGRkdPZWu:localhost"
- }
-
-The "room alias" is a human-readable string which can be shared with other users
-so they can join a room, rather than the room ID which is a randomly generated
-string. You can have multiple room aliases per room.
-
-.. TODO(kegan)
- How to add/remove aliases from an existing room.
-
-
-Sending messages
-----------------
-You can now send messages to this room::
-
- curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/send/m.room.message?access_token=YOUR_ACCESS_TOKEN"
-
- {
- "event_id": "YUwRidLecu"
- }
-
-The event ID returned is a unique ID which identifies this message.
-
-NB: There are no limitations to the types of messages which can be exchanged.
-The only requirement is that ``"msgtype"`` is specified. The Matrix
-specification outlines the following standard types: ``m.text``, ``m.image``,
-``m.audio``, ``m.video``, ``m.location``, ``m.emote``. See the specification for
-more information on these types.
-
-Users and rooms
-===============
-
-Each room can be configured to allow or disallow certain rules. In particular,
-these rules may specify if you require an **invitation** from someone already in
-the room in order to **join the room**. In addition, you may also be able to
-join a room **via a room alias** if one was set up.
-
-.. NOTE::
- `Try out the fiddle`__
-
- .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/room_memberships
-
-Inviting a user to a room
--------------------------
-You can directly invite a user to a room like so::
-
- curl -XPOST -d '{"user_id":"@myfriend:localhost"}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/invite?access_token=YOUR_ACCESS_TOKEN"
-
-This informs ``@myfriend:localhost`` of the room ID
-``!CvcvRuDYDzTOzfKKgh:localhost`` and allows them to join the room.
-
-Joining a room via an invite
-----------------------------
-If you receive an invite, you can join the room::
-
- curl -XPOST -d '{}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/join?access_token=YOUR_ACCESS_TOKEN"
-
-NB: Only the person invited (``@myfriend:localhost``) can change the membership
-state to ``"join"``. Repeatedly joining a room does nothing.
-
-Joining a room via an alias
----------------------------
-Alternatively, if you know the room alias for this room and the room config
-allows it, you can directly join a room via the alias::
-
- curl -XPOST -d '{}' "https://localhost:8448/_matrix/client/r0/join/%21asfLdzLnOdGRkdPZWu:localhost?access_token=YOUR_ACCESS_TOKEN"
-
- {
- "room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
- }
-
-You will need to use the room ID when sending messages, not the room alias.
-
-NB: If the room is configured to be an invite-only room, you will still require
-an invite in order to join the room even though you know the room alias. As a
-result, it is more common to see a room alias in relation to a public room,
-which do not require invitations.
-
-Getting events
-==============
-An event is some interesting piece of data that a client may be interested in.
-It can be a message in a room, a room invite, etc. There are many different ways
-of getting events, depending on what the client already knows.
-
-.. NOTE::
- `Try out the fiddle`__
-
- .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/event_stream
-
-Getting all state
------------------
-If the client doesn't know any information on the rooms the user is
-invited/joined on, they can get all the user's state for all rooms::
-
- curl -XGET "https://localhost:8448/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN"
-
- {
- "account_data": {
- "events": [
- {
- ...
- }
- ]
- },
- "next_batch": "s9_3_0_1_1_1",
- "presence": {
- "events": [
- {
- "content": {
- "currently_active": true,
- "last_active_ago": 19,
- "presence": "online"
- },
- "sender": "@example:localhost",
- "type": "m.presence"
- }
- ]
- },
- "rooms": {
- "invite": {},
- "join": {
- "!asfLdzLnOdGRkdPZWu:localhost": {
- "account_data": {
- "events": []
- },
- "ephemeral": {
- "events": []
- },
- "state": {
- "events": []
- },
- "timeline": {
- "events": [
- {
- "content": {
- "creator": "@example:localhost"
- },
- "event_id": "$14606534990LhqHt:localhost",
- "origin_server_ts": 1460653499699,
- "sender": "@example:localhost",
- "state_key": "",
- "type": "m.room.create",
- "unsigned": {
- "age": 239192
- }
- },
- {
- "content": {
- "avatar_url": null,
- "displayname": null,
- "membership": "join"
- },
- "event_id": "$14606534991nsZKk:localhost",
- "membership": "join",
- "origin_server_ts": 1460653499727,
- "sender": "@example:localhost",
- "state_key": "@example:localhost",
- "type": "m.room.member",
- "unsigned": {
- "age": 239164
- }
- },
- ...
- ],
- "limited": false,
- "prev_batch": "s9_3_0_1_1_1"
- },
- "unread_notifications": {}
- }
- },
- "leave": {}
- }
- }
-
-This returns all the room information the user is invited/joined on, as well as
-all of the presences relevant for these rooms. This can be a LOT of data. You
-may just want the most recent event for each room. This can be achieved by
-applying a filter that asks for a limit of 1 timeline event per room::
-
- curl --globoff -XGET "https://localhost:8448/_matrix/client/r0/sync?filter={'room':{'timeline':{'limit':1}}}&access_token=YOUR_ACCESS_TOKEN"
-
- {
- ...
- "rooms": {
- "invite": {},
- "join": {
- "!asfLdzLnOdGRkdPZWu:localhost": {
- ...
- "timeline": {
- "events": [
- {
- "content": {
- "body": "hello",
- "msgtype": "m.text"
- },
- "event_id": "$14606535757KCGXo:localhost",
- "origin_server_ts": 1460653575105,
- "sender": "@example:localhost",
- "type": "m.room.message",
- "unsigned": {
- "age": 800348
- }
- }
- ],
- "limited": true,
- "prev_batch": "t8-8_7_0_1_1_1"
- },
- "unread_notifications": {}
- }
- },
- "leave": {}
- }
- }
-
-(additionally we have to ask ``curl`` not to try to interpret any ``{}``
-characters in the URL, which is what the ``--globoff`` option is for)
-
-Getting live state
-------------------
-In the response to this ``sync`` request the server includes a token that can
-be used to obtain updates since this point under the object key ``next_batch``.
-To use this token, specify its value as the ``since`` parameter to another
-``/sync`` request.::
-
- curl -XGET "https://localhost:8448/_matrix/client/r0/sync?since=s9_7_0_1_1_1&access_token=YOUR_ACCESS_TOKEN"
-
- {
- "account_data": {
- "events": []
- },
- "next_batch": "s9_9_0_1_1_1",
- "presence": {
- "events": [
- {
- "content": {
- "currently_active": true,
- "last_active_ago": 12,
- "presence": "online"
- },
- "sender": "@example:localhost",
- "type": "m.presence"
- }
- ]
- },
- "rooms": {
- "invite": {},
- "join": {},
- "leave": {}
- }
- }
-
-By default this request will not wait in the server, always returning a value
-even if nothing interesting happened. However, by applying the ``timeout``
-query parameter, which gives a duration in miliseconds, we can ask the server
-to wait for up to that amount of time before it returns. If no interesting
-events have happened since then, the response will be relatively empty.::
-
- curl -XGET "https://localhost:8448/_matrix/client/r0/sync?since=s9_13_0_1_1_1&access_token=YOUR_ACCESS_TOKEN"
- {
- "account_data": {
- "events": []
- },
- "next_batch": "s9_13_0_1_1_1",
- "presence": {
- "events": []
- },
- "rooms": {
- "invite": {},
- "join": {},
- "leave": {}
- }
- }
-
-Example application
--------------------
-The following example demonstrates registration and login, live event streaming,
-creating and joining rooms, sending messages, getting member lists and getting
-historical messages for a room. This covers most functionality of a messaging
-application.
-
-.. NOTE::
- `Try out the fiddle`__
-
- .. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/example_app
diff --git a/supporting-docs/guides/2016-10-18-e2e_implementation.rst b/supporting-docs/guides/2016-10-18-e2e_implementation.rst
deleted file mode 100644
index a754f7c6..00000000
--- a/supporting-docs/guides/2016-10-18-e2e_implementation.rst
+++ /dev/null
@@ -1,778 +0,0 @@
----
-layout: post
-title: End-to-End Encryption implementation guide
-categories: guides
----
-
-Implementing End-to-End Encryption in Matrix clients
-====================================================
-
-This guide is intended for authors of Matrix clients who wish to add
-support for end-to-end encryption. It is highly recommended that readers
-be familiar with the Matrix protocol and the use of access tokens before
-proceeding.
-
-.. contents::
-
-The libolm library
-------------------
-
-End-to-end encryption in Matrix is based on the Olm and Megolm
-cryptographic ratchets. The recommended starting point for any client
-authors is with the `libolm `__ library,
-which contains implementations of all of the cryptographic primitives
-required. The library itself is written in C/C++, but is architected in
-a way which makes it easy to write wrappers for higher-level languages.
-
-Devices
--------
-
-We have a particular meaning for “device”. As a user, I might have
-several devices (a desktop client, some web browsers, an Android device,
-an iPhone, etc). When I first use a client, it should register itself as
-a new device. If I log out and log in again as a different user, the
-client must register as a new device. Critically, the client must create
-a new set of keys (see below) for each “device”.
-
-The longevity of devices will depend on the client. In the web client,
-we create a new device every single time you log in. In a mobile client,
-it might be acceptable to reuse the device if a login session expires,
-**provided** the user is the same. **Never** share keys between
-different users.
-
-Devices are identified by their ``device_id`` (which is unique within
-the scope of a given user). By default, the ``/login`` and ``/register``
-endpoints will auto-generate a ``device_id`` and return it in the
-response; a client is also free to generate its own ``device_id`` or, as
-above, reuse a device, in which case the client should pass the
-``device_id`` in the request body.
-
-The lifetime of devices and ``access_token``\ s are closely related. In
-the simple case where a new device is created each time you log in,
-there is a one-to-one mapping between a ``device_id`` and an
-``access_token``. If a client reuses a ``device_id`` when logging
-in, there will be several ``access_token``\ s associated with a
-given ``device_id`` - but still, we would expect only one of these to be
-active at once (though we do not currently enforce that in Synapse).
-
-Keys used in End-to-End encryption
-----------------------------------
-
-There are a number of keys involved in encrypted communication: a
-summary of them follows.
-
-Ed25519 fingerprint key pair
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Ed25519 is a public-key cryptographic system for signing messages. In
-Matrix, each device has an Ed25519 key pair which serves to identify
-that device. The private part of the key pair should never leave the
-device, but the public part is published to the Matrix network.
-
-Curve25519 identity key pair
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Curve25519 is a public-key cryptographic system which can be used to
-establish a shared secret. In Matrix, each device has a long-lived
-Curve25519 identity key which is used to establish Olm sessions with
-that device. Again, the private key should never leave the device, but
-the public part is signed with the Ed25519 fingerprint key and published
-to the network.
-
-Theoretically we should rotate the Curve25519 identity key from time to
-time, but we haven't implemented this yet.
-
-Curve25519 one-time keys
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-As well as the identity key, each device creates a number of Curve25519
-key pairs which are also used to establish Olm sessions, but can only be
-used once. Once again, the private part remains on the device.
-
-At startup, Alice creates a number of one-time key pairs, and publishes
-them to her homeserver. If Bob wants to establish an Olm session with
-Alice, he needs to claim one of Alice’s one-time keys, and creates a new
-one of his own. Those two keys, along with Alice’s and Bob’s identity
-keys, are used in establishing an Olm session between Alice and Bob.
-
-Megolm encryption keys
-~~~~~~~~~~~~~~~~~~~~~~
-
-The Megolm key is used to encrypt group messages (in fact it is used to
-derive an AES-256 key, and an HMAC-SHA-256 key). It is initialised with
-random data. Each time a message is sent, a hash calculation is done on
-the Megolm key to derive the key for the next message. It is therefore
-possible to share the current state of the Megolm key with a user,
-allowing them to decrypt future messages but not past messages.
-
-Ed25519 Megolm signing key pair
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When a sender creates a Megolm session, he also creates another Ed25519
-signing key pair. This is used to sign messages sent via that Megolm
-session, to authenticate the sender. Once again, the private part of the
-key remains on the device. The public part is shared with other devices
-in the room alongside the encryption key.
-
-Creating and registering device keys
-------------------------------------
-
-This process only happens once, when a device first starts.
-
-It must create the Ed25519 fingerprint key pair and the Curve25519
-identity key pair. This is done by calling ``olm_create_account`` in
-libolm. The (base64-encoded) keys are retrieved by calling
-``olm_account_identity_keys``. The account should be stored for future
-use.
-
-It should then publish these keys to the homeserver. To do this, it
-should construct a JSON object as follows:
-
-.. code:: json
-
- {
- "algorithms": ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
- "device_id": "",
- "keys": {
- "curve25519:": "",
- "ed25519:": ""
- },
- "user_id: "
- }
-
-The object should be formatted as `Canonical
-JSON `__,
-then signed with ``olm_account_sign``; the signature should be added to
-the JSON as ``signatures..ed25519:``.
-
-The signed JSON is then uploaded via
-``POST /_matrix/client/unstable/keys/upload``.
-
-Creating and registering one-time keys
---------------------------------------
-
-At first start, and at regular intervals
-thereafter\ [#]_, the client should check how
-many one-time keys the homeserver has stored for it, and, if necessary,
-generate and upload some more.
-
-.. [#] Every 10 minutes is suggested.
-
-The number of one-time keys currently stored is returned by
-``POST /_matrix/client/unstable/keys/upload``. (Post an empty JSON object
-``{}`` if you don’t want to upload the device keys.)
-
-The maximum number of active keys supported by libolm is returned by
-``olm_account_max_number_of_one_time_keys``. The client should try to
-maintain about half this number on the homeserver.
-
-To generate new one-time keys:
-
-* Call ``olm_account_generate_one_time_keys`` to generate new keys.
-
-* Call ``olm_account_one_time_keys`` to retrieve the unpublished keys. This
- returns a JSON-formatted object with the single property ``curve25519``,
- which is itself an object mapping key id to base64-encoded Curve25519
- key. For example:
-
- .. code:: json
-
- {
- "curve25519": {
- "AAAAAA": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
- "AAAAAB": "LRvjo46L1X2vx69sS9QNFD29HWulxrmW11Up5AfAjgU"
- }
- }
-
-* Each key should be signed with the account key. To do this:
-
- * Construct a JSON object as follows:
-
- .. code:: json
-
- {
- "key": ""
- }
-
- * Call ``olm_account_sign`` to calculate the signature.
-
- * Add the signature should be added to the JSON as
- ``signatures..ed25519:``.
-
- * The complete key object should now look like:
-
- .. code:: json
-
- {
- "key": "wo76WcYtb0Vk/pBOdmduiGJ0wIEjW4IBMbbQn7aSnTo",
- "signatures": {
- "@alice:example.com": {
- "ed25519:JLAFKJWSCS": "dSO80A01XiigH3uBiDVx/EjzaoycHcjq9lfQX0uWsqxl2giMIiSPR8a4d291W1ihKJL/a+myXS367WT6NAIcBA"
- }
- }
- }
-
-
-* Aggregate all the signed one-time keys into a single JSON object as follows:
-
- .. code:: json
-
- {
- "one_time_keys": {
- "signed_curve25519:": {
- "key": "",
- "signatures": {
- "": {
- "ed25519:": ""
- }
- }
- },
- "signed_curve25519:": {
- ...
- },
- ...
- }
- }
-
-* Upload the object via ``POST /_matrix/client/unstable/keys/upload``.
-
-* Call ``olm_account_mark_keys_as_published`` to tell the olm library not to
- return the same keys from a future call to ``olm_account_one_time_keys``.
-
-Configuring a room to use encryption
-------------------------------------
-
-To enable encryption in a room, a client should send a state event of
-type ``m.room.encryption``, and content ``{ "algorithm":
-"m.megolm.v1.aes-sha2" }``.
-
-.. |m.room.encryption| replace:: ``m.room.encryption``
-.. _`m.room.encryption`:
-
-Handling an ``m.room.encryption`` state event
----------------------------------------------
-
-When a client receives an ``m.room.encryption`` event as above, it
-should set a flag to indicate that messages sent in the room should be
-encrypted.
-
-This flag should **not** be cleared if a later ``m.room.encryption``
-event changes the configuration. This is to avoid a situation where a
-MITM can simply ask participants to disable encryption. In short: once
-encryption is enabled in a room, it can never be disabled.
-
-The event should contain an ``algorithm`` property which defines which
-encryption algorithm should be used for encryption. Currently only
-``m.megolm.v1-aes-sha2`` is permitted here.
-
-The event may also include other settings for how messages sent in the room
-should be encrypted (for example, ``rotation_period_ms`` to define how often
-the session should be replaced).
-
-Handling an ``m.room.encrypted`` event
---------------------------------------
-
-Encrypted events have a type of ``m.room.encrypted``. They have a
-content property ``algorithm`` which gives the encryption algorithm in
-use, as well as other properties specific to the algorithm.
-
-The encrypted payload is a JSON object with the properties ``type``
-(giving the decrypted event type), and ``content`` (giving the decrypted
-content). Depending on the algorithm in use, the payload may contain
-additional keys.
-
-There are currently two defined algorithms:
-
-``m.olm.v1.curve25519-aes-sha2``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Encrypted events using this algorithm should have a ``sender_key`` and a
-``ciphertext`` property.
-
-The ``sender_key`` property of the event content gives the Curve25519
-identity key of the sender. Clients should maintain a list of known Olm
-sessions for each device they speak to; it is recommended to index them
-by Curve25519 identity key.
-
-Olm messages are encrypted separately for each recipient device.
-``ciphertext`` is an object mapping from the Curve25519 identity key for
-the recipient device. The receiving client should, of course, look for
-its own identity key in this object. (If it isn't listed, the message
-wasn't sent for it, and the client can't decrypt it; it should show an
-error instead, or similar).
-
-This should result in an object with the properties ``type`` and
-``body``. Messages of type '0' are 'prekey' messages which are used to
-establish a new Olm session between two devices; type '1' are normal
-messages which are used once a message has been received on the session.
-
-When a message (of either type) is received, a client should first
-attempt to decrypt it with each of the known sessions for that sender.
-There are two steps to this:
-
-- If (and only if) ``type==0``, the client should call
- ``olm_matches_inbound_session`` with the session and ``body``. This
- returns a flag indicating whether the message was encrypted using
- that session.
-
-- The client calls ``olm_decrypt``, with the session, ``type``, and
- ``body``. If this is successful, it returns the plaintext of the
- event.
-
-If the client was unable to decrypt the message using any known sessions
-(or if there are no known sessions yet), **and** the message had type 0,
-**and** ``olm_matches_inbound_session`` wasn't true for any existing
-sessions, then the client can try establishing a new session. This is
-done as follows:
-
-- Call ``olm_create_inbound_session_from`` using the olm account, and
- the ``sender_key`` and ``body`` of the message.
-
-- If the session was established successfully:
-
- - call ``olm_remove_one_time_keys`` to ensure that the same
- one-time-key cannot be reused.
-
- - Call ``olm_decrypt`` with the new session
-
- - Store the session for future use
-
-At the end of this, the client will hopefully have successfully
-decrypted the payload.
-
-As well as the ``type`` and ``content`` properties, the payload should
-contain a number of other properties. Each of these should be checked as
-follows [#]_.
-
-``sender``
- The user ID of the sender. The client should check that this matches the
- ``sender`` in the event.
-
-``recipient``
- The user ID of the recipient. The client should check that this matches the
- local user ID.
-
-``keys``
- an object with a property ``ed25519``, The client should check that the
- value of this property matches the sender's fingerprint key when `marking
- the event as verified`_\ .
-
-``recipient_keys``
-
- an object with a property ``ed25519``. The client should check that the
- value of this property matches its own fingerprint key.
-
-.. [#] These tests prevent an attacker publishing someone else's curve25519
- keys as their own and subsequently claiming to have sent messages which they
- didn't.
-
-``m.megolm.v1.aes-sha2``
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Encrypted events using this algorithm should have ``sender_key``,
-``session_id`` and ``ciphertext`` content properties. If the
-``room_id``, ``sender_key`` and ``session_id`` correspond to a known
-Megolm session (see `below`__), the ciphertext can be
-decrypted by passing the ciphertext into ``olm_group_decrypt``.
-
-__ `m.room_key`_
-
-In order to avoid replay attacks a client should remember the megolm
-``message_index`` returned by ``olm_group_decrypt`` of each event they decrypt
-for each session. If the client decrypts an event with the same
-``message_index`` as one that it has already received using that session then
-it should treat the message as invalid.
-
-The client should check that the sender's fingerprint key matches the
-``keys.ed25519`` property of the event which established the Megolm session
-when `marking the event as verified`_.
-
-.. _`m.room_key`:
-
-Handling an ``m.room_key`` event
---------------------------------
-
-These events contain key data to allow decryption of other messages.
-They are sent to specific devices, so they appear in the ``to_device``
-section of the response to ``GET /_matrix/client/r0/sync``. They will
-also be encrypted, so will need decrypting as above before they can be
-seen.
-
-The event content will contain an 'algorithm' property, indicating the
-encryption algorithm the key data is to be used for. Currently, this
-will always be ``m.megolm.v1.aes-sha2``.
-
-Room key events for Megolm will also have ``room_id``, ``session_id``, and
-``session_key`` keys. They are used to establish a Megolm session. The
-``room_id`` identifies which room the session will be used in. The ``room_id``,
-together with the ``sender_key`` of the ``room_key`` event before it was
-decrypted, and the ``session_id``, uniquely identify a Megolm session. If they
-do not represent a known session, the client should start a new inbound Megolm
-session by calling ``olm_init_inbound_group_session`` with the ``session_key``.
-
-The client should remember the value of the keys property of the payload
-of the encrypted ``m.room_key`` event and store it with the inbound
-session. This is used as above when marking the event as verified.
-
-.. _`download the device list`:
-
-Downloading the device list for users in the room
--------------------------------------------------
-
-Before an encrypted message can be sent, it is necessary to retrieve the
-list of devices for each user in the room. This can be done proactively,
-or deferred until the first message is sent. The information is also
-required to allow users to `verify or block devices`__.
-
-__ `blocking`_
-
-The client should build a JSON query object as follows:
-
-.. code:: json
-
- {
- "": {},
- ...
- }
-
-Each member in the room should be included in the query. This is then
-sent via ``POST /_matrix/client/unstable/keys/query.``
-
-The result includes, for each listed user id, a map from device ID to an
-object containing information on the device, as follows:
-
-.. code:: json
-
- {
- "algorithms": [...],
- "device_id": "",
- "keys": {
- "curve25519:": "",
- "ed25519:": ""
- },
- "signatures": {
- "": {
- "ed25519:": ""
- },
- },
- "unsigned": {
- "device_display_name": ""
- },
- "user_id: "
- }
-
-The client should first check the signature on this object. To do this,
-it should remove the ``signatures`` and ``unsigned`` properties, format
-the remainder as Canonical JSON, and pass the result into
-``olm_ed25519_verify``, using the Ed25519 key for the ``key`` parameter,
-and the corresponding signature for the ``signature`` parameter. If the
-signature check fails, no further processing should be done on the
-device.
-
-The client must also check that the ``user_id`` and ``device_id`` fields in the
-object match those in the top-level map [#]_.
-
-The client should check if the ``user_id``/``device_id`` correspond to a device
-it had seen previously. If it did, the client **must** check that the Ed25519
-key hasn't changed. Again, if it has changed, no further processing should be
-done on the device.
-
-Otherwise the client stores the information about this device.
-
-.. [#] This prevents a malicious or compromised homeserver replacing the keys
- for the device with those of another.
-
-Sending an encrypted event
---------------------------
-
-When sending a message in a room `configured to use encryption`__, a client
-first checks to see if it has an active outbound Megolm session. If not, it
-first `creates one as per below`__. If an outbound session exists, it should
-check if it is time to `rotate`__ it, and create a new one if so.
-
-__ `Configuring a room to use encryption`_
-__ `Starting a Megolm session`_
-__ `Rotating Megolm sessions`_
-
-The client then builds an encryption payload as follows:
-
-.. code:: json
-
- {
- "type": "",
- "content": "",
- "room_id": ""
- }
-
-and calls ``olm_group_encrypt`` to encrypt the payload. This is then packaged
-into event content as follows:
-
-.. code:: json
-
- {
- "algorithm": "m.megolm.v1.aes-sha2",
- "sender_key": "",
- "ciphertext": "",
- "session_id": "",
- "device_id": ""
- }
-
-Finally, the encrypted event is sent to the room with ``POST
-/_matrix/client/r0/rooms//send/m.room.encrypted/``.
-
-Starting a Megolm session
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When a message is first sent in an encrypted room, the client should
-start a new outbound Megolm session. This should **not** be done
-proactively, to avoid proliferation of unnecessary Megolm sessions.
-
-To create the session, the client should call
-``olm_init_outbound_group_session``, and store the details of the
-outbound session for future use.
-
-The client should then call ``olm_outbound_group_session_id`` to get the
-unique ID of the new session, and ``olm_outbound_group_session_key`` to
-retrieve the current ratchet key and index. It should store these
-details as an inbound session, just as it would when `receiving them via
-an m.room_key event`__.
-
-__ `m.room_key`_
-
-The client must then share the keys for this session with each device in the
-room. It must therefore `download the device list`_ if it hasn't already done
-so, and for each device in the room which has not been `blocked`__, the client
-should:
-
-__ `blocking`_
-
-* Build a content object as follows:
-
- .. code:: json
-
- {
- "algorithm": "m.megolm.v1.aes-sha2",
- "room_id": "",
- "session_id": "",
- "session_key": ""
- }
-
-- Encrypt the content as an ``m.room_key`` event using Olm, as below.
-
-Once all of the key-sharing event contents have been assembled, the
-events should be sent to the corresponding devices via
-``PUT /_matrix/client/unstable/sendToDevice/m.room.encrypted/``.
-
-Rotating Megolm sessions
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Megolm sessions may not be reused indefinitely.
-
-The number of messages which can be sent before a session should be rotated is
-given by the ``rotation_period_msgs`` property of the |m.room.encryption|_
-event, or ``100`` if that property isn't present.
-
-Similarly, the maximum age of a megolm session is given, in milliseconds, by
-the ``rotation_period_ms`` property of the ``m.room.encryption``
-event. ``604800000`` (a week) is the recommended default here.
-
-Once either the message limit or time limit have been reached, the client
-should start a new session before sending any more messages.
-
-
-Encrypting an event with Olm
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Olm is not used for encrypting room events, as it requires a separate
-copy of the ciphertext for each device, and because the receiving device
-can only decrypt received messages once. However, it is used for
-encrypting key-sharing events for Megolm.
-
-When encrypting an event using Olm, the client should:
-
-- Build an encryption payload as follows:
-
- .. code:: json
-
- {
- "type": "",
- "content": "",
- "sender": "",
- "sender_device": "",
- "keys": {
- "ed25519": ""
- },
- "recipient": "",
- "recipient_keys": {
- "ed25519": ""
- },
- }
-
-- Check if it has an existing Olm session; if it does not, `start a new
- one`__. If it has several (as may happen due to
- races when establishing sessions), it should use the one with the
- first session_id when sorted by their ASCII codepoints (ie, 'A'
- would be before 'Z', which would be before 'a').
-
- __ `Starting an Olm session`_
-
-- Encrypt the payload by calling ``olm_encrypt``.
-
-- Package the payload into event content as follows:
-
- .. code:: json
-
- {
- "algorithm": "m.olm.v1.curve25519-aes-sha2",
- "sender_key": "",
- "ciphertext": ""
- }
-
-Starting an Olm session
-~~~~~~~~~~~~~~~~~~~~~~~
-
-To start a new Olm session with another device, a client must first
-claim one of the other device's one-time keys. To do this, it should
-create a query object as follows:
-
-.. code:: json
-
- {
- "": {
- "": "signed_curve25519",
- ...
- },
- ...
- }
-
-and send this via ``POST /_matrix/client/unstable/keys/claim``. Claims
-for multiple devices should be aggregated into a single request.
-
-This will return a result as follows:
-
-.. code:: json
-
- {
- "": {
- "": {
- "signed_curve25519:": {
- "key": "",
- "signatures": {
- "": {
- "ed25519:": ""
- }
- }
- },
- },
- ...
- },
- ...
- }
-
-The client should first check the signatures on the signed key objects. As with
-checking the signatures on the device keys, it should remove the ``signatures``
-and (if present) ``unsigned`` properties, format the remainder as Canonical
-JSON, and pass the result into ``olm_ed25519_verify``, using the Ed25519 device
-key for the ``key`` parameter.
-
-Provided the key object passes verification, the client should then pass the
-key, along with the Curve25519 Identity key for the remote device, into
-``olm_create_outbound_session``.
-
-Handling membership changes
----------------------------
-
-The client should monitor rooms which are configured to use encryption for
-membership changes.
-
-When a member leaves a room, the client should invalidate any active outbound
-Megolm session, to ensure that a new session is used next time the user sends a
-message.
-
-When a new member joins a room, the client should first `download the device
-list`_ for the new member, if it doesn't already have it.
-
-After giving the user an opportunity to `block`__ any suspicious devices, the
-client should share the keys for the outbound Megolm session with all the new
-member's devices. This is done in the same way as `creating a new session`__,
-except that there is no need to start a new Megolm session: due to the design
-of the Megolm ratchet, the new user will only be able to decrypt messages
-starting from the current state. The recommended method is to maintain a list
-of members who are waiting for the session keys, and share them when the user
-next sends a message.
-
-__ `blocking`_
-__ `Starting a Megolm session`_
-
-Sending New Device announcements
---------------------------------
-
-When a user logs in on a new device, it is necessary to make sure that
-other devices in any rooms with encryption enabled are aware of the new
-device. This is done as follows.
-
-Once the initial call to the ``/sync`` API completes, the client should
-iterate through each room where encryption is enabled. For each user
-(including the client's own user), it should build a content object as
-follows:
-
-.. code:: json
-
- {
- "device_id": "",
- "rooms": ["", "", ... ]
- }
-
-Once all of these have been constructed, they should be sent to all of the
-relevant user's devices (using the wildcard ``*`` in place of the
-``device_id``) via ``PUT
-/_matrix/client/unstable/sendToDevice/m.new_device/.``
-
-Handling an ``m.new_device`` event
-----------------------------------
-
-As with ``m.room_key`` events, these will appear in the ``to_device``
-section of the ``/sync`` response.
-
-The client should `download the device list`_ of the sender, to get the details
-of the new device.
-
-The event content will contain a ``rooms`` property, as well as the
-``device_id`` of the new device. For each room in the list, the client
-should check if encryption is enabled, and if the sender of the event is
-a member of that room. If so, the client should share the keys for the
-outbound Megolm session with the new device, in the same way as
-`handling a new user in the room`__.
-
-__ `Handling membership changes`_
-
-.. _`blocking`:
-
-Blocking / Verifying devices
-----------------------------
-
-It should be possible for a user to mark each device belonging to
-another user as 'Blocked' or 'Verified'.
-
-When a user chooses to block a device, this means that no further
-encrypted messages should be shared with that device. In short, it
-should be excluded when sharing room keys when `starting a new Megolm
-session <#_p5d1esx6gkrc>`__. Any active outbound Megolm sessions whose
-keys have been shared with the device should also be invalidated so that
-no further messages are sent over them.
-
-Verifying a device involves ensuring that the device belongs to the
-claimed user. Currently this must be done by showing the user the
-Ed25519 fingerprint key for the device, and prompting the user to verify
-out-of-band that it matches the key shown on the other user's device.
-
-.. _`marking the event as verified`:
-
-Marking events as 'verified'
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Once a device has been verified, it is possible to verify that events
-have been sent from a particular device. See the section on `Handling an
-m.room.encrypted event`_ for notes on how to do this
-for each algorithm. Events sent from a verified device can be decorated
-in the UI to show that they have been sent from a verified device.
diff --git a/supporting-docs/howtos/2014-06-09-client-server.md b/supporting-docs/howtos/2014-06-09-client-server.md
deleted file mode 100644
index d62f86d7..00000000
--- a/supporting-docs/howtos/2014-06-09-client-server.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: project
-title: Try Matrix Now!
-categories: howtos
----
-
-
-
-
-
-
diff --git a/supporting-docs/howtos/jsfiddles/create_room_send_msg/demo.css b/supporting-docs/howtos/jsfiddles/create_room_send_msg/demo.css
deleted file mode 100644
index 48a55f37..00000000
--- a/supporting-docs/howtos/jsfiddles/create_room_send_msg/demo.css
+++ /dev/null
@@ -1,17 +0,0 @@
-.loggedin {
- visibility: hidden;
-}
-
-p {
- font-family: monospace;
-}
-
-table
-{
- border-spacing:5px;
-}
-
-th,td
-{
- padding:5px;
-}
diff --git a/supporting-docs/howtos/jsfiddles/create_room_send_msg/demo.html b/supporting-docs/howtos/jsfiddles/create_room_send_msg/demo.html
deleted file mode 100644
index b7e874c2..00000000
--- a/supporting-docs/howtos/jsfiddles/create_room_send_msg/demo.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
This room creation / message sending demo requires a homeserver to be running on http://localhost:8008
-
-
-This page aims to collect all known Matrix projects - if you want to add a new one (or update an existing one), you can submit a PR to the [matrix-doc](https://github.com/matrix-org/matrix-doc) project on github - the existing projects can be found [here](https://github.com/matrix-org/matrix-doc/tree/master/supporting-docs/projects) - or just let us know in the #matrix:matrix.org room.
-
-|
-
-
-
diff --git a/supporting-docs/projects/2016-02-06-vector-desktop.md b/supporting-docs/projects/2016-02-06-vector-desktop.md
deleted file mode 100644
index 7355ccfc..00000000
--- a/supporting-docs/projects/2016-02-06-vector-desktop.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-layout: project
-title: Vector Desktop
-description: Desktop version of Vector
-author: Steven Hammerton
-maturity: Alpha
----
-
-# {{ page.title }}
-Desktop version of the [Vector](./vector.html) web client. Basically it's Vector wrapped in an [Electron](https://github.com/atom/electron) app. Source here: [https://github.com/stevenhammerton/vector-desktop](https://github.com/stevenhammerton/vector-desktop)
diff --git a/supporting-docs/projects/2016-02-07-morpheus.md b/supporting-docs/projects/2016-02-07-morpheus.md
deleted file mode 100644
index 14e17223..00000000
--- a/supporting-docs/projects/2016-02-07-morpheus.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: project
-title: morpheus
-categories: projects client
-description: A Haskell client for Matrix
-author: Xe
-maturity: Alpha
----
-
-# {{ page.title }}
-A Matrix client written in Haskell ([github](https://github.com/Xe/morpheus))
diff --git a/supporting-docs/projects/2016-02-08-h4x.no-blog.md b/supporting-docs/projects/2016-02-08-h4x.no-blog.md
deleted file mode 100644
index 4fd99f02..00000000
--- a/supporting-docs/projects/2016-02-08-h4x.no-blog.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: project
-title: Matrix Blog
-categories: projects client
-description: Read-only blog-style Matrix interface
-author: simeng
-maturity: Alpha
----
-
-# {{ page.title }}
-An example read-only blog-style interface to a Matrix room ([github](https://github.com/simeng/matrix-blog)).
diff --git a/supporting-docs/projects/2016-02-10-matrix-appservice-gitter.md b/supporting-docs/projects/2016-02-10-matrix-appservice-gitter.md
deleted file mode 100644
index 9081f619..00000000
--- a/supporting-docs/projects/2016-02-10-matrix-appservice-gitter.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: matrix-appservice-gitter
-categories: projects as
-author: LeoNerd
-maturity: Early beta
----
-
-# {{ page.title }}
-This project bridges [Gitter](https://gitter.im) to Matrix, via the AS API on the Matrix side, and a Gitter user on the Gitter side.
-
-Get it from [github](https://github.com/matrix-org/matrix-appservice-gitter).
diff --git a/supporting-docs/projects/2016-03-18-matrix-dotnet-sdk.md b/supporting-docs/projects/2016-03-18-matrix-dotnet-sdk.md
deleted file mode 100644
index 5ea27290..00000000
--- a/supporting-docs/projects/2016-03-18-matrix-dotnet-sdk.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: Matrix .NET SDK
-categories: projects sdk
-author: Half-Shot
-maturity: Alpha
----
-# {{ page.title }}
-
-The .NET SDK provides an object oriented library to interact with Matrix. It is currently mature enough to be used for simple clients and bots.
-
-[Github](https://github.com/Half-Shot/matrix-dotnet-sdk)
diff --git a/supporting-docs/projects/2016-03-19-mpd-dj.md b/supporting-docs/projects/2016-03-19-mpd-dj.md
deleted file mode 100644
index e43cc5a9..00000000
--- a/supporting-docs/projects/2016-03-19-mpd-dj.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-layout: project
-title: MPD DJ
-categories: projects other
-description: A bot for controlling MPD over matrix.
-author: Half-Shot
-maturity: Alpha
----
-
-# {{ page.title }}
-
-Ever wanted to control a MPD instance over matrix? Now you can!
-
-Supports a selection (with more coming soon) of mpd commands with the ability to play Youtube links.
-
-Development is steadily ongoing at [Github](https://github.com/Half-Shot/matrix-mpd-dj)
-
diff --git a/supporting-docs/projects/2016-04-05-libqmatrixclient.md b/supporting-docs/projects/2016-04-05-libqmatrixclient.md
deleted file mode 100644
index bc3b852d..00000000
--- a/supporting-docs/projects/2016-04-05-libqmatrixclient.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: libqmatrixclient
-categories: projects sdk
-author: Felix Rohrbach
-maturity: Alpha
----
-
-# {{ page.title }}
-libqmatrixclient is a Qt-based library to make IM clients for the Matrix protocol. It is used by the [Quaternion client](https://matrix.org/docs/projects/client/quaternion.html) and is a part of the Quaternion project.
-
-The project lives in Felix Rohrbach's [github space](https://github.com/Fxrh/libqmatrixclient).
diff --git a/supporting-docs/projects/2016-04-30-concourse-matrix-notification-resource.md b/supporting-docs/projects/2016-04-30-concourse-matrix-notification-resource.md
deleted file mode 100644
index 50ee59b3..00000000
--- a/supporting-docs/projects/2016-04-30-concourse-matrix-notification-resource.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: Concourse/Matrix notification resource
-categories: projects other
-thumbnail: /docs/projects/images/concourse-ci-logo.png
-description: Post notifications from Concourse CI jobs
-author: freelock
-maturity: beta
----
-
-# {{ page.title }}
-Create a Concourse custom resource type using [freelock/matrix-notification-resource](https://hub.docker.com/r/freelock/matrix-notification-resource/) from Docker Hub, or fork/contribute on [github](https://github.com/freelock/matrix-notification-resource)
diff --git a/supporting-docs/projects/2016-05-11-goMatrix.md b/supporting-docs/projects/2016-05-11-goMatrix.md
deleted file mode 100644
index bb8450a6..00000000
--- a/supporting-docs/projects/2016-05-11-goMatrix.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-layout: project
-title: goMatrix
-categories: projects sdk
-author: geir54
-maturity: Alpha
----
-
-# {{ page.title }}
-A Matrix library for go currently in development. [https://github.com/geir54/goMatrix](https://github.com/geir54/goMatrix)
diff --git a/supporting-docs/projects/2016-05-23-twitter-bridge.md b/supporting-docs/projects/2016-05-23-twitter-bridge.md
deleted file mode 100644
index 55d53dda..00000000
--- a/supporting-docs/projects/2016-05-23-twitter-bridge.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-layout: project
-title: Twitter bridge
-categories: projects as
-author: Half-Shot
-maturity: Alpha
----
-
-# {{ page.title }}
-Read tweets and tweet from any Matrix client via this Twitter bridge! Source available at [github](https://github.com/Half-Shot/matrix-appservice-twitter).
diff --git a/supporting-docs/projects/2016-06-30-hdd-space-calc-for-synapse.md b/supporting-docs/projects/2016-06-30-hdd-space-calc-for-synapse.md
deleted file mode 100644
index 3b3cf84d..00000000
--- a/supporting-docs/projects/2016-06-30-hdd-space-calc-for-synapse.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: Synapse HDD space calculator
-categories: projects other
-description: Hard Disk Space Capacity Calculation for Matrix.org Synapse Homeserver
-author: Rick Cogley
-maturity: Early beta
----
-# {{ page.title }}
-
-A handy spreadsheet to help calculate how much disc space you need for your Synapse instance.
-
-You can copy it from [Google Docs](https://docs.google.com/spreadsheets/d/1clrE4WFT7A5NS5AJUdU-mbDZ9rS9fzl6QEbUFJezo7U/edit#gid=0)
diff --git a/supporting-docs/projects/2016-07-03-nachat.md b/supporting-docs/projects/2016-07-03-nachat.md
deleted file mode 100644
index dfe6b820..00000000
--- a/supporting-docs/projects/2016-07-03-nachat.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-layout: project
-title: NaChat
-categories: projects client
-thumbnail: /docs/projects/images/nachat005-150.jpg
-description: Desktop Qt client
-author: Ralith
-maturity: Alpha
----
-
-
-
-# {{ page.title }}
-A Matrix desktop client written in C++ and Qt - source at [github](https://github.com/Ralith/nachat).
diff --git a/supporting-docs/projects/2016-07-05-revolt.md b/supporting-docs/projects/2016-07-05-revolt.md
deleted file mode 100644
index 159ac24d..00000000
--- a/supporting-docs/projects/2016-07-05-revolt.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-layout: project
-title: Revolt
-categories: projects client
-description: Revolt wraps Riot to provide better integration with desktop environments.
-author: Adrian Perez
-maturity: Alpha
----
-
-# {{ page.title }}
-Revolt is a small application which wraps [Riot](./riot.html) to provide better integration with desktop environments in general, and GNOME in particular.
-
-Check it out on [GitHub](https://github.com/aperezdc/revolt)!
-
diff --git a/supporting-docs/projects/2016-07-29-matrix-appservice-rocketchat.md b/supporting-docs/projects/2016-07-29-matrix-appservice-rocketchat.md
deleted file mode 100644
index fb827692..00000000
--- a/supporting-docs/projects/2016-07-29-matrix-appservice-rocketchat.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: matrix-appservice-rocketchat
-categories: projects as
-author: oddvar
-maturity: Early beta
----
-
-# {{ page.title }}
-This project bridges [Rocket Chat](https://rocket.chat) to Matrix. This uses Rocket Chat's webhooks directly (there is also a Hubot-solution - see the [Rocket Chat federation](http://matrix.org/docs/projects/as/rocket-chat-federation.html) project).
-
-Get it from [github](https://github.com/matrix-org/matrix-appservice-rocketchat).
diff --git a/supporting-docs/projects/2016-08-10-matrix-appservice-gitter-twisted.md b/supporting-docs/projects/2016-08-10-matrix-appservice-gitter-twisted.md
deleted file mode 100644
index 6a071757..00000000
--- a/supporting-docs/projects/2016-08-10-matrix-appservice-gitter-twisted.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-layout: project
-title: matrix-appservice-gitter-twisted
-categories: projects as
-author: Remi Rampin
-maturity: Alpha
----
-
-# {{ page.title }}
-This is a Python 2 application using Twisted that bridges the Matrix chat network with the Gitter system.
-
-This is supposed to be deployed as a Matrix application service alongside a homeserver. It allows users to log in to their personal Gitter accounts and chat in Gitter rooms via their Matrix client.
-
-Contrary to other bridges, this doesn't link a public Matrix room with a Gitter one. You won't be able to join a Gitter room without a Gitter account. On the other hand, Gitter users won't see the difference between a Matrix user and a normal Gitter user, since they will appear to be chatting natively.
-
-Find it on [GitHub](https://github.com/remram44/matrix-appservice-gitter-twisted/).
diff --git a/supporting-docs/projects/2016-08-11-nervewire.md b/supporting-docs/projects/2016-08-11-nervewire.md
deleted file mode 100644
index 6c0ef2c4..00000000
--- a/supporting-docs/projects/2016-08-11-nervewire.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: project
-title: Nervewire
-categories: projects other
-description: A Matrix.org image deck
-author: Ryan Rix
-maturity: Early beta
----
-# {{ page.title }}
-
-Nervewire collects images from a specified room, and dumps them on to a page so that they can be used for external purposes (perhaps as a screensaver or wallpaper). More information at [Ryan's blog](http://notes.whatthefuck.computer/1465799340.0-note.html), code at [Ryan's cgit](http://fort.kickass.systems:10082/cgit/personal/rrix/pub/nervewire.git/).
diff --git a/supporting-docs/projects/2016-08-27-interlocutor.md b/supporting-docs/projects/2016-08-27-interlocutor.md
deleted file mode 100644
index 67599286..00000000
--- a/supporting-docs/projects/2016-08-27-interlocutor.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: Interlocutor
-description: Free, decentralized comment software for the web
-categories: projects other
-author: Alexander Maznev
-maturity: Alpha
----
-
-# {{ page.title }}
-Interlocutor is a decentralized comment software built on top of Matrix as a Polymer Webcomponent, it is under active development and not yet ready for production use.
-
-Check it on [GitHub](https://github.com/pik/interlocutor).
diff --git a/supporting-docs/projects/2016-09-04-hello-matrix-bot.md b/supporting-docs/projects/2016-09-04-hello-matrix-bot.md
deleted file mode 100644
index 68506c10..00000000
--- a/supporting-docs/projects/2016-09-04-hello-matrix-bot.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-layout: project
-title: Hello Matrix Bot
-categories: projects other
-description: Bot with an array of plugins
-author: Alexander Rudyk
-maturity: Alpha
----
-
-# {{ page.title }}
-This project is a simple attempt at providing a friendly face to as many popular services as possible, making them accessible from any Matrixroom. "Hello Matrix" is written as NodeJS application, building on matrix-js-sdk, the JavaScript SDK from the creators of Matrix. It is a hobby project and as such far from feature complete, in fact it is all very basic right now - hopefully this will change over the coming months.
-
-You can either use the "Hello Matrix" bot running on our server (just invite @hello-matrix:matrix.org into your Matrix room) or you can check out the code from this repository and run your own instance of "Hello Matrix" (also providing your own API keys to the services you want to use).
-
-"Hello Matrix" currently supports the following services:
-
-* Sending and receiving messages using Bitmessage
-* Numeric calculations using Wolfram Alpha
-* Throwing the dice (generating a random number)
-* Adding tasks to your Kanban board from Kanban Tool and getting notified of task status changes
-* Tracerouting a given IP
-* Weather from OpenWeatherMap
-* Providing WHOIS information on a domain or IP address
-* Adding tasks and monitoring progress on Wunderlist lists
-* The goal is to add at least generic web hook functionality in the coming months, which would immediately make a large number of other integrations possible. We’ll keep you updated on any progress.
-
-|
-
-Follow the progress and grab the code from [gitlab](https://gitlab.com/argit/hello-matrix-bot)!
diff --git a/supporting-docs/projects/2016-09-05-node-red-nodes.md b/supporting-docs/projects/2016-09-05-node-red-nodes.md
deleted file mode 100644
index 90de2114..00000000
--- a/supporting-docs/projects/2016-09-05-node-red-nodes.md
+++ /dev/null
@@ -1,15 +0,0 @@
----
-layout: project
-title: Matrix bot nodes for Node-RED
-description: Visual bot creation using Node-RED
-categories: projects other
-author: mlopezr
-maturity: Alpha
----
-
-# {{ page.title }}
-It's very easy to create simple interactions with a Matrix chatroom without programming. Discover Node-RED, a visual tool to wire together APIs. We have extended Node-RED with nodes to listen and talk in Matrix chatrooms.
-
-
-
-Check it out on [GitHub](https://github.com/mlopezr/node-red-contrib-matrixbot).
diff --git a/supporting-docs/projects/2016-09-13-telematrix.md b/supporting-docs/projects/2016-09-13-telematrix.md
deleted file mode 100644
index 29c46554..00000000
--- a/supporting-docs/projects/2016-09-13-telematrix.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: telematrix
-categories: projects as
-author: SijmenSchoon
-maturity: Alpha
----
-
-# {{ page.title }}
-This project bridges [Telegram Messenger](https://telegram.org/) to Matrix. It's currently in early development, and not considered to be in a usable state yet.
-
-Get it and report issues at [github](https://github.com/SijmenSchoon/telematrix)!
diff --git a/supporting-docs/projects/2016-09-17-drupal-matrix-api.md b/supporting-docs/projects/2016-09-17-drupal-matrix-api.md
deleted file mode 100644
index 3b4a5cc2..00000000
--- a/supporting-docs/projects/2016-09-17-drupal-matrix-api.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: Drupal matrix_api module
-categories: projects sdk
-thumbnail: /docs/projects/images/200px-druplicon.png
-description: A Drupal 8 integration module/SDK
-author: freelock
-maturity: Alpha
----
-
-# {{ page.title }}
-A Drupal API module to facilitate posting messages into Matrix rooms. [Drupal.org](https://drupal.org/project/matrix_api)
diff --git a/supporting-docs/projects/2016-10-04-matrix-ircd.md b/supporting-docs/projects/2016-10-04-matrix-ircd.md
deleted file mode 100644
index 5d52a004..00000000
--- a/supporting-docs/projects/2016-10-04-matrix-ircd.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-layout: project
-title: matrix-ircd
-categories: projects client
-description: An IRCd implementation backed by Matrix.
-author: Erik
-maturity: Alpha
----
-
-# {{ page.title }}
-An IRCd implementation backed by Matrix. Inspired by [PTO](./pto.html)!
-
-This is a work in progress. Matrix IRCd should be stable enough to hack on and test, but has not been tested in production or for any length of time.
-
-See the [GitHub issues page](https://github.com/matrix-org/matrix-ircd/issues) for a more detailed breakdown of what is left to do, and/or join the discussion on the Matrix channel: [#matrix-ircd:matrix.org](https://matrix.to/#/#matrix-ircd:matrix.org)
-
-You can check out the code from [GitHub](https://github.com/matrix-org/matrix-ircd).
diff --git a/supporting-docs/projects/2016-10-07-mm.md b/supporting-docs/projects/2016-10-07-mm.md
deleted file mode 100644
index e1892a80..00000000
--- a/supporting-docs/projects/2016-10-07-mm.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-layout: project
-title: mm
-categories: projects client
-author: Meutraa
-description: A minimalistic client written in Go
-maturity: Alpha
----
-
-# {{ page.title }}
-A minimalistic client written in Go
-
-Features
-
-* Less than 250 lines of code.
-* Fetching last ten messages of each room on start.
-* Receiving messages by long polling sync call.
-* Sending messages through named pipes.
-* Marking latest event as read (the recommendation to only mark this as read when the user has read the message seems iffy here. An IMAP type tagging system could work, but would be as complex as the program itself). At least this way, users of other clients will know your computer has recieved the message.
-* Online presence.
-* Message modification time set to message timestamp minus five seconds.
-* List of new messages (file paths) written to stdout
-
-Planned
-
-* Syncing all message history without gaps.
-
-
-Above list fetched from [gitlab](https:///gitlab.com/meutraa/mm/tree/master)!
diff --git a/supporting-docs/projects/2016-10-12-freebird.md b/supporting-docs/projects/2016-10-12-freebird.md
deleted file mode 100644
index bc28d8b6..00000000
--- a/supporting-docs/projects/2016-10-12-freebird.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: Freebird
-categories: projects other
-description: Matrix based Twitter clone
-author: tjgillies
-maturity: Alpha
----
-
-# {{ page.title }}
-Matrix based Twitter clone. Try it [here](http://freebird.tyler.cat/)!
-
-The code can be found on [GitHub](https://github.com/tjgillies/freebird/).
diff --git a/supporting-docs/projects/2016-10-16-hangouts-bridge.md b/supporting-docs/projects/2016-10-16-hangouts-bridge.md
deleted file mode 100644
index 84049129..00000000
--- a/supporting-docs/projects/2016-10-16-hangouts-bridge.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: Hangouts Bridge
-categories: projects as
-author: CyrusTheHedgehog
-maturity: Alpha
----
-
-# {{ page.title }}
-This project creates a bridge between Google Hangouts and Matrix. It was originally developed as part of the 2016 Global TADHack - see related [blogpost](https://matrix.org/blog/2016/10/20/tadhack-global-2016/) and [video of the presentation](https://www.youtube.com/watch?v=X41RbOKTrbE&t=17m31s).
-
-You can get the source from [GitHub](https://github.com/CyrusTheHedgehog/Hangouts-Bridge).
-
diff --git a/supporting-docs/projects/2016-10-16-matrix-esp8266.md b/supporting-docs/projects/2016-10-16-matrix-esp8266.md
deleted file mode 100644
index 61f0b11b..00000000
--- a/supporting-docs/projects/2016-10-16-matrix-esp8266.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: matrix-esp8266
-categories: projects other
-description: Tiny matrix client for the esp8266 microcontroller
-author: Matt Williams
-maturity: Alpha
----
-
-# {{ page.title }}
-Originally made for [TADHack 2016](https://www.youtube.com/watch?v=X41RbOKTrbE&t=11m30s), this hack is a tiny Matrix client for the esp8266 microcontroller.
-
-Grab the code from [GitHub](https://github.com/matt-williams/matrix-esp8266/).
diff --git a/supporting-docs/projects/2016-10-22-j.md b/supporting-docs/projects/2016-10-22-j.md
deleted file mode 100644
index 46252d21..00000000
--- a/supporting-docs/projects/2016-10-22-j.md
+++ /dev/null
@@ -1,15 +0,0 @@
----
-layout: project
-title: j - journalism for [matrix]
-description: A web client for writing news stories, personal blogs and more, built on matrix.
-categories: projects other
-author: Luke Bernard
-maturity: Early beta
----
-
-# {{ page.title }}
-A web client for writing news stories, personal blogs and more, built on matrix.
-
-
-
-Check it out from [Luke's GitHub repo](https://github.com/lukebarnard1/j).
diff --git a/supporting-docs/projects/2016-10-30-mxpp.md b/supporting-docs/projects/2016-10-30-mxpp.md
deleted file mode 100644
index 6adaa461..00000000
--- a/supporting-docs/projects/2016-10-30-mxpp.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: mxpp
-categories: projects other
-description: Bot for bridging Matrix and one-to-one XMPP chats
-author: anewusername
-maturity: Alpha
----
-
-# {{ page.title }}
-[mxpp](https://github.com/anewusername/mxpp) is a bot for bridging XMPP and Matrix. It creates one Matrix room per user on your XMPP roster, and then passes messages between the Matrix room and XMPP user.
-
diff --git a/supporting-docs/projects/2016-11-12-matrex.md b/supporting-docs/projects/2016-11-12-matrex.md
deleted file mode 100644
index d146b338..00000000
--- a/supporting-docs/projects/2016-11-12-matrex.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-layout: project
-title: Matrex
-categories: projects server
-author: Ryan Johnson
-maturity: Alpha
----
-
-
-# {{ page.title }}
-A WIP toy Matrix server implementation in Elixir. Find it on [GitHub](https://github.com/bismark/matrex).
diff --git a/supporting-docs/projects/2016-11-20-matrix-rocketchat.md b/supporting-docs/projects/2016-11-20-matrix-rocketchat.md
deleted file mode 100644
index f66064de..00000000
--- a/supporting-docs/projects/2016-11-20-matrix-rocketchat.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: matrix-rocketchat
-categories: projects as
-author: exul
-maturity: Early beta
----
-
-# {{ page.title }}
-This is an application service that bridges Matrix to Rocket.Chat, written in Rust.
-
-Find it on [GitHub](https://github.com/exul/matrix-rocketchat).
diff --git a/supporting-docs/projects/2016-11-27-matrix-pushgw.md b/supporting-docs/projects/2016-11-27-matrix-pushgw.md
deleted file mode 100644
index 18a602de..00000000
--- a/supporting-docs/projects/2016-11-27-matrix-pushgw.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: matrix-pushgw
-description: A custom push gateway
-categories: projects other
-author: Sergio L. Pascual
-maturity: Alpha
----
-
-# {{ page.title }}
-matrix-pushgw is a custom push gateway for Matrix, written in Go.
-
-Get the source from [GitHub](https://github.com/slp/matrix-pushgw).
diff --git a/supporting-docs/projects/2016-11-27-synapse-password-reset.md b/supporting-docs/projects/2016-11-27-synapse-password-reset.md
deleted file mode 100644
index fe8234cd..00000000
--- a/supporting-docs/projects/2016-11-27-synapse-password-reset.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: synapse-password-reset
-description: A tool to manage Synapse password resets
-categories: projects other
-author: Euan Kemp
-maturity: Alpha
----
-
-# {{ page.title }}
-A small tool that creates a new access_token in the database so that a user can reset their password.
-
-Check it out on [GitHub](https://github.com/euank/synapse-password-reset).
diff --git a/supporting-docs/projects/2016-11-29-gomatrix.md b/supporting-docs/projects/2016-11-29-gomatrix.md
deleted file mode 100644
index 26913a99..00000000
--- a/supporting-docs/projects/2016-11-29-gomatrix.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: gomatrix
-categories: projects client
-description: A Golang Matrix client
-author: Kegsay
-maturity: Alpha
----
-
-# {{ page.title }}
-A Golang Matrix client in early development.
-
-Find it on [GitHub](https://github.com/matrix-org/gomatrix).
diff --git a/supporting-docs/projects/2016-11-29-matrix-live.md b/supporting-docs/projects/2016-11-29-matrix-live.md
deleted file mode 100644
index e95a95b2..00000000
--- a/supporting-docs/projects/2016-11-29-matrix-live.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-layout: project
-title: Matrix Live
-description: Liveblogging based on Matrix
-categories: projects other
-author: ar
-maturity: Alpha
----
-
-# {{ page.title }}
-Liveblogging is reporting about events -- with text and pictures -- as they happen. Matrix is a new, powerful messaging platform for instant communication. We believe the two belong together. That's why we have created Matrix Live.
-
-With Matrix Live, you can create a room in Matrix, and while you use your favorite Matrix client (such as Riot) to write text and post pictures in this room, your users will see them appear live on the web - either here or right on your blog or website.
-
-How does it look like in practice? You can try it out. We have prepared two demo versions for you, which are both fully functional Matrix Live implementations - check them out [here](https://live.hello-matrix.net/live.html#matrix.hello-matrix.net/!sBcCeweWXOjVViEiIY%3Ahello-matrix.net/Hello%2C%20Matrix!) and [here](https://live.hello-matrix.net/demo2.html).
-
-For more information, visit [live.hello-matrix.net](https://live.hello-matrix.net/) or look at the source code on [gitlab](https://gitlab.com/argit/matrix-live).
diff --git a/supporting-docs/projects/2016-12-03-matrix-appservice-imessage.md b/supporting-docs/projects/2016-12-03-matrix-appservice-imessage.md
deleted file mode 100644
index 05c769e3..00000000
--- a/supporting-docs/projects/2016-12-03-matrix-appservice-imessage.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: imessage bridge
-categories: projects as
-author: Keyvan Fatehi
-maturity: Alpha
----
-
-# {{ pagae.title }}
-This is a Matrix bridge for Apple iMessage. It connects your homeserver to Messages.app
-
-Find the source on [GitHub](https://github.com/kfatehi/matrix-appservice-imessage).
-
diff --git a/supporting-docs/projects/2016-12-11-riotchat.md b/supporting-docs/projects/2016-12-11-riotchat.md
deleted file mode 100644
index c34a537a..00000000
--- a/supporting-docs/projects/2016-12-11-riotchat.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: riotchat
-categories: projects other
-description: Ansible playbook for matrix/riot.im setup
-author: kultsinuppeli
-maturity: Alpha
----
-
-# {{ page.title }}
-This playbook sets up Matrix and Riot.im on a single server or two separate servers. The servers will request Let's Encrypt certificates for the hostnames assigned to them.
-
-Find the source on [GitHub](https://github.com/kultsinuppeli/riotchat).
diff --git a/supporting-docs/projects/2016-12-12-matrix-fb-chat.md b/supporting-docs/projects/2016-12-12-matrix-fb-chat.md
deleted file mode 100644
index 74f46b88..00000000
--- a/supporting-docs/projects/2016-12-12-matrix-fb-chat.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-layout: project
-title: matrix-fb-chat
-categories: projects other
-author: Half-Shot
-maturity: Alpha
----
-
-# {{ page.title }}
-This project connects Matrix rooms with Facebook conversations. It's very much a PoC in its current state.
-
-What can it do?
-
-* Bridge one conversation to one room.
-* Send text messages from matrix.
-* Recieve messages and urls from facebook.
-
-Find the source on [GitHub](https://github.com/Half-Shot/matrix-fb-chat/).
diff --git a/supporting-docs/projects/2016-12-21-tiny-matrix-bot.md b/supporting-docs/projects/2016-12-21-tiny-matrix-bot.md
deleted file mode 100644
index 4db6b49e..00000000
--- a/supporting-docs/projects/2016-12-21-tiny-matrix-bot.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: project
-title: tiny-matrix-bot
-categories: projects other
-description: Bot with plugins
-author: Ander Punnar
-maturity: Alpha
----
-
-# {{ page.title }}
-Simple and dirty matrix.org bot based on matrix-python-sdk
-
-no manual, no support, no warranty
-
-pull requests are welcome!
-
-manual:
-
-* git clone https://github.com/4nd3r/tiny-matrix-bot
-* git clone https://github.com/matrix-org/matrix-python-sdk
-* cd tiny-matrix-bot
-* ln -s ../matrix-python-sdk/matrix_client
-* cp tiny-matrix-bot.cfg.sample tiny-matrix-bot.cfg
-* vim tiny-matrix-bot.cfg
-* screen ./tiny-matrix-bot.py tiny-matrix-bot.cfg
-* scripts must have execute bit - chmod +x
-
-|
-
-Follow the progress and grab the code from [GitHub](https://github.com/4nd3r/tiny-matrix-bot/)!
diff --git a/supporting-docs/projects/2016-12-26-matrix-music-bot.md b/supporting-docs/projects/2016-12-26-matrix-music-bot.md
deleted file mode 100644
index 59a4527e..00000000
--- a/supporting-docs/projects/2016-12-26-matrix-music-bot.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: matrix-music-bot
-categories: projects other
-description: last.fm scrobbler
-author: Falko
-maturity: Alpha
----
-
-# {{ page.title }}
-A small bot that lets you look up artists using lastfm's API.
-
-Follow the progress and grab the code from [GitHub](https://github.com/select/matrix-music-bot)
diff --git a/supporting-docs/projects/2016-12-30-matrix-appservice-facebook.md b/supporting-docs/projects/2016-12-30-matrix-appservice-facebook.md
deleted file mode 100644
index eec144c1..00000000
--- a/supporting-docs/projects/2016-12-30-matrix-appservice-facebook.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-layout: project
-title: matrix-appservice-facebook
-categories: projects as
-author: Keyvan Fatehi
-maturity: Alpha
----
-
-# {{ page.title }}
-This is a puppeted Facebook bridge for Matrix.
-
-Get it from [GitHub](https://github.com/kfatehi/matrix-appservice-facebook).
diff --git a/supporting-docs/projects/2017-01-05-synpurge.md b/supporting-docs/projects/2017-01-05-synpurge.md
deleted file mode 100644
index 29dde3b2..00000000
--- a/supporting-docs/projects/2017-01-05-synpurge.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-layout: project
-title: synpurge
-categories: projects other
-description: Purges Matrix room history room using the HTTP API
-author: Adrian Perez de Castro
-maturity: Alpha
----
-
-# {{ page.title }}
-Utility to remove history of Matrix rooms using the purge_history API of Synapse.
-
-Find it on [PyPI](https://pypi.python.org/pypi/synpurge/4).
diff --git a/supporting-docs/projects/README.md b/supporting-docs/projects/README.md
deleted file mode 100644
index dd6bf485..00000000
--- a/supporting-docs/projects/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Matrix Projects
-We are using [jekyll](https://jekyllrb.com/) to generate the [try matrix now](https://matrix.org/docs/projects/try-matrix-now.html) page and the project pages.
-
-Feel free to send us a PR to add or update a project entry. You can start with the [template](./template.md), and you can upload a thumbnail and/or a main picture to the [images](./images/) subfolder - these will be accessible from https://matrix.org/docs/projects/images/
-
-Jekyll requires a date in the project filename; we use the date to sort the various project lists (oldest projects first). Please submit new entries with the starting date of the project.
diff --git a/supporting-docs/projects/images/200px-druplicon.png b/supporting-docs/projects/images/200px-druplicon.png
deleted file mode 100644
index 86ac1368..00000000
Binary files a/supporting-docs/projects/images/200px-druplicon.png and /dev/null differ
diff --git a/supporting-docs/projects/images/concourse-ci-logo.png b/supporting-docs/projects/images/concourse-ci-logo.png
deleted file mode 100644
index b7a2cc4f..00000000
Binary files a/supporting-docs/projects/images/concourse-ci-logo.png and /dev/null differ
diff --git a/supporting-docs/projects/images/matrix-console-android-2016-02-16-cropped.png b/supporting-docs/projects/images/matrix-console-android-2016-02-16-cropped.png
deleted file mode 100644
index c293a26d..00000000
Binary files a/supporting-docs/projects/images/matrix-console-android-2016-02-16-cropped.png and /dev/null differ
diff --git a/supporting-docs/projects/images/matrix-console-android-2016-02-16-large.png b/supporting-docs/projects/images/matrix-console-android-2016-02-16-large.png
deleted file mode 100644
index e1282584..00000000
Binary files a/supporting-docs/projects/images/matrix-console-android-2016-02-16-large.png and /dev/null differ
diff --git a/supporting-docs/projects/images/matrix-console-android-2016-02-16-small.png b/supporting-docs/projects/images/matrix-console-android-2016-02-16-small.png
deleted file mode 100644
index 2d89b717..00000000
Binary files a/supporting-docs/projects/images/matrix-console-android-2016-02-16-small.png and /dev/null differ
diff --git a/supporting-docs/projects/images/matrix-console-ios-2016-02-16-cropped.png b/supporting-docs/projects/images/matrix-console-ios-2016-02-16-cropped.png
deleted file mode 100644
index 24ac2695..00000000
Binary files a/supporting-docs/projects/images/matrix-console-ios-2016-02-16-cropped.png and /dev/null differ
diff --git a/supporting-docs/projects/images/matrix-console-ios-2016-02-16-large.png b/supporting-docs/projects/images/matrix-console-ios-2016-02-16-large.png
deleted file mode 100644
index 8da09f9a..00000000
Binary files a/supporting-docs/projects/images/matrix-console-ios-2016-02-16-large.png and /dev/null differ
diff --git a/supporting-docs/projects/images/matrix-console-ios-2016-02-16-small.png b/supporting-docs/projects/images/matrix-console-ios-2016-02-16-small.png
deleted file mode 100644
index 9e974037..00000000
Binary files a/supporting-docs/projects/images/matrix-console-ios-2016-02-16-small.png and /dev/null differ
diff --git a/supporting-docs/projects/images/nachat005-150.jpg b/supporting-docs/projects/images/nachat005-150.jpg
deleted file mode 100644
index e73b5dfa..00000000
Binary files a/supporting-docs/projects/images/nachat005-150.jpg and /dev/null differ
diff --git a/supporting-docs/projects/images/nachat005.jpg b/supporting-docs/projects/images/nachat005.jpg
deleted file mode 100644
index f978515f..00000000
Binary files a/supporting-docs/projects/images/nachat005.jpg and /dev/null differ
diff --git a/supporting-docs/projects/images/pto.png b/supporting-docs/projects/images/pto.png
deleted file mode 100644
index a49e54f9..00000000
Binary files a/supporting-docs/projects/images/pto.png and /dev/null differ
diff --git a/supporting-docs/projects/images/riot-web-featured.png b/supporting-docs/projects/images/riot-web-featured.png
deleted file mode 100644
index 2967376c..00000000
Binary files a/supporting-docs/projects/images/riot-web-featured.png and /dev/null differ
diff --git a/supporting-docs/projects/images/riot-web-large.png b/supporting-docs/projects/images/riot-web-large.png
deleted file mode 100644
index 4fcadda0..00000000
Binary files a/supporting-docs/projects/images/riot-web-large.png and /dev/null differ
diff --git a/supporting-docs/projects/images/riot-web-small.png b/supporting-docs/projects/images/riot-web-small.png
deleted file mode 100644
index e70b7141..00000000
Binary files a/supporting-docs/projects/images/riot-web-small.png and /dev/null differ
diff --git a/supporting-docs/projects/images/vector-android-featured.png b/supporting-docs/projects/images/vector-android-featured.png
deleted file mode 100644
index 489c4668..00000000
Binary files a/supporting-docs/projects/images/vector-android-featured.png and /dev/null differ
diff --git a/supporting-docs/projects/images/vector-android-large.png b/supporting-docs/projects/images/vector-android-large.png
deleted file mode 100644
index b674b3e6..00000000
Binary files a/supporting-docs/projects/images/vector-android-large.png and /dev/null differ
diff --git a/supporting-docs/projects/images/vector-android-small.png b/supporting-docs/projects/images/vector-android-small.png
deleted file mode 100644
index 1cbb24be..00000000
Binary files a/supporting-docs/projects/images/vector-android-small.png and /dev/null differ
diff --git a/supporting-docs/projects/images/vector-iOS-featured.png b/supporting-docs/projects/images/vector-iOS-featured.png
deleted file mode 100644
index 54491582..00000000
Binary files a/supporting-docs/projects/images/vector-iOS-featured.png and /dev/null differ
diff --git a/supporting-docs/projects/images/vector-iOS-large.png b/supporting-docs/projects/images/vector-iOS-large.png
deleted file mode 100644
index cd14453c..00000000
Binary files a/supporting-docs/projects/images/vector-iOS-large.png and /dev/null differ
diff --git a/supporting-docs/projects/images/vector-iOS-small.png b/supporting-docs/projects/images/vector-iOS-small.png
deleted file mode 100644
index 14f44d02..00000000
Binary files a/supporting-docs/projects/images/vector-iOS-small.png and /dev/null differ
diff --git a/supporting-docs/projects/template.md b/supporting-docs/projects/template.md
deleted file mode 100644
index d274eb65..00000000
--- a/supporting-docs/projects/template.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-layout: project
-title: My First Matrix Client
-categories: projects client
-# valid categories are client, server, as (application service), sdk (client sdk), and other
-thumbnail: /docs/projects/images/upload-a-thumbnail-image-to-the-images-subfolder.png
-description: Describe your project here
-author: Myself
-maturity: Alpha
----
-
-
-
-# {{ page.title }}
-Add a fuller description/explanation of your project here, ideally with a link to the source on [github](https://github.com/matrix-org/) or elsewhere.
-
diff --git a/templating/matrix_templates/templates/common-event-fields.tmpl b/templating/matrix_templates/templates/common-event-fields.tmpl
deleted file mode 100644
index 8d8c8f0c..00000000
--- a/templating/matrix_templates/templates/common-event-fields.tmpl
+++ /dev/null
@@ -1,8 +0,0 @@
-{% import 'tables.tmpl' as tables -%}
-
-{{common_event.title}} Fields
-{{(7 + common_event.title | length) * title_kind}}
-
-{{common_event.desc | wrap(80)}}
-
-{{ tables.paramtable(common_event.rows, ["Key", "Type", "Description"]) }}
diff --git a/templating/matrix_templates/units.py b/templating/matrix_templates/units.py
deleted file mode 100644
index 051b38d7..00000000
--- a/templating/matrix_templates/units.py
+++ /dev/null
@@ -1,840 +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.
-"""
-Contains all the units for the spec.
-
-This file loads swagger and JSON schema files and parses out the useful bits
-and returns them as Units for use in Batesian.
-
-For the actual conversion of data -> RST (including templates), see the sections
-file instead.
-"""
-from batesian.units import Units
-from collections import OrderedDict
-import logging
-import inspect
-import json
-import os
-import re
-import subprocess
-import sys
-import urllib
-import yaml
-
-HTTP_APIS = {
- "../api/application-service": "as",
- "../api/client-server": "cs",
- "../api/identity": "is",
- "../api/push-gateway": "push",
-}
-EVENT_EXAMPLES = "../event-schemas/examples"
-EVENT_SCHEMA = "../event-schemas/schema"
-CORE_EVENT_SCHEMA = "../event-schemas/schema/core-event-schema"
-CHANGELOG_DIR = "../changelogs"
-TARGETS = "../specification/targets.yaml"
-
-ROOM_EVENT = "core-event-schema/room_event.yaml"
-STATE_EVENT = "core-event-schema/state_event.yaml"
-
-logger = logging.getLogger(__name__)
-
-# a yaml Loader which loads mappings into OrderedDicts instead of regular
-# dicts, so that we preserve the ordering of properties from the api files.
-#
-# with thanks to http://stackoverflow.com/a/21912744/637864
-class OrderedLoader(yaml.Loader):
- pass
-def construct_mapping(loader, node):
- loader.flatten_mapping(node)
- pairs = loader.construct_pairs(node)
- return OrderedDict(pairs)
-OrderedLoader.add_constructor(
- yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
- construct_mapping)
-
-def resolve_references(path, schema):
- if isinstance(schema, dict):
- # do $ref first
- if '$ref' in schema:
- value = schema['$ref']
- path = os.path.join(os.path.dirname(path), value)
- with open(path) as f:
- ref = yaml.load(f, OrderedLoader)
- result = resolve_references(path, ref)
- del schema['$ref']
- else:
- result = OrderedDict()
-
- for key, value in schema.items():
- result[key] = resolve_references(path, value)
- return result
- elif isinstance(schema, list):
- return [resolve_references(path, value) for value in schema]
- else:
- return schema
-
-
-def inherit_parents(obj):
- """
- Recurse through the 'allOf' declarations in the object
- """
- logger.debug("inherit_parents %r" % obj)
- parents = obj.get("allOf", [])
- if not parents:
- return obj
-
- result = {}
-
- # settings defined in the child take priority over the parents, so we
- # iterate through the parents first, and then overwrite with the settings
- # from the child.
- for p in map(inherit_parents, parents) + [obj]:
- for key in ('title', 'type', 'required', 'description'):
- if p.get(key):
- result[key] = p[key]
-
- for key in ('properties', 'additionalProperties', 'patternProperties'):
- if p.get(key):
- result.setdefault(key, OrderedDict()).update(p[key])
-
- return result
-
-
-def get_json_schema_object_fields(obj, enforce_title=False):
- # Algorithm:
- # f.e. property => add field info (if field is object then recurse)
- if obj.get("type") != "object":
- raise Exception(
- "get_json_schema_object_fields: Object %s isn't an object." % obj
- )
-
- obj_title = obj.get("title")
-
- logger.debug("Processing object with title '%s'", obj_title)
-
- if enforce_title and not obj_title:
- # Force a default titile of "NO_TITLE" to make it obvious in the
- # specification output which parts of the schema are missing a title
- obj_title = 'NO_TITLE'
-
- additionalProps = obj.get("additionalProperties")
- props = obj.get("properties")
- if additionalProps and not props:
- # not "really" an object, just a KV store
- logger.debug("%s is a pseudo-object", obj_title)
-
- key_type = additionalProps.get("x-pattern", "string")
- res = process_data_type(additionalProps)
- return {
- "type": "{%s: %s}" % (key_type, res["type"]),
- "tables": res["tables"],
- }
-
- if not props:
- props = obj.get("patternProperties")
- if props:
- # try to replace horrible regex key names with pretty x-pattern ones
- for key_name in props.keys():
- pretty_key = props[key_name].get("x-pattern")
- if pretty_key:
- props[pretty_key] = props[key_name]
- del props[key_name]
-
- # Sometimes you just want to specify that a thing is an object without
- # doing all the keys.
- if not props:
- return {
- "type": obj_title,
- "tables": [],
- }
-
- required_keys = set(obj.get("required", []))
-
- first_table_rows = []
- tables = []
-
- for key_name in props:
- try:
- logger.debug("Processing property %s.%s", obj_title, key_name)
- required = key_name in required_keys
- res = process_data_type(props[key_name], required)
-
- first_table_rows.append({
- "key": key_name,
- "type": res["type"],
- "required": required,
- "desc": res["desc"],
- })
- tables.extend(res["tables"])
- logger.debug("Done property %s" % key_name)
-
- except Exception, e:
- e2 = Exception("Error reading property %s.%s: %s" %
- (obj_title, key_name, str(e)))
- # throw the new exception with the old stack trace, so that
- # we don't lose information about where the error occurred.
- raise e2, None, sys.exc_info()[2]
-
- tables.insert(0, {
- "title": obj_title,
- "rows": first_table_rows,
- })
-
- return {
- "type": obj_title,
- "tables": tables,
- }
-
-
-# process a data type definition. returns a dictionary with the keys:
-# type: stringified type name
-# desc: description
-# enum_desc: description of permissible enum fields
-# is_object: true if the data type is an object
-# tables: list of additional table definitions
-def process_data_type(prop, required=False, enforce_title=True):
- prop = inherit_parents(prop)
-
- prop_type = prop['type']
- tables = []
- enum_desc = None
- is_object = False
-
- if prop_type == "object":
- res = get_json_schema_object_fields(
- prop,
- enforce_title=enforce_title,
- )
- prop_type = res["type"]
- tables = res["tables"]
- is_object = True
-
- elif prop_type == "array":
- nested = process_data_type(prop["items"])
- prop_type = "[%s]" % nested["type"]
- tables = nested["tables"]
- enum_desc = nested["enum_desc"]
-
- if prop.get("enum"):
- if len(prop["enum"]) > 1:
- prop_type = "enum"
- enum_desc = (
- "One of: %s" % json.dumps(prop["enum"])
- )
- else:
- enum_desc = (
- "Must be '%s'." % prop["enum"][0]
- )
-
- if isinstance(prop_type, list):
- prop_type = " or ".join(prop_type)
-
-
- rq = "**Required.**" if required else None
- desc = " ".join(x for x in [rq, prop.get("description"), enum_desc] if x)
-
- return {
- "type": prop_type,
- "desc": desc,
- "enum_desc": enum_desc,
- "is_object": is_object,
- "tables": tables,
- }
-
-def deduplicate_tables(tables):
- # the result may contain duplicates, if objects are referred to more than
- # once. Filter them out.
- #
- # Go through the tables backwards so that we end up with a breadth-first
- # rather than depth-first ordering.
-
- titles = set()
- filtered = []
- for table in reversed(tables):
- if table.get("no-table"):
- continue
-
- if table.get("title") in titles:
- continue
-
- titles.add(table.get("title"))
- filtered.append(table)
- filtered.reverse()
-
- return filtered
-
-def get_tables_for_schema(schema):
- pv = process_data_type(schema, enforce_title=False)
- return deduplicate_tables(pv["tables"])
-
-def get_tables_for_response(schema):
- pv = process_data_type(schema, enforce_title=False)
- tables = deduplicate_tables(pv["tables"])
-
- # make up the first table, with just the 'body' row in, unless the response
- # is an object, in which case there's little point in having one.
- if not pv["is_object"]:
- tables = [{
- "title": None,
- "rows": [{
- "key": "",
- "type": pv["type"],
- "desc": pv["desc"],
- }]
- }] + tables
-
- logger.debug("response: %r" % tables)
-
- return tables
-
-def get_example_for_schema(schema):
- """Returns a python object representing a suitable example for this object"""
- schema = inherit_parents(schema)
- if 'example' in schema:
- example = schema['example']
- return example
-
- proptype = schema['type']
-
- if proptype == 'object':
- if 'properties' not in schema:
- raise Exception('"object" property has neither properties nor example')
- res = OrderedDict()
- for prop_name, prop in schema['properties'].iteritems():
- logger.debug("Parsing property %r" % prop_name)
- prop_example = get_example_for_schema(prop)
- res[prop_name] = prop_example
- return res
-
- if proptype == 'array':
- if 'items' not in schema:
- raise Exception('"array" property has neither items nor example')
- return [get_example_for_schema(schema['items'])]
-
- if proptype == 'integer':
- return 0
-
- if proptype == 'string':
- return proptype
-
- raise Exception("Don't know to make an example %s" % proptype)
-
-def get_example_for_param(param):
- """Returns a stringified example for a parameter"""
- if 'x-example' in param:
- return param['x-example']
- schema = param.get('schema')
- if not schema:
- return None
-
- # allow examples for the top-level object to be in formatted json
- exampleobj = None
- if 'example' in schema:
- exampleobj = schema['example']
- if isinstance(exampleobj, basestring):
- return exampleobj
-
- if exampleobj is None:
- exampleobj = get_example_for_schema(schema)
-
- return json.dumps(exampleobj, indent=2)
-
-def get_example_for_response(response):
- """Returns a stringified example for a response"""
- exampleobj = None
- if 'examples' in response:
- exampleobj = response["examples"].get("application/json")
- # the openapi spec suggests that examples in the 'examples' section should
- # be formatted as raw objects rather than json-formatted strings, but we
- # have lots of the latter in our spec, which work with the swagger UI,
- # so grandfather them in.
- if isinstance(exampleobj, basestring):
- return exampleobj
-
- if exampleobj is None:
- schema = response.get('schema')
- if schema:
- if schema['type'] == 'file':
- # no example for 'file' responses
- return None
- exampleobj = get_example_for_schema(schema)
-
- if exampleobj is None:
- return None
-
- return json.dumps(exampleobj, indent=2)
-
-class MatrixUnits(Units):
- def _load_swagger_meta(self, api, group_name):
- endpoints = []
- for path in api["paths"]:
- for method in api["paths"][path]:
- single_api = api["paths"][path][method]
- full_path = api.get("basePath", "").rstrip("/") + path
- endpoint = {
- "title": single_api.get("summary", ""),
- "deprecated": single_api.get("deprecated", False),
- "desc": single_api.get("description", single_api.get("summary", "")),
- "method": method.upper(),
- "path": full_path.strip(),
- "requires_auth": "security" in single_api,
- "rate_limited": 429 in single_api.get("responses", {}),
- "req_param_by_loc": {},
- "req_body_tables": [],
- "res_headers": [],
- "res_tables": [],
- "responses": [],
- "example": {
- "req": "",
- }
- }
- logger.info(" ------- Endpoint: %s %s ------- " % (method, path))
-
- path_template = api.get("basePath", "").rstrip("/") + path
- example_query_params = []
- example_body = ""
-
- for param in single_api.get("parameters", []):
- # even body params should have names, otherwise the active docs don't work.
- param_name = param["name"]
-
- try:
- param_loc = param["in"]
-
- if param_loc == "body":
- self._handle_body_param(param, endpoint)
- example_body = get_example_for_param(param)
- continue
-
- # description
- desc = param.get("description", "")
- if param.get("required"):
- desc = "**Required.** " + desc
-
- # assign value expected for this param
- val_type = param.get("type") # integer/string
-
- if param.get("enum"):
- val_type = "enum"
- desc += (
- " One of: %s" % json.dumps(param.get("enum"))
- )
-
- endpoint["req_param_by_loc"].setdefault(param_loc, []).append({
- "key": param_name,
- "type": val_type,
- "desc": desc
- })
-
- example = get_example_for_param(param)
- if example is None:
- continue
-
- if param_loc == "path":
- path_template = path_template.replace(
- "{%s}" % param_name, urllib.quote(example)
- )
- elif param_loc == "query":
- if type(example) == list:
- for value in example:
- example_query_params.append((param_name, value))
- else:
- example_query_params.append((param_name, example))
-
- except Exception, e:
- raise Exception("Error handling parameter %s" % param_name, e)
- # endfor[param]
-
- good_response = None
- for code in sorted(single_api.get("responses", {}).keys()):
- res = single_api["responses"][code]
- if not good_response and code == 200:
- good_response = res
- description = res.get("description", "")
- example = get_example_for_response(res)
- endpoint["responses"].append({
- "code": code,
- "description": description,
- "example": example,
- })
-
- # add response params if this API has any.
- if good_response:
- if "schema" in good_response:
- endpoint["res_tables"] = get_tables_for_response(
- good_response["schema"]
- )
- if "headers" in good_response:
- headers = []
- for (header_name, header) in good_response["headers"].iteritems():
- headers.append({
- "key": header_name,
- "type": header["type"],
- "desc": header["description"],
- })
- endpoint["res_headers"] = headers
-
- query_string = "" if len(example_query_params) == 0 else "?"+urllib.urlencode(example_query_params)
- if example_body:
- endpoint["example"]["req"] = "%s %s%s HTTP/1.1\nContent-Type: application/json\n\n%s" % (
- method.upper(), path_template, query_string, example_body
- )
- else:
- endpoint["example"]["req"] = "%s %s%s HTTP/1.1\n\n" % (
- method.upper(), path_template, query_string
- )
-
- endpoints.append(endpoint)
-
- return {
- "base": api.get("basePath").rstrip("/"),
- "group": group_name,
- "endpoints": endpoints,
- }
-
-
- def _handle_body_param(self, param, endpoint_data):
- """Update endpoint_data object with the details of the body param
- :param string filepath path to the yaml
- :param dict param the parameter data from the yaml
- :param dict endpoint_data dictionary of endpoint data to be updated
- """
- try:
- schema = inherit_parents(param["schema"])
- if schema["type"] != "object":
- logger.warn(
- "Unsupported body type %s for %s %s", schema["type"],
- endpoint_data["method"], endpoint_data["path"]
- )
- return
-
- req_body_tables = get_tables_for_schema(schema)
-
- if req_body_tables == []:
- # no fields defined for the body.
- return
-
- # put the top-level parameters into 'req_param_by_loc', and the others
- # into 'req_body_tables'
- body_params = endpoint_data['req_param_by_loc'].setdefault("JSON body",[])
- body_params.extend(req_body_tables[0]["rows"])
-
- body_tables = req_body_tables[1:]
- endpoint_data['req_body_tables'].extend(body_tables)
-
- except Exception, e:
- e2 = Exception(
- "Error decoding body of API endpoint %s %s: %s" %
- (endpoint_data["method"], endpoint_data["path"], e)
- )
- raise e2, None, sys.exc_info()[2]
-
-
- def load_swagger_apis(self):
- apis = {}
- for path, suffix in HTTP_APIS.items():
- for filename in os.listdir(path):
- if not filename.endswith(".yaml"):
- continue
- logger.info("Reading swagger API: %s" % filename)
- filepath = os.path.join(path, filename)
- with open(filepath, "r") as f:
- # strip .yaml
- group_name = filename[:-5].replace("-", "_")
- group_name = "%s_%s" % (group_name, suffix)
- api = yaml.load(f.read(), OrderedLoader)
- api = resolve_references(filepath, api)
- api["__meta"] = self._load_swagger_meta(
- api, group_name
- )
- apis[group_name] = api
- return apis
-
- def load_common_event_fields(self):
- path = CORE_EVENT_SCHEMA
- event_types = {}
-
- for (root, dirs, files) in os.walk(path):
- for filename in files:
- if not filename.endswith(".yaml"):
- continue
-
- event_type = filename[:-5] # strip the ".yaml"
- filepath = os.path.join(root, filename)
- with open(filepath) as f:
- try:
- event_info = yaml.load(f, OrderedLoader)
- except Exception as e:
- raise ValueError(
- "Error reading file %r" % (filepath,), e
- )
-
- if "event" not in event_type:
- continue # filter ImageInfo and co
-
- table = {
- "title": event_info["title"],
- "desc": event_info["description"],
- "rows": []
- }
-
- for prop in sorted(event_info["properties"]):
- row = {
- "key": prop,
- "type": event_info["properties"][prop]["type"],
- "desc": event_info["properties"][prop].get("description","")
- }
- table["rows"].append(row)
-
- event_types[event_type] = table
- return event_types
-
- def load_apis(self, substitutions):
- cs_ver = substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable")
- fed_ver = substitutions.get("%SERVER_RELEASE_LABEL%", "unstable")
- return {
- "rows": [{
- "key": "`Client-Server API `_",
- "type": cs_ver,
- "desc": "Interaction between clients and servers",
- }, {
- "key": "`Server-Server API `_",
- "type": fed_ver,
- "desc": "Federation between servers",
- }, {
- "key": "`Application Service API `_",
- "type": "unstable",
- "desc": "Privileged server plugins",
- }, {
- "key": "`Identity Service API `_",
- "type": "unstable",
- "desc": "Mapping of third party IDs to Matrix IDs",
- }, {
- "key": "`Push Gateway API `_",
- "type": "unstable",
- "desc": "Push notifications for Matrix events",
- }]
- }
-
- def load_event_examples(self):
- path = EVENT_EXAMPLES
- examples = {}
- for filename in os.listdir(path):
- if not filename.startswith("m."):
- continue
- with open(os.path.join(path, filename), "r") as f:
- event_name = filename.split("#")[0]
- example = json.loads(f.read())
-
- examples[filename] = examples.get(filename, [])
- examples[filename].append(example)
- if filename != event_name:
- examples[event_name] = examples.get(event_name, [])
- examples[event_name].append(example)
- return examples
-
- def load_event_schemas(self):
- path = EVENT_SCHEMA
- schemata = {}
-
- for filename in os.listdir(path):
- if not filename.startswith("m."):
- continue
- filepath = os.path.join(path, filename)
- try:
- schemata[filename] = self.read_event_schema(filepath)
- except Exception, e:
- e2 = Exception("Error reading event schema "+filepath+": "+
- str(e))
- # throw the new exception with the old stack trace, so that
- # we don't lose information about where the error occurred.
- raise e2, None, sys.exc_info()[2]
-
- return schemata
-
- def read_event_schema(self, filepath):
- logger.info("Reading %s" % filepath)
-
- with open(filepath, "r") as f:
- json_schema = yaml.load(f, OrderedLoader)
-
- schema = {
- "typeof": "",
- "typeof_info": "",
- "type": None,
- "title": None,
- "desc": None,
- "msgtype": None,
- "content_fields": [
- # {
- # title: " key"
- # rows: [
- # { key: , type: ,
- # desc: , required: }
- # ]
- # }
- ]
- }
-
- # add typeof
- base_defs = {
- ROOM_EVENT: "Message Event",
- STATE_EVENT: "State Event"
- }
- if type(json_schema.get("allOf")) == list:
- firstRef = json_schema["allOf"][0]["$ref"]
- if firstRef in base_defs:
- schema["typeof"] = base_defs[firstRef]
-
- json_schema = resolve_references(filepath, json_schema)
-
- # add type
- schema["type"] = Units.prop(
- json_schema, "properties/type/enum"
- )[0]
-
- # add summary and desc
- schema["title"] = json_schema.get("title")
- schema["desc"] = json_schema.get("description", "")
-
- # walk the object for field info
- schema["content_fields"] = get_tables_for_schema(
- Units.prop(json_schema, "properties/content")
- )
-
- # This is horrible because we're special casing a key on m.room.member.
- # We need to do this because we want to document a non-content object.
- if schema["type"] == "m.room.member":
- invite_room_state = get_tables_for_schema(
- json_schema["properties"]["invite_room_state"]["items"],
- )
- schema["content_fields"].extend(invite_room_state)
-
-
- # grab msgtype if it is the right kind of event
- msgtype = Units.prop(
- json_schema, "properties/content/properties/msgtype/enum"
- )
- if msgtype:
- schema["msgtype"] = msgtype[0] # enum prop
-
- # link to msgtypes for m.room.message
- if schema["type"] == "m.room.message" and not msgtype:
- schema["desc"] += (
- " For more information on ``msgtypes``, see "+
- "`m.room.message msgtypes`_."
- )
-
- # Assign state key info if it has some
- if schema["typeof"] == "State Event":
- skey_desc = Units.prop(
- json_schema, "properties/state_key/description"
- )
- if not skey_desc:
- raise Exception("Missing description for state_key")
- schema["typeof_info"] = "``state_key``: %s" % skey_desc
-
- return schema
-
- def load_changelogs(self):
- changelogs = {}
-
- for f in os.listdir(CHANGELOG_DIR):
- if not f.endswith(".rst"):
- continue
- path = os.path.join(CHANGELOG_DIR, f)
- name = f[:-4]
-
- title_part = None
- changelog_lines = []
- with open(path, "r") as f:
- lines = f.readlines()
- prev_line = None
- for line in lines:
- if prev_line is None:
- prev_line = line
- continue
- if not title_part:
- # find the title underline (at least 3 =)
- if re.match("^[=]{3,}$", line.strip()):
- title_part = prev_line
- continue
- prev_line = line
- else: # have title, get body (stop on next title or EOF)
- if re.match("^[=]{3,}$", line.strip()):
- # we added the title in the previous iteration, pop it
- # then bail out.
- changelog_lines.pop()
- break
- changelog_lines.append(" " + line)
- changelogs[name] = "".join(changelog_lines)
-
- return changelogs
-
-
- def load_spec_targets(self):
- with open(TARGETS, "r") as f:
- return yaml.load(f.read())
-
-
- def load_git_version(self):
- null = open(os.devnull, 'w')
- cwd = os.path.dirname(os.path.abspath(__file__))
- try:
- git_branch = subprocess.check_output(
- ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
- stderr=null,
- cwd=cwd,
- ).strip()
- except subprocess.CalledProcessError:
- git_branch = ""
- try:
- git_tag = subprocess.check_output(
- ['git', 'describe', '--exact-match'],
- stderr=null,
- cwd=cwd,
- ).strip()
- git_tag = "tag=" + git_tag
- except subprocess.CalledProcessError:
- git_tag = ""
- try:
- git_commit = subprocess.check_output(
- ['git', 'rev-parse', '--short', 'HEAD'],
- stderr=null,
- cwd=cwd,
- ).strip()
- except subprocess.CalledProcessError:
- git_commit = ""
- try:
- dirty_string = "-this_is_a_dirty_checkout"
- is_dirty = subprocess.check_output(
- ['git', 'describe', '--dirty=' + dirty_string, "--all"],
- stderr=null,
- cwd=cwd,
- ).strip().endswith(dirty_string)
- git_dirty = "dirty" if is_dirty else ""
- except subprocess.CalledProcessError:
- git_dirty = ""
-
- git_version = "Unknown"
- if git_branch or git_tag or git_commit or git_dirty:
- git_version = ",".join(
- s for s in
- (git_branch, git_tag, git_commit, git_dirty,)
- if s
- ).encode("ascii")
- return {
- "string": git_version,
- "revision": git_commit
- }