Compare commits

..

No commits in common. "main" and "0.2.0" have entirely different histories.
main ... 0.2.0

921 changed files with 30519 additions and 72694 deletions

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @matrix-org/spec-core-team

2
.github/FUNDING.yml vendored
View file

@ -1,2 +0,0 @@
patreon: matrixdotorg
liberapay: matrixdotorg

View file

@ -1,13 +0,0 @@
---
name: Clarity problem
about: Report an area of the spec that is unclear.
title: ''
labels: 'clarification'
assignees: ''
---
**Link to problem area**:
**Issue**
What is wrong? How can we improve?

View file

@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Matrix Spec Discussion
url: "https://matrix.to/#/#matrix-spec:matrix.org"
about: Questions about the spec and proposal process can be asked here.
- name: Matrix Security Policy
url: https://www.matrix.org/security-disclosure-policy/
about: Learn more about our security disclosure policy.

View file

@ -1,13 +0,0 @@
---
name: Cosmetic issue
about: Report an issue with how the spec looks.
title: ''
labels: 'aesthetic'
assignees: ''
---
**Link to problem area**:
**Issue**
What is wrong? What can we do to improve?

View file

@ -1,12 +0,0 @@
---
name: Spec idea
about: Suggest a future MSC idea.
title: ''
labels: 'improvement'
assignees: ''
---
**Suggestion**
What would you like to see in Matrix? If your idea is vaguely complete enough, we
recommend submitting [an MSC](https://matrix.org/docs/spec/proposals) instead.

View file

@ -1,37 +0,0 @@
---
name: '[SCT] Release checklist'
about: 'Used by the Spec Core Team to create a new release.'
title: 'Matrix 1.X'
labels: 'release-blocker'
assignees: ''
---
<!-- ------------------------------------------------------------------------ -->
<!-- Please asssign the release coordinator (probably yourself) to this issue -->
<!-- ------------------------------------------------------------------------ -->
Date: **Thursday, May 25, 2023** <!-- CHANGE ME -->
Previous release: <!-- LINK TO LAST RELEASE'S CHECKLIST -->
Preflight checklist ([release steps](https://github.com/matrix-org/matrix-spec/blob/main/meta/releasing.md)):
* [ ] Pin this issue to the repo.
* [ ] Ensure the social media account holders are available for the release day.
* [ ] Blog post written.
* [ ] Check for release blockers that may have been missed.
* [ ] Review/fix the changelog.
Release checklist ([release steps](https://github.com/matrix-org/matrix-spec/blob/main/meta/releasing.md)):
* [ ] Branch stuffs.
* [ ] Github release artifact.
* [ ] Published to spec.matrix.org.
* [ ] All links work.
* [ ] Publish blog post.
* [ ] Announce in #matrix-spec, client developers, HS developers, SCT office, and other rooms as warranted.
* [ ] Post to Twitter/Mastodon.
* [ ] Close this issue.
* [ ] Unpin this issue from the repo.
Known release blockers:
* [ ] <!-- Issue/PR link -->

View file

@ -1,16 +0,0 @@
---
name: Documentation error
about: Report an issue with the spec itself (incorrect text).
title: ''
labels: 'spec-bug'
assignees: ''
---
**Link to problem area**:
**Issue**
What is wrong?
**Expected behaviour**
How can the issue be fixed? Links to implementations/documents which prove the spec is wrong are appreciated.

13
.github/_typos.toml vendored
View file

@ -1,13 +0,0 @@
[files]
extend-exclude = ["/themes", "/attic", "/data-definitions", "*.css", "syntax.scss", "package-lock.json"]
[default]
check-filename = true
[default.extend-identifiers]
au1ba7o = "au1ba7o"
[default.extend-words]
Appy = "Appy"
fo = "fo"
Iy = "Iy"

View file

@ -1,8 +0,0 @@
### Pull Request Checklist
<!-- Please read CONTRIBUTING.rst before submitting your pull request -->
* [ ] Pull request includes a [changelog file](https://github.com/matrix-org/matrix-spec/blob/master/CONTRIBUTING.rst#adding-to-the-changelog)
* [ ] Pull request includes a [sign off](https://github.com/matrix-org/matrix-spec/blob/master/CONTRIBUTING.rst#sign-off)
* [ ] Pull request is classified as ['other changes'](https://github.com/matrix-org/matrix-spec/blob/master/CONTRIBUTING.rst#other-changes)

View file

@ -1,18 +0,0 @@
# workflow steps that ought to pass on a PR, but shouldn't block a preview.
name: "Checks"
on:
pull_request:
jobs:
check-newsfragments:
name: "🔎 Check that new newsfragments are valid"
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: scripts/check-newsfragments
env:
PULL_REQUEST_NUMBER: ${{ github.event.number }}

View file

@ -1,312 +0,0 @@
name: "Spec"
env:
HUGO_VERSION: 0.148.1
PYTHON_VERSION: 3.13
on:
push:
branches:
- main
tags:
- v*
pull_request:
workflow_dispatch:
schedule:
# Run this workflow every day at 2am. This helps keep the page of
# current spec proposals up-to-date.
- cron: '0 2 * * *'
jobs:
validate-openapi:
name: "🔎 Validate OpenAPI specifications"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Node"
uses: actions/setup-node@v4
with:
node-version: '20'
- name: "🔎 Run validator"
run: |
npx @redocly/cli@latest lint data/api/*/*.yaml
check-event-examples:
name: "🔎 Check Event schema examples"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "🔎 Run validator"
run: |
python scripts/check-event-schema-examples.py
check-openapi-examples:
name: "🔎 Check OpenAPI definitions examples"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "🔎 Run validator"
run: |
python scripts/check-openapi-sources.py
check-schemas-examples:
name: "🔎 Check JSON Schemas inline examples"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "🔎 Run validator"
run: |
python scripts/check-json-schemas.py
calculate-baseurl:
name: "⚙️ Calculate baseURL for later jobs"
runs-on: ubuntu-latest
outputs:
baseURL: "${{ steps.set-baseurl.outputs.baseURL }}"
steps:
# For PRs, set the baseURL to `/`.
# For releases, set the baseURL to `/$tag` (eg: `/v1.2`).
# Otherwise, set it to `/unstable`.
- name: "⚙️ Calculate baseURL"
id: set-baseurl
# Double brackets on the elif to avoid auto-escaping refs/tags/* because we need
# the asterisk matching behaviour, not the literal string.
run: |
if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then
echo "baseURL=/" >> "$GITHUB_OUTPUT"
elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
echo "baseURL=/${GITHUB_REF/refs\/tags\//}" >> "$GITHUB_OUTPUT"
else
echo "baseURL=/unstable" >> "$GITHUB_OUTPUT"
fi
build-openapi:
name: "🐍 Build OpenAPI definitions"
runs-on: ubuntu-latest
needs: [calculate-baseurl]
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "📦 Asset creation"
run: |
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
export RELEASE="${GITHUB_REF/refs\/tags\//}"
else
export RELEASE="unstable"
fi
# The output path matches the final deployment path at spec.matrix.org
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api application-service \
-r "$RELEASE" \
-o spec/application-service-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api client-server \
-r "$RELEASE" \
-o spec/client-server-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api push-gateway \
-r "$RELEASE" \
-o spec/push-gateway-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api server-server \
-r "$RELEASE" \
-o spec/server-server-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api identity \
-r "$RELEASE" \
-o spec/identity-service-api/api.json
tar -czf openapi.tar.gz spec
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: openapi-artifact
path: openapi.tar.gz
generate-changelog:
name: "📢 Run towncrier for changelog"
# skip for builds of git tags
if: "!startsWith(github.ref, 'refs/tags/')"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: " Install towncrier"
run: "pip install 'towncrier'"
- name: "Generate changelog"
run: ./scripts/generate-changelog.sh vUNSTABLE
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: changelog-artifact
path: content/changelog/unstable.md
build-spec:
name: "📖 Build the spec"
runs-on: ubuntu-latest
needs: [calculate-baseurl, build-openapi, generate-changelog]
# run even if generate-changelog was skipped
if: ${{ always() }}
steps:
- name: " Setup Node"
uses: actions/setup-node@v4
with:
node-version: '20'
- name: " Setup Hugo"
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3.0.0
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: "⚙️ npm"
run: |
npm i
npm run get-proposals
- name: "📥 Download generated changelog"
if: "needs.generate-changelog.result == 'success'"
uses: actions/download-artifact@v4
with:
name: changelog-artifact
path: content/changelog
- name: "⚙️ hugo"
run: hugo --baseURL "${{ needs.calculate-baseurl.outputs.baseURL }}" -d "spec"
# We manually unpack the spec OpenAPI definition JSON to the website tree
# to make it available to the world in a canonical place:
# https://spec.matrix.org/latest/client-server-api/api.json
# Works for /unstable/ and /v1.1/ as well.
- name: "📥 Spec definition download"
uses: actions/download-artifact@v4
with:
name: openapi-artifact
- name: "📝 Unpack the OpenAPI definitions in the right location"
run: |
tar -xzf openapi.tar.gz
- name: "📦 Tarball creation"
run: tar -czf spec.tar.gz spec
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: spec-artifact
path: spec.tar.gz
htmlcheck:
name: "🔎 Validate generated HTML"
runs-on: ubuntu-latest
needs: [calculate-baseurl, build-spec]
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: "📥 Fetch built spec"
uses: actions/download-artifact@v4
with:
name: spec-artifact
- name: "📝 Unpack the spec"
# we have to unpack it into the right path given the baseurl, so that the
# links are correct.
# eg if baseurl is `/unstable`, we want to put the site in `spec/unstable`.
run: |
mkdir -p "spec${baseURL}"
tar -C "spec${baseURL}" --strip-components=1 -xvzf spec.tar.gz
env:
baseURL: "${{ needs.calculate-baseurl.outputs.baseURL }}"
- name: "Run htmltest"
uses: wjdp/htmltest-action@master
with:
config: .htmltest.yml
build-historical-spec:
name: "📖 Build the historical backup spec"
runs-on: ubuntu-latest
needs: [build-openapi]
if: ${{ startsWith(github.ref, 'refs/tags/') }}
steps:
- name: " Setup Node"
uses: actions/setup-node@v4
with:
node-version: '20'
- name: " Setup Hugo"
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3.0.0
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: "⚙️ npm"
run: |
npm i
npm run get-proposals
- name: "⚙️ hugo"
env:
HUGO_PARAMS_VERSION_STATUS: "historical"
# Create a baseURL like `/v1.2` out of the `v1.2` tag
run: |
hugo --baseURL "/${GITHUB_REF/refs\/tags\//}" -d "spec"
- name: "📥 Spec definition download"
uses: actions/download-artifact@v4
with:
name: openapi-artifact
- name: "📝 Unpack the OpenAPI definitions in the right location"
run: |
tar -xzf openapi.tar.gz
- name: "📦 Tarball creation"
run: tar -czf spec-historical.tar.gz spec
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: spec-historical-artifact
path: spec-historical.tar.gz

View file

@ -1,72 +0,0 @@
# GHA workflow which publishes previews of spec PRs to netlify.
#
# We keep this in a separate workflow to the main spec build, because it
# requires access to the Netlify secret. By having it run on `workflow_run`, we
# will only use the workflow definition file on the default branch, so we can
# ensure that the secret can't be exfiltrated.
#
name: Upload Preview Build to Netlify
on:
workflow_run:
workflows: [Spec]
types: [completed]
jobs:
build:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
steps:
- name: "dump context data"
run: |
jq . < $GITHUB_EVENT_PATH
- name: "🔍 Read PR number"
id: readctx
# we need to find the PR number that corresponds to the branch, which we do by
# searching the GH API
env:
OWNER_LOGIN: ${{ github.event.workflow_run.head_repository.owner.login }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
run: |
head_branch="${OWNER_LOGIN}:${HEAD_BRANCH}"
echo "head branch: $head_branch"
pulls_uri="https://api.github.com/repos/${{ github.repository }}/pulls?head=$(jq -Rr '@uri' <<<$head_branch)"
pr_number=$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' "$pulls_uri" |
jq -r '.[] | .number')
echo "PR number: $pr_number"
echo "prnumber=$pr_number" >> "$GITHUB_OUTPUT"
- name: '📥 Download artifact'
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
with:
workflow: main.yaml
run_id: ${{ github.event.workflow_run.id }}
name: spec-artifact
- name: "📦 Extract Artifacts"
run: tar -xzvf spec.tar.gz && rm spec.tar.gz
- name: "📤 Deploy to Netlify"
id: netlify
uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0.0
with:
publish-dir: spec
deploy-message: "Deploy from GitHub Actions"
enable-pull-request-comment: false
enable-commit-comment: false
alias: pr${{ steps.readctx.outputs.prnumber }}
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
timeout-minutes: 1
- name: "📝 Edit PR Description"
# v1.0.1
uses: Beakyn/gha-comment-pull-request@2167a7aee24f9e61ce76a23039f322e49a990409
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
pull-request-number: ${{ steps.readctx.outputs.prnumber }}
description-message: |
Preview: ${{ steps.netlify.outputs.deploy-url }}

View file

@ -1,43 +0,0 @@
name: Release packages
on:
release:
types: [published]
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
# Releases to npm after bumping the package.json version from 0.0.0 to $TAG.0 as the tags only contain MAJOR.MINOR
npm:
name: Publish to npm
runs-on: ubuntu-latest
if: github.event.release.prerelease == false
defaults:
run:
working-directory: packages/npm
permissions:
contents: read
id-token: write
steps:
- name: 🧮 Checkout code
uses: actions/checkout@v4
- name: 🔧 Yarn cache
uses: actions/setup-node@v4
with:
cache: "yarn"
cache-dependency-path: packages/npm/yarn.lock
registry-url: "https://registry.npmjs.org"
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: 🔨 Install dependencies
run: "yarn install --frozen-lockfile"
# We bump the package.json version to git, we just need it for publish to do the right thing
- name: 🎖 Bump package.json version
run: "yarn version --new-version ${VERSION#v} --no-git-tag-version"
env:
VERSION: ${{ github.event.release.tag_name }}.0
- name: 🚀 Publish to npm
run: npm publish --provenance --access public --tag latest

View file

@ -1,19 +0,0 @@
name: Spell Check
on:
push:
branches:
- main
pull_request:
jobs:
run:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v4
- name: Check spelling of proposals
uses: crate-ci/typos@f2c1f08a7b3c1b96050cb786baaa2a94797bdb7d # v1.20.10
with:
config: ${{github.workspace}}/.github/_typos.toml

23
.gitignore vendored
View file

@ -1,17 +1,8 @@
node_modules
/data/msc
/env*
/resources
/scripts/openapi
/scripts/tmp
/hugo-config.toml
/public
scripts/gen
scripts/continuserv/continuserv
scripts/speculator/speculator
templating/out
*.pyc
*.swp
_rendered.rst
/.vscode/
/.idea/
/spec/
changelogs/rendered.*
.hugo_build.lock
hugo_stats.json
supporting-docs/_site
supporting-docs/.sass-cache
api/node_modules

View file

@ -1,7 +0,0 @@
# config file for htmltest. This is used by one of the checks in Github
# Actions.
IgnoreDirectoryMissingTrailingSlash: true
DirectoryPath: spec
CheckExternal: false
IgnoreInternalEmptyHash: true

41
CHANGELOG.rst Normal file
View file

@ -0,0 +1,41 @@
.. This file is automatically processed by the templating system. To make it
.. happy, you MUST use '=' as the title underline and you MUST stick the version
.. in the title. The version MUST follow the numbering format
.. "v<num>.<num>.<num>" - You cannot use a-z. If the templating system fails to
.. find the right info, it will be treated as a test failure and so will show up
.. in Jenkins. Comments like this are ignored by both RST and the templating
.. system. Add the newest release notes beneath this comment.
Specification changes in v0.2.0 (2015-10-02)
============================================
This update fundamentally restructures the specification. The specification has
been split into more digestible "modules" which each describe a particular
function (e.g. typing). This was done in order make the specification easier to
maintain and help define which modules are mandatory for certain types
of clients. Types of clients along with the mandatory modules can be found in a
new "Feature Profiles" section. This update also begins to aggressively
standardise on using Swagger and JSON Schema to document HTTP endpoints and
Events respectively. It also introduces a number of new concepts to Matrix.
Additions:
- New section: Feature Profiles.
- New section: Receipts.
- New section: Room history visibility.
- New event: ``m.receipt``.
- New event: ``m.room.canonical_alias``
- New event: ``m.room.history_visibility``
- New keys: ``/createRoom`` - allows room "presets" using ``preset`` and
``initial_state`` keys.
- New endpoint: ``/tokenrefresh`` - Related to refreshing access tokens.
Modifications:
- Convert most of the older HTTP APIs to Swagger documentation.
- Convert most of the older event formats to JSON Schema.
- Move selected client-server sections to be "Modules".
Specification changes in v0.1.0 (2015-06-01)
============================================
- First numbered release.
- Restructure the format of Event information. Add more information.
- Restructure the format of the Client-Server HTTP APIs.

View file

@ -1,170 +0,0 @@
Contributing to matrix-spec
===========================
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-spec/blob/main/meta/documentation_style.rst.
Matrix-spec workflows
---------------------
Specification changes
~~~~~~~~~~~~~~~~~~~~~
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.
Changes to the protocol (new endpoints, ideas, etc) need to go through the
`proposals process <https://matrix.org/docs/spec/proposals>`_.
Other changes
~~~~~~~~~~~~~
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 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
<https://github.com/matrix-org/matrix-spec/labels/spec-omission>`_ label).
* 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
<https://github.com/matrix-org/matrix-spec/labels/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-spec: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. This includes anything with the
`clarification <https://github.com/matrix-org/matrix-spec/labels/clarification>`_
label.
For example, areas where the specification is unclear do not require a proposal
to fix. On the other hand, introducing new behaviour is best represented by a
proposal.
* Design or aesthetic changes, such as improving accessibility, colour schemes,
etc. Please check in with us at `#matrix-docs:matrix.org`_ with your proposed
design change before opening a PR so we can work with you on it.
For such changes, please do just open a `pull request`_. If you're not sure if
your change is covered by the above, please visit `#matrix-spec:matrix.org` and
ask.
.. _`pull request`: https://help.github.com/articles/about-pull-requests
.. _`#matrix-spec:matrix.org`: https://matrix.to/#/#matrix-spec:matrix.org
.. _`#matrix-docs:matrix.org`: https://matrix.to/#/#matrix-docs:matrix.org
Adding to the changelog
~~~~~~~~~~~~~~~~~~~~~~~
All changes to the contents of this repository require a changelog entry. Adding to the changelog can only
be done after you've opened your pull request, so be sure to do that first.
The changelog is managed by `Towncrier <https://github.com/twisted/towncrier>`_ in the
form of "news fragments". Depending on which API you changed, an entry should be added to
each relevant API's ``newsfragments`` directory. A directory exists for each API under
``changelogs/``. For instance, news fragments for the client-server API are stored
under ``changelogs/client_server/newsfragments``. Any changes to the repository that do
not affect the spec content itself, such as changes to the build script, formatting, CSS,
etc. should be documented under ``changelogs/internal/newsfragments``.
To create a changelog entry, create a file named in the format ``prNumber.type`` in
the ``newsfragments`` directory. The ``type`` can be one of the following:
* ``new`` - Used when adding new endpoints. Please have the file contents be the
method and route being added, surrounded in markdown code tags. For example: \`POST
/accounts/whoami\`.
* ``feature`` - Used when adding backwards-compatible changes to the API.
* ``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.
* ``removal`` - Used when removing something that was unused or previously deprecated.
All news fragments must have a brief summary explaining the change in the
contents of the file. The summary must end in a full stop to be in line with
the style guide and formatting must be done using Markdown.
Sign off
--------
We ask that everybody who contributes to this 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
same lightweight approach used by the `Linux Kernel <https://www.kernel.org/doc/html/latest/process/submitting-patches.html>`_,
`Docker <https://github.com/docker/docker/blob/master/CONTRIBUTING.md>`_, and many other
projects: the `Developer Certificate of Origin <http://developercertificate.org/>`_
(DCO). This is a simple declaration that you wrote
the contribution or otherwise have the right to contribute it to Matrix::
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
If you agree to this for your contribution, then all that's needed is to
include the line in your commit or pull request comment::
Signed-off-by: Your Name <your@email.example.org>
Git allows you to add this signoff automatically when using the ``-s``
flag to ``git commit``, which uses the name and email set in your
``user.name`` and ``user.email`` git configs.

177
LICENSE
View file

@ -1,177 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

101
README.md
View file

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

30
README.rst Normal file
View file

@ -0,0 +1,30 @@
This repository contains the documentation for Matrix.
Structure
=========
- ``api`` : Contains the HTTP API specification.
- ``drafts`` : Contains documents which will make it into the specification
and/or supporting documentation at some point in the future.
- ``event-schemas`` : Contains 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
documents, e.g. documentation style, guidelines.
- ``scripts`` : Contains 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
https://matrix.org/jira/browse/SPEC
If you want to ask more about the specification, or have suggestions for
improvements, join us on ``#matrix-dev:matrix.org`` via https://matrix.org/beta.
.. _JSON Schema: http://json-schema.org/

4
api/README Normal file
View file

@ -0,0 +1,4 @@
To get this running:
python -m SimpleHTTPServer
Go to http://localhost:8000/swagger.html

118
api/check_examples.py Executable file
View file

@ -0,0 +1,118 @@
#! /usr/bin/env python
import sys
import json
import os
def import_error(module, package, debian, error):
sys.stderr.write((
"Error importing %(module)s: %(error)r\n"
"To install %(module)s run:\n"
" pip install %(package)s\n"
"or on Debian run:\n"
" sudo apt-get install python-%(debian)s\n"
) % locals())
if __name__ == '__main__':
sys.exit(1)
try:
import jsonschema
except ImportError as e:
import_error("jsonschema", "jsonschema", "jsonschema", e)
raise
try:
import yaml
except ImportError as e:
import_error("yaml", "PyYAML", "yaml", e)
raise
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)
if example and schema:
try:
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
jsonschema.validate(example, schema)
except Exception as e:
raise ValueError("Error validating JSON schema for %r %r" % (
request, code
), e)
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)
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
jsonschema.validate(example, schema)
except Exception as e:
raise ValueError("Error validating JSON schema for %r %r" % (
request, code
), e)
def check_swagger_file(filepath):
with open(filepath) as f:
swagger = yaml.load(f)
for path, path_api in swagger.get('paths', {}).items():
for method, request_api in path_api.items():
request = "%s %s" % (method.upper(), path)
for parameter in request_api.get('parameters', ()):
if parameter['in'] == 'body':
check_parameter(filepath, request, parameter)
try:
responses = request_api['responses']
except KeyError:
raise ValueError("No responses for %r" % (request,))
for code, response in responses.items():
check_response(filepath, request, code, response)
if __name__ == '__main__':
paths = sys.argv[1:]
if not paths:
paths = []
for (root, dirs, files) in os.walk(os.curdir):
for filename in files:
if filename.endswith(".yaml"):
paths.append(os.path.join(root, filename))
for path in paths:
try:
check_swagger_file(path)
except Exception as e:
raise ValueError("Error checking file %r" % (path,), e)

View file

@ -0,0 +1,50 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"apis": [
{
"path": "-login",
"description": "Login operations"
},
{
"path": "-registration",
"description": "Registration operations"
},
{
"path": "-rooms",
"description": "Room operations"
},
{
"path": "-profile",
"description": "Profile operations"
},
{
"path": "-presence",
"description": "Presence operations"
},
{
"path": "-events",
"description": "Event operations"
},
{
"path": "-directory",
"description": "Directory operations"
},
{
"path": "-content",
"description": "Content repository operations"
}
],
"authorizations": {
"token": {
"scopes": []
}
},
"info": {
"title": "Matrix Client-Server API Reference",
"description": "This contains the client-server API for the reference implementation of the home server",
"termsOfServiceUrl": "http://matrix.org",
"license": "Apache 2.0",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
}

View file

@ -0,0 +1,119 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix",
"resourcePath": "/media/v1/",
"apis": [
{
"path": "/media/v1/upload",
"operations": [
{
"method": "POST",
"summary": "Upload some content to the content repository.",
"type": "ContentUploadResponse",
"nickname": "upload_content",
"parameters": [
{
"name": "body",
"description": "The file to upload.",
"required": true,
"type": "file",
"paramType": "body"
}
]
}
]
},
{
"path": "/media/v1/download/{serverName}/{mediaId}",
"operations": [
{
"method": "GET",
"summary": "Get the content stored at this address.",
"type": "file",
"nickname": "download_content",
"parameters": [
{
"name": "serverName",
"description": "The serverName from the mxc://<serverName>/<mediaId> URI (the authority component).",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "mediaId",
"description": "The mediaId from the mxc://<serverName>/<mediaId> URI (the path component).",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/media/v1/thumbnail/{serverName}/{mediaId}",
"operations": [
{
"method": "GET",
"summary": "Get a thumbnail of the content stored at this address.",
"type": "file",
"nickname": "thumbnail_content",
"parameters": [
{
"name": "serverName",
"description": "The serverName from the mxc://<serverName>/<mediaId> URI (the authority component).",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "mediaId",
"description": "The mediaId from the mxc://<serverName>/<mediaId> URI (the path component).",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "width",
"description": "The desired width of the thumbnail.",
"required": false,
"type": "integer",
"paramType": "query"
},
{
"name": "height",
"description": "The desired height of the thumbnail.",
"required": false,
"type": "integer",
"paramType": "query"
},
{
"name": "method",
"description": "The desired resizing method.",
"enum": [
"crop",
"scale"
],
"required": false,
"type": "string",
"paramType": "query"
}
]
}
]
}
],
"models": {
"ContentUploadResponse": {
"id": "ContentUploadResponse",
"properties": {
"content_uri": {
"type": "string",
"description": "The mxc:// URI where this content is stored. This is of the form 'mxc://{serverName}/{mediaId}'",
"required": true
}
}
}
}
}

View file

@ -0,0 +1,101 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/directory",
"produces": [
"application/json"
],
"apis": [
{
"path": "/directory/room/{roomAlias}",
"operations": [
{
"method": "GET",
"summary": "Get the room ID corresponding to this room alias.",
"notes": "Volatile: This API is likely to change.",
"type": "DirectoryResponse",
"nickname": "get_room_id_for_alias",
"parameters": [
{
"name": "roomAlias",
"description": "The room alias.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "PUT",
"summary": "Create a new mapping from room alias to room ID.",
"notes": "Volatile: This API is likely to change.",
"type": "void",
"nickname": "add_room_alias",
"parameters": [
{
"name": "roomAlias",
"description": "The room alias to set.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"description": "The room ID to set.",
"required": true,
"type": "RoomAliasRequest",
"paramType": "body"
}
]
},
{
"method": "DELETE",
"summary": "Removes a mapping of room alias to room ID.",
"notes": "Only privileged users can perform this action.",
"type": "void",
"nickname": "remove_room_alias",
"parameters": [
{
"name": "roomAlias",
"description": "The room alias to remove.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
}
],
"models": {
"DirectoryResponse": {
"id": "DirectoryResponse",
"properties": {
"room_id": {
"type": "string",
"description": "The fully-qualified room ID.",
"required": true
},
"servers": {
"type": "array",
"items": {
"$ref": "string"
},
"description": "A list of servers that know about this room.",
"required": true
}
}
},
"RoomAliasRequest": {
"id": "RoomAliasRequest",
"properties": {
"room_id": {
"type": "string",
"description": "The room ID to map the alias to.",
"required": true
}
}
}
}
}

View file

@ -0,0 +1,247 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/events",
"produces": [
"application/json"
],
"apis": [
{
"path": "/events",
"operations": [
{
"method": "GET",
"summary": "Listen on the event stream",
"notes": "This can only be done by the logged in user. This will block until an event is received, or until the timeout is reached.",
"type": "PaginationChunk",
"nickname": "get_event_stream",
"parameters": [
{
"name": "from",
"description": "The token to stream from.",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "timeout",
"description": "The maximum time in milliseconds to wait for an event.",
"required": false,
"type": "integer",
"paramType": "query"
}
]
}
],
"responseMessages": [
{
"code": 400,
"message": "Bad pagination token."
}
]
},
{
"path": "/events/{eventId}",
"operations": [
{
"method": "GET",
"summary": "Get information about a single event.",
"notes": "Get information about a single event.",
"type": "Event",
"nickname": "get_event",
"parameters": [
{
"name": "eventId",
"description": "The event ID to get.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 404,
"message": "Event not found."
}
]
}
]
},
{
"path": "/initialSync",
"operations": [
{
"method": "GET",
"summary": "Get this user's current state.",
"notes": "Get this user's current state.",
"type": "InitialSyncResponse",
"nickname": "initial_sync",
"parameters": [
{
"name": "limit",
"description": "The maximum number of messages to return for each room.",
"type": "integer",
"paramType": "query",
"required": false
}
]
}
]
},
{
"path": "/publicRooms",
"operations": [
{
"method": "GET",
"summary": "Get a list of publicly visible rooms.",
"type": "PublicRoomsPaginationChunk",
"nickname": "get_public_room_list"
}
]
}
],
"models": {
"PaginationChunk": {
"id": "PaginationChunk",
"properties": {
"start": {
"type": "string",
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
"required": true
},
"end": {
"type": "string",
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
"required": true
},
"chunk": {
"type": "array",
"description": "An array of events.",
"required": true,
"items": {
"$ref": "Event"
}
}
}
},
"Event": {
"id": "Event",
"properties": {
"event_id": {
"type": "string",
"description": "An ID which uniquely identifies this event.",
"required": true
},
"room_id": {
"type": "string",
"description": "The room in which this event occurred.",
"required": true
}
}
},
"PublicRoomInfo": {
"id": "PublicRoomInfo",
"properties": {
"aliases": {
"type": "array",
"description": "A list of room aliases for this room.",
"items": {
"$ref": "string"
}
},
"name": {
"type": "string",
"description": "The name of the room, as given by the m.room.name state event."
},
"room_id": {
"type": "string",
"description": "The room ID for this public room.",
"required": true
},
"topic": {
"type": "string",
"description": "The topic of this room, as given by the m.room.topic state event."
}
}
},
"PublicRoomsPaginationChunk": {
"id": "PublicRoomsPaginationChunk",
"properties": {
"start": {
"type": "string",
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
"required": true
},
"end": {
"type": "string",
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
"required": true
},
"chunk": {
"type": "array",
"description": "A list of public room data.",
"required": true,
"items": {
"$ref": "PublicRoomInfo"
}
}
}
},
"InitialSyncResponse": {
"id": "InitialSyncResponse",
"properties": {
"end": {
"type": "string",
"description": "A streaming token which can be used with /events to continue from this snapshot of data.",
"required": true
},
"presence": {
"type": "array",
"description": "A list of presence events.",
"items": {
"$ref": "Event"
},
"required": false
},
"rooms": {
"type": "array",
"description": "A list of initial sync room data.",
"required": false,
"items": {
"$ref": "InitialSyncRoomData"
}
}
}
},
"InitialSyncRoomData": {
"id": "InitialSyncRoomData",
"properties": {
"membership": {
"type": "string",
"description": "This user's membership state in this room.",
"required": true
},
"room_id": {
"type": "string",
"description": "The ID of this room.",
"required": true
},
"messages": {
"type": "PaginationChunk",
"description": "The most recent messages for this room, governed by the limit parameter.",
"required": false
},
"state": {
"type": "array",
"description": "A list of state events representing the current state of the room.",
"required": false,
"items": {
"$ref": "Event"
}
}
}
}
}
}

View file

@ -0,0 +1,120 @@
{
"apiVersion": "1.0.0",
"apis": [
{
"operations": [
{
"method": "GET",
"nickname": "get_login_info",
"notes": "All login stages MUST be mentioned if there is >1 login type.",
"summary": "Get the login mechanism to use when logging in.",
"type": "LoginFlows"
},
{
"method": "POST",
"nickname": "submit_login",
"notes": "If this is part of a multi-stage login, there MUST be a 'session' key.",
"parameters": [
{
"description": "A login submission",
"name": "body",
"paramType": "body",
"required": true,
"type": "LoginSubmission"
}
],
"responseMessages": [
{
"code": 400,
"message": "Bad login type"
},
{
"code": 400,
"message": "Missing JSON keys"
}
],
"summary": "Submit a login action.",
"type": "LoginResult"
}
],
"path": "/login"
}
],
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"consumes": [
"application/json"
],
"models": {
"LoginFlows": {
"id": "LoginFlows",
"properties": {
"flows": {
"description": "A list of valid login flows.",
"type": "array",
"items": {
"$ref": "LoginInfo"
}
}
}
},
"LoginInfo": {
"id": "LoginInfo",
"properties": {
"stages": {
"description": "Multi-stage login only: An array of all the login types required to login.",
"items": {
"$ref": "string"
},
"type": "array"
},
"type": {
"description": "The login type that must be used when logging in.",
"type": "string"
}
}
},
"LoginResult": {
"id": "LoginResult",
"properties": {
"access_token": {
"description": "The access token for this user's login if this is the final stage of the login process.",
"type": "string"
},
"user_id": {
"description": "The user's fully-qualified user ID.",
"type": "string"
},
"next": {
"description": "Multi-stage login only: The next login type to submit.",
"type": "string"
},
"session": {
"description": "Multi-stage login only: The session token to send when submitting the next login type.",
"type": "string"
}
}
},
"LoginSubmission": {
"id": "LoginSubmission",
"properties": {
"type": {
"description": "The type of login being submitted.",
"type": "string"
},
"session": {
"description": "Multi-stage login only: The session token from an earlier login stage.",
"type": "string"
},
"_login_type_defined_keys_": {
"description": "Keys as defined by the specified login type, e.g. \"user\", \"password\""
}
}
}
},
"produces": [
"application/json"
],
"resourcePath": "/login",
"swaggerVersion": "1.2"
}

View file

@ -0,0 +1,164 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/presence",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"apis": [
{
"path": "/presence/{userId}/status",
"operations": [
{
"method": "PUT",
"summary": "Update this user's presence state.",
"notes": "This can only be done by the logged in user.",
"type": "void",
"nickname": "update_presence",
"parameters": [
{
"name": "body",
"description": "The new presence state",
"required": true,
"type": "PresenceUpdate",
"paramType": "body"
},
{
"name": "userId",
"description": "The user whose presence to set.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get this user's presence state.",
"notes": "Get this user's presence state.",
"type": "PresenceUpdate",
"nickname": "get_presence",
"parameters": [
{
"name": "userId",
"description": "The user whose presence to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/presence/list/{userId}",
"operations": [
{
"method": "GET",
"summary": "Retrieve a list of presences for all of this user's friends.",
"notes": "",
"type": "array",
"items": {
"$ref": "Presence"
},
"nickname": "get_presence_list",
"parameters": [
{
"name": "userId",
"description": "The user whose presence list to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "POST",
"summary": "Add or remove users from this presence list.",
"notes": "Add or remove users from this presence list.",
"type": "void",
"nickname": "modify_presence_list",
"parameters": [
{
"name": "userId",
"description": "The user whose presence list is being modified.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"description": "The modifications to make to this presence list.",
"required": true,
"type": "PresenceListModifications",
"paramType": "body"
}
]
}
]
}
],
"models": {
"PresenceUpdate": {
"id": "PresenceUpdate",
"properties": {
"presence": {
"type": "string",
"description": "Enum: The presence state.",
"enum": [
"offline",
"unavailable",
"online",
"free_for_chat"
]
},
"status_msg": {
"type": "string",
"description": "The user-defined message associated with this presence state."
}
},
"subTypes": [
"Presence"
]
},
"Presence": {
"id": "Presence",
"properties": {
"last_active_ago": {
"type": "integer",
"format": "int64",
"description": "The last time this user performed an action on their home server."
},
"user_id": {
"type": "string",
"description": "The fully qualified user ID"
}
}
},
"PresenceListModifications": {
"id": "PresenceListModifications",
"properties": {
"invite": {
"type": "array",
"description": "A list of user IDs to add to the list.",
"items": {
"type": "string",
"description": "A fully qualified user ID."
}
},
"drop": {
"type": "array",
"description": "A list of user IDs to remove from the list.",
"items": {
"type": "string",
"description": "A fully qualified user ID."
}
}
}
}
}
}

View file

@ -0,0 +1,122 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/profile",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"apis": [
{
"path": "/profile/{userId}/displayname",
"operations": [
{
"method": "PUT",
"summary": "Set a display name.",
"notes": "This can only be done by the logged in user.",
"type": "void",
"nickname": "set_display_name",
"parameters": [
{
"name": "body",
"description": "The new display name for this user.",
"required": true,
"type": "DisplayName",
"paramType": "body"
},
{
"name": "userId",
"description": "The user whose display name to set.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get a display name.",
"notes": "This can be done by anyone.",
"type": "DisplayName",
"nickname": "get_display_name",
"parameters": [
{
"name": "userId",
"description": "The user whose display name to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/profile/{userId}/avatar_url",
"operations": [
{
"method": "PUT",
"summary": "Set an avatar URL.",
"notes": "This can only be done by the logged in user.",
"type": "void",
"nickname": "set_avatar_url",
"parameters": [
{
"name": "body",
"description": "The new avatar url for this user.",
"required": true,
"type": "AvatarUrl",
"paramType": "body"
},
{
"name": "userId",
"description": "The user whose avatar url to set.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get an avatar url.",
"notes": "This can be done by anyone.",
"type": "AvatarUrl",
"nickname": "get_avatar_url",
"parameters": [
{
"name": "userId",
"description": "The user whose avatar url to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
}
],
"models": {
"DisplayName": {
"id": "DisplayName",
"properties": {
"displayname": {
"type": "string",
"description": "The textual display name"
}
}
},
"AvatarUrl": {
"id": "AvatarUrl",
"properties": {
"avatar_url": {
"type": "string",
"description": "A url to an image representing an avatar."
}
}
}
}
}

View file

@ -0,0 +1,120 @@
{
"apiVersion": "1.0.0",
"apis": [
{
"operations": [
{
"method": "GET",
"nickname": "get_registration_info",
"notes": "All login stages MUST be mentioned if there is >1 login type.",
"summary": "Get the login mechanism to use when registering.",
"type": "RegistrationFlows"
},
{
"method": "POST",
"nickname": "submit_registration",
"notes": "If this is part of a multi-stage registration, there MUST be a 'session' key.",
"parameters": [
{
"description": "A registration submission",
"name": "body",
"paramType": "body",
"required": true,
"type": "RegistrationSubmission"
}
],
"responseMessages": [
{
"code": 400,
"message": "Bad login type"
},
{
"code": 400,
"message": "Missing JSON keys"
}
],
"summary": "Submit a registration action.",
"type": "RegistrationResult"
}
],
"path": "/register"
}
],
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"consumes": [
"application/json"
],
"models": {
"RegistrationFlows": {
"id": "RegistrationFlows",
"properties": {
"flows": {
"description": "A list of valid registration flows.",
"type": "array",
"items": {
"$ref": "RegistrationInfo"
}
}
}
},
"RegistrationInfo": {
"id": "RegistrationInfo",
"properties": {
"stages": {
"description": "Multi-stage registration only: An array of all the login types required to registration.",
"items": {
"$ref": "string"
},
"type": "array"
},
"type": {
"description": "The first login type that must be used when logging in.",
"type": "string"
}
}
},
"RegistrationResult": {
"id": "RegistrationResult",
"properties": {
"access_token": {
"description": "The access token for this user's registration if this is the final stage of the registration process.",
"type": "string"
},
"user_id": {
"description": "The user's fully-qualified user ID.",
"type": "string"
},
"next": {
"description": "Multi-stage registration only: The next registration type to submit.",
"type": "string"
},
"session": {
"description": "Multi-stage registration only: The session token to send when submitting the next registration type.",
"type": "string"
}
}
},
"RegistrationSubmission": {
"id": "RegistrationSubmission",
"properties": {
"type": {
"description": "The type of registration being submitted.",
"type": "string"
},
"session": {
"description": "Multi-stage registration only: The session token from an earlier registration stage.",
"type": "string"
},
"_registration_type_defined_keys_": {
"description": "Keys as defined by the specified registration type, e.g. \"user\", \"password\""
}
}
}
},
"produces": [
"application/json"
],
"resourcePath": "/register",
"swaggerVersion": "1.2"
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,127 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Content Repository API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
basePath: /_matrix/media/v1
produces:
- application/json
- "*/*"
paths:
"/upload":
post:
summary: Upload some content to the content repository.
produces: ["application/json"]
parameters:
- in: header
name: Content-Type
type: string
description: The content type of the file being uploaded
x-example: "Content-Type: audio/mpeg"
- in: body
name: "<content>"
description: The content to be uploaded.
required: true
schema:
type: string
example: "<bytes>"
format: byte
responses:
200:
description: The MXC URI for the uploaded content.
schema:
type: object
required: ["content_uri"]
properties:
content_uri:
type: string
description: "The MXC URI to the uploaded content."
examples:
"application/json": |-
{
"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
}
"/download/{serverName}/{mediaId}":
get:
summary: "Download content from the content repository."
produces: ["*/*"]
parameters:
- in: path
type: string
name: serverName
x-example: matrix.org
required: true
description: |
The server name from the ``mxc://`` URI (the authoritory component)
- in: path
type: string
name: mediaId
x-example: ascERGshawAWawugaAcauga
required: true
description: |
The media ID from the ``mxc://`` URI (the path component)
responses:
200:
description: "The content that was previously uploaded."
headers:
Content-Type:
description: "The content type of the file that was previously uploaded."
type: "string"
Content-Disposition:
description: "The name of the file that was previously uploaded, if set."
type: "string"
schema:
type: file
"/thumbnail/{serverName}/{mediaId}":
get:
summary: "Download a thumbnail of the content from the content repository."
produces: ["image/jpeg", "image/png"]
parameters:
- in: path
type: string
name: serverName
required: true
x-example: matrix.org
description: |
The server name from the ``mxc://`` URI (the authoritory component)
- in: path
type: string
name: mediaId
x-example: ascERGshawAWawugaAcauga
required: true
description: |
The media ID from the ``mxc://`` URI (the path component)
- in: query
type: integer
x-example: 64
name: width
description: |-
The *desired* width of the thumbnail. The actual thumbnail may not
match the size specified.
- in: query
type: integer
x-example: 64
name: height
description: |-
The *desired* height of the thumbnail. The actual thumbnail may not
match the size specified.
- in: query
type: string
enum: ["crop", "scale"]
name: method
x-example: "scale"
description: The desired resizing method.
responses:
200:
description: "A thumbnail of the requested content."
headers:
Content-Type:
description: "The content type of the thumbnail."
type: "string"
enum: ["image/jpeg", "image/png"]
schema:
type: file

View file

@ -0,0 +1 @@
v1-event-schema/core-event-schema

View file

@ -0,0 +1,10 @@
type: object
description: A Matrix-level Error
properties:
errcode:
type: string
description: An error code.
error:
type: string
description: A human-readable error message.
required: ["errcode"]

View file

@ -0,0 +1,87 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Directory API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1/directory
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/room/{roomAlias}":
put:
summary: Create a new mapping from room alias to room ID.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomAlias
description: The room alias to set.
required: true
- in: body
name: roomInfo
description: Information about this room alias.
required: true
schema:
type: object
properties:
room_id:
type: string
description: The room ID to set.
responses:
200:
description: The mapping was created.
schema:
type: object # empty json object
get:
summary: Get the room ID corresponding to this room alias.
parameters:
- in: path
type: string
name: roomAlias
description: The room alias.
required: true
responses:
200:
description: The room ID and other information for this alias.
schema:
type: object
properties:
room_id:
type: string
description: The room ID for this room alias.
servers:
type: array
description: A list of servers that are aware of this room ID.
items:
type: string
description: A server which is aware of this room ID.
404:
description: There is no mapped room ID for this room alias.
delete:
summary: Remove a mapping of room alias to room ID.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomAlias
description: The room alias to remove.
required: true
responses:
200:
description: The mapping was removed.
schema:
type: object # empty json object

View file

@ -0,0 +1,147 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Registration and Login API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/login":
post:
summary: Authenticates the user.
description: |-
Authenticates the user by password, and issues an access token they can
use to authorize themself in subsequent requests.
security:
- accessToken: []
parameters:
- in: body
name: body
schema:
type: object
example: |-
{
"username": "cheeky_monkey",
"password": "ilovebananas"
}
properties:
username:
type: string
description: The fully qualified user ID or just local part of the user ID, to log in.
password:
type: string
description: The user's password.
required: ["username", "password"]
responses:
200:
description: The user has been authenticated.
examples:
application/json: |-
{
"user_id": "@cheeky_monkey:matrix.org",
"access_token": "abc123",
"home_server": "matrix.org"
}
schema:
type: object
properties:
user_id:
type: string
description: The fully-qualified Matrix ID that has been registered.
access_token:
type: string
description: |-
An access token for the account.
This access token can then be used to authorize other requests.
The access token may expire at some point, and if so, it SHOULD come with a ``refresh_token``.
There is no specific error message to indicate that a request has failed because
an access token has expired; instead, if a client has reason to believe its
access token is valid, and it receives an auth error, they should attempt to
refresh for a new token on failure, and retry the request with the new token.
refresh_token:
type: string
# TODO: Work out how to linkify /tokenrefresh
description: |-
(optional) A ``refresh_token`` may be exchanged for a new ``access_token`` using the /tokenrefresh API endpoint.
home_server:
type: string
description: The hostname of the Home Server on which the account has been registered.
403:
description: |-
The login attempt failed. For example, the password may have been incorrect.
examples:
application/json: |-
{"errcode": "M_FORBIDDEN"}
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
"/tokenrefresh":
post:
summary: Exchanges a refresh token for an access token.
description: |-
Exchanges a refresh token for a new access token.
This is intended to be used if the access token has expired.
security:
- accessToken: []
parameters:
- in: body
name: body
schema:
type: object
example: |-
{
"refresh_token": "a1b2c3"
}
properties:
refresh_token:
type: string
description: The refresh token which was issued by the server.
required: ["refresh_token"]
responses:
200:
description: |-
The refresh token was accepted, and a new access token has been issued.
The passed refresh token is no longer valid and cannot be used.
A new refresh token will have been returned unless some policy does
not allow the user to continue to renew their session.
examples:
application/json: |-
{
"access_token": "bearwithme123",
"refresh_token": "exchangewithme987"
}
schema:
type: object
properties:
access_token:
type: string
description: |-
An access token for the account.
This access token can then be used to authorize other requests.
The access token may expire at some point, and if so, it SHOULD come with a ``refresh_token``.
refresh_token:
type: string
description: (optional) A ``refresh_token`` may be exchanged for a new ``access_token`` using the TODO Linkify /tokenrefresh API endpoint.
403:
description: |-
The exchange attempt failed. For example, the refresh token may have already been used.
examples:
application/json: |-
{"errcode": "M_FORBIDDEN"}
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"

View file

@ -0,0 +1,128 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Room Membership API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/rooms/{roomId}/join":
post:
summary: Start the requesting user participating in a particular room.
description: |-
This API starts a user participating in a particular room, if that user
is allowed to participate in that room. After this call, the client is
allowed to see all current state events in the room, and all subsequent
events associated with the room until the user leaves the room.
After a user has joined a room, the room will appear as an entry in the
response of the |initialSync| API.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room identifier or room alias to join.
required: true
x-example: "#monkeys:matrix.org"
responses:
200:
description: |-
The room has been joined.
The joined room ID must be returned in the ``room_id`` field.
examples:
application/json: |-
{"room_id": "!d41d8cd:matrix.org"}
schema:
type: object
403:
description: |-
You do not have permission to join the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejection are:
- 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."}
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
x-alias:
canonical-link: "post-matrix-client-api-v1-rooms-roomid-join"
aliases:
- /join/{roomId}
"/rooms/{roomId}/invite":
post:
summary: Invite a user to participate in a particular room.
description: |-
This API invites a user to participate in a particular room.
They do not start participating in the room until they actually join the
room.
This serves two purposes; firstly, to notify the user that the room
exists (and that their presence is requested). Secondly, some rooms can
only be joined if a user is invited to join it; sending the invite gives
that user permission to join the room.
Only users currently in a particular room can invite other users to
join that room.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room identifier (not alias) to which to invite the user.
required: true
x-example: "!d41d8cd:matrix.org"
- in: body
name: user_id
required: true
schema:
type: object
example: |-
{
"user_id": "@cheeky_monkey:matrix.org"
}
properties:
user_id:
type: string
description: The fully qualified user ID of the invitee.
required: ["user_id"]
responses:
200:
description: The user has been invited to join the room.
examples:
application/json: |-
{}
schema:
type: object # empty json object
403:
description: |-
You do not have permission to invite the user to the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are:
- The invitee has been banned from the room.
- The invitee is already a member of the room.
- 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"}
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"

View file

@ -0,0 +1,208 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Presence API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/presence/{userId}/status":
put:
summary: Update this user's presence state.
description: |-
This API sets the given user's presence state. When setting the status,
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.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
description: The user whose presence state to update.
required: true
x-example: "@alice:example.com"
- in: body
name: presenceState
description: The updated presence state.
required: true
schema:
type: object
example: |-
{
"presence": "online",
"status_msg": "I am here."
}
properties:
presence:
type: string
enum: ["online", "offline", "unavailable", "free_for_chat"]
description: The new presence state.
status_msg:
type: string
description: "The status message to attach to this state."
required: ["presence"]
responses:
200:
description: The new presence state was set.
examples:
application/json: |-
{}
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
get:
summary: Get this user's presence state.
description: |-
Get the given user's presence state.
parameters:
- in: path
type: string
name: userId
description: The user whose presence state to get.
required: true
x-example: "@alice:example.com"
responses:
200:
description: The presence state for this user.
examples:
application/json: |-
{
"presence": "unavailable",
"last_active_ago": 420845,
"status_msg": null
}
schema:
type: object
properties:
presence:
type: string
enum: ["online", "offline", "unavailable", "free_for_chat"]
description: This user's presence.
last_active_ago:
type: integer
description: |-
The length of time in milliseconds since an action was performed
by this user.
status_msg:
type: [string, "null"]
description: The state message for this user if one was set.
404:
description: |-
There is no presence state for this user. This user may not exist or
isn't exposing presence information to you.
"/presence/list/{userId}":
post:
summary: Add or remove users from this presence list.
description: |-
Adds or removes users from this presence list.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
description: The user whose presence list is being modified.
required: true
x-example: "@alice:example.com"
- in: body
name: presence_diff
description: The modifications to make to this presence list.
required: true
schema:
type: object
example: |-
{
"invite": [
"@bob:matrix.org"
],
"drop": [
"@alice:matrix.org"
]
}
properties:
invite:
type: array
description: A list of user IDs to add to the list.
items:
type: string
description: A list of user IDs.
drop:
type: array
description: A list of user IDs to remove from the list.
items:
type: string
description: A list of user IDs.
responses:
200:
description: The list was updated.
examples:
application/json: |-
{}
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
get:
summary: Get presence events for this presence list.
description: |-
Retrieve a list of presence events for every user on this list.
parameters:
- in: path
type: string
name: userId
description: The user whose presence list should be retrieved.
required: true
x-example: "@alice:example.com"
responses:
200:
description: A list of presence events for this list.
examples:
application/json: |-
[
{
"content": {
"avatar_url": "mxc://matrix.org/AfwefuigfWEfhuiPP",
"displayname": "Alice Margatroid",
"last_active_ago": 395,
"presence": "offline",
"user_id": "@alice:matrix.org"
},
"type": "m.presence"
},
{
"content": {
"avatar_url": "mxc://matrix.org/FWEhuiwegfWEfhuiwf",
"displayname": "Marisa Kirisame",
"last_active_ago": 16874,
"presence": "online",
"user_id": "@marisa:matrix.org"
},
"type": "m.presence"
}
]
schema:
type: array
items:
type: object
title: PresenceEvent
allOf:
- "$ref": "core-event-schema/event.json"

View file

@ -0,0 +1,195 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Profile API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/profile/{userId}/displayname":
put:
summary: Set the user's display name.
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``.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
description: The user whose display name to set.
required: true
x-example: "@alice:example.com"
- in: body
name: displayName
description: The display name info.
required: true
schema:
type: object
example: |-
{
"displayname": "Alice Margatroid"
}
properties:
displayname:
type: string
description: The new display name for this user.
responses:
200:
description: The display name was set.
examples:
application/json: |-
{}
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
get:
summary: Get the user's display name.
description: |-
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.
parameters:
- in: path
type: string
name: userId
description: The user whose display name to get.
required: true
x-example: "@alice:example.com"
responses:
200:
description: The display name for this user.
examples:
application/json: |-
{
"displayname": "Alice Margatroid"
}
schema:
type: object
properties:
displayname:
type: string
description: The user's display name if they have set one.
404:
description: There is no display name for this user or this user does not exist.
"/profile/{userId}/avatar_url":
put:
summary: Set the user's avatar URL.
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``.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
description: The user whose avatar URL to set.
required: true
x-example: "@alice:example.com"
- in: body
name: avatar_url
description: The avatar url info.
required: true
schema:
type: object
example: |-
{
"avatar_url": "mxc://matrix.org/wefh34uihSDRGhw34"
}
properties:
avatar_url:
type: string
description: The new avatar URL for this user.
responses:
200:
description: The avatar URL was set.
examples:
application/json: |-
{}
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
get:
summary: Get the user's avatar URL.
description: |-
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.
parameters:
- in: path
type: string
name: userId
description: The user whose avatar URL to get.
required: true
x-example: "@alice:example.com"
responses:
200:
description: The avatar URL for this user.
examples:
application/json: |-
{
"avatar_url": "mxc://matrix.org/SDGdghriugerRg"
}
schema:
type: object
properties:
avatar_url:
type: string
description: The user's avatar URL if they have set one.
404:
description: There is no avatar URL for this user or this user does not exist.
"/profile/{userId}":
get:
summary: Get this user's profile information.
description: |-
Get the combined profile information for this user. This API may be used
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``.
parameters:
- in: path
type: string
name: userId
description: The user whose avatar URL to get.
required: true
x-example: "@alice:example.com"
responses:
200:
description: The avatar URL for this user.
examples:
application/json: |-
{
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
"displayname": "Alice Margatroid"
}
schema:
type: object
properties:
avatar_url:
type: string
description: The user's avatar URL if they have set one.
displayname:
type: string
description: The user's display name if they have set one.
404:
description: There is no profile information for this user or this user does not exist.

View file

@ -0,0 +1,448 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Rooms API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/rooms/{roomId}/state/{eventType}/{stateKey}":
get:
summary: Get the state identified by the type and key.
description: |-
Looks up the contents of a state event in a room. If the user is
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.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to look up the state in.
required: true
x-example: "!636q39766251:example.com"
- in: path
type: string
name: eventType
description: The type of state to look up.
required: true
x-example: "m.room.name"
- in: path
type: string
name: stateKey
description: The key of the state to look up. Defaults to the empty string.
required: true
x-example: ""
responses:
200:
description: The content of the state event.
examples:
application/json: |-
{"name": "Example room name"}
schema:
type: object
404:
description: The room has no state with the given type or key.
403:
description: >
You aren't a member of the room and weren't previously a
member of the room.
"/rooms/{roomId}/state":
get:
summary: Get all state events in the current state of a room.
description: |-
Get the state events for the current state of a room.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to look up the state for.
required: true
x-example: "!636q39766251:example.com"
responses:
200:
description: The current state of the room
examples:
application/json: |-
[
{
"age": 7148266897,
"content": {
"join_rule": "public"
},
"event_id": "$14259997323TLwtb:example.com",
"origin_server_ts": 1425999732392,
"room_id": "!636q39766251:example.com",
"state_key": "",
"type": "m.room.join_rules",
"user_id": "@alice:example.com"
},
{
"age": 6547561012,
"content": {
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
"displayname": null,
"membership": "join"
},
"event_id": "$1426600438280zExKY:example.com",
"membership": "join",
"origin_server_ts": 1426600438277,
"room_id": "!636q39766251:example.com",
"state_key": "@alice:example.com",
"type": "m.room.member",
"user_id": "@alice:example.com"
},
{
"age": 7148267200,
"content": {
"creator": "@alice:example.com"
},
"event_id": "$14259997320KhbwJ:example.com",
"origin_server_ts": 1425999732089,
"room_id": "!636q39766251:example.com",
"state_key": "",
"type": "m.room.create",
"user_id": "@alice:example.com"
},
{
"age": 1622568720,
"content": {
"avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
"displayname": "Bob",
"membership": "join"
},
"event_id": "$1431525430134MxlLX:example.com",
"origin_server_ts": 1431525430569,
"replaces_state": "$142652023736BSXcM:example.com",
"room_id": "!636q39766251:example.com",
"state_key": "@bob:example.com",
"type": "m.room.member",
"user_id": "@bob:example.com"
},
{
"age": 7148267004,
"content": {
"ban": 50,
"events": {
"m.room.name": 100,
"m.room.power_levels": 100
},
"events_default": 0,
"kick": 50,
"redact": 50,
"state_default": 50,
"users": {
"@alice:example.com": 100
},
"users_default": 0
},
"event_id": "$14259997322mqfaq:example.com",
"origin_server_ts": 1425999732285,
"room_id": "!636q39766251:example.com",
"state_key": "",
"type": "m.room.power_levels",
"user_id": "@alice:example.com"
}
]
schema:
type: array
title: RoomState
description: |-
If the user is a member of the room this will be the
current state of the room as a list of events. If the user
has left the room then this will be the state of the room
when they left as a list of events.
items:
title: StateEvent
type: object
allOf:
- "$ref": "core-event-schema/state_event.json"
403:
description: >
You aren't a member of the room and weren't previously a
member of the room.
"/rooms/{roomId}/initialSync":
get:
summary: Snapshot the current state of a room and its most recent messages.
description: |-
Get a copy of the current state and the most recent messages in a room.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to get the data.
required: true
x-example: "!636q39766251:example.com"
responses:
200:
description: The current state of the room
examples:
application/json: |-
{
"membership": "join",
"messages": {
"chunk": [
{
"age": 343513403,
"content": {
"body": "foo",
"msgtype": "m.text"
},
"event_id": "$14328044851tzTJS:example.com",
"origin_server_ts": 1432804485886,
"room_id": "!636q39766251:example.com",
"type": "m.room.message",
"user_id": "@alice:example.com"
},
{
"age": 343511809,
"content": {
"body": "bar",
"msgtype": "m.text"
},
"event_id": "$14328044872spjFg:example.com",
"origin_server_ts": 1432804487480,
"room_id": "!636q39766251:example.com",
"type": "m.room.message",
"user_id": "@bob:example.com"
}
],
"end": "s3456_9_0",
"start": "t44-3453_9_0"
},
"room_id": "!636q39766251:example.com",
"state": [
{
"age": 7148266897,
"content": {
"join_rule": "public"
},
"event_id": "$14259997323TLwtb:example.com",
"origin_server_ts": 1425999732392,
"room_id": "!636q39766251:example.com",
"state_key": "",
"type": "m.room.join_rules",
"user_id": "@alice:example.com"
},
{
"age": 6547561012,
"content": {
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
"displayname": null,
"membership": "join"
},
"event_id": "$1426600438280zExKY:example.com",
"membership": "join",
"origin_server_ts": 1426600438277,
"room_id": "!636q39766251:example.com",
"state_key": "@alice:example.com",
"type": "m.room.member",
"user_id": "@alice:example.com"
},
{
"age": 7148267200,
"content": {
"creator": "@alice:example.com"
},
"event_id": "$14259997320KhbwJ:example.com",
"origin_server_ts": 1425999732089,
"room_id": "!636q39766251:example.com",
"state_key": "",
"type": "m.room.create",
"user_id": "@alice:example.com"
},
{
"age": 1622568720,
"content": {
"avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
"displayname": "Bob",
"membership": "join"
},
"event_id": "$1431525430134MxlLX:example.com",
"origin_server_ts": 1431525430569,
"replaces_state": "$142652023736BSXcM:example.com",
"room_id": "!636q39766251:example.com",
"state_key": "@bob:example.com",
"type": "m.room.member",
"user_id": "@bob:example.com"
},
{
"age": 7148267004,
"content": {
"ban": 50,
"events": {
"m.room.name": 100,
"m.room.power_levels": 100
},
"events_default": 0,
"kick": 50,
"redact": 50,
"state_default": 50,
"users": {
"@alice:example.com": 100
},
"users_default": 0
},
"event_id": "$14259997322mqfaq:example.com",
"origin_server_ts": 1425999732285,
"room_id": "!636q39766251:example.com",
"state_key": "",
"type": "m.room.power_levels",
"user_id": "@alice:example.com"
}
],
"visibility": "private"
}
schema:
title: RoomInfo
type: object
properties:
room_id:
type: string
description: "The ID of this room."
membership:
type: string
description: "The user's membership state in this room."
enum: ["invite", "join", "leave", "ban"]
messages:
type: object
title: PaginationChunk
description: "The pagination chunk for this room."
properties:
start:
type: string
description: |-
A token which correlates to the first value in ``chunk``.
Used for pagination.
end:
type: string
description: |-
A token which correlates to the last value in ``chunk``.
Used for pagination.
chunk:
type: array
description: |-
If the user is a member of the room this will be a
list of the most recent messages for this room. If
the user has left the room this will be the
messages that preceeded them leaving. This array
will consist of at most ``limit`` elements.
items:
type: object
title: RoomEvent
allOf:
- "$ref": "core-event-schema/room_event.json"
required: ["start", "end", "chunk"]
state:
type: array
description: |-
If the user is a member of the room this will be the
current state of the room as a list of events. If the
user has left the room this will be the state of the
room when they left it.
items:
title: StateEvent
type: object
allOf:
- "$ref": "core-event-schema/state_event.json"
visibility:
type: string
enum: ["private", "public"]
description: |-
Whether this room is visible to the ``/publicRooms`` API
or not."
required: ["room_id", "membership"]
403:
description: >
You aren't a member of the room and weren't previously a
member of the room.
"/rooms/{roomId}/members":
get:
summary: Get the m.room.member events for the room.
description:
Get the list of members for this room.
parameters:
- in: path
type: string
name: roomId
description: The room to get the member events for.
required: true
x-example: "!636q39766251:example.com"
responses:
200:
description: |-
A list of members of the room. If you are joined to the room then
this will be the current members of the room. If you have left te
room then this will be the members of the room when you left.
examples:
application/json: |-
{
"chunk": [
{
"age": 6547561012,
"content": {
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
"displayname": null,
"membership": "join"
},
"event_id": "$1426600438280zExKY:example.com",
"membership": "join",
"origin_server_ts": 1426600438277,
"room_id": "!636q39766251:example.com",
"state_key": "@alice:example.com",
"type": "m.room.member",
"user_id": "@alice:example.com"
},
{
"age": 1622568720,
"content": {
"avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
"displayname": "Bob",
"membership": "join"
},
"event_id": "$1431525430134MxlLX:example.com",
"origin_server_ts": 1431525430569,
"replaces_state": "$142652023736BSXcM:example.com",
"room_id": "!636q39766251:example.com",
"state_key": "@bob:example.com",
"type": "m.room.member",
"user_id": "@bob:example.com"
}
]
}
schema:
type: object
properties:
chunk:
type: array
items:
title: MemberEvent
type: object
allOf:
- "$ref": "v1-event-schema/m.room.member"
403:
description: >
You aren't a member of the room and weren't previously a
member of the room.

View file

@ -0,0 +1,361 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Sync API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/events":
get:
summary: Listen on the event stream.
description: |-
This will listen for new events and return them to the caller. This will
block until an event is received, or until the ``timeout`` is reached.
security:
- accessToken: []
parameters:
- in: query
type: string
name: from
description: The token to stream from.
required: false
x-example: "s3456_9_0"
- in: query
type: integer
name: timeout
description: The maximum time in milliseconds to wait for an event.
required: false
x-example: "35000"
responses:
200:
description: "The events received, which may be none."
examples:
application/json: |-
{
"start": "s3456_9_0",
"end": "s3457_9_0",
"chunk": [
{
"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"
}
]
}
schema:
type: object
properties:
start:
type: string
description: |-
A token which correlates to the first value in ``chunk``. Used
for pagination.
end:
type: string
description: |-
A token which correlates to the last value in ``chunk``. Used
for pagination.
chunk:
type: array
description: "An array of events."
items:
type: object
title: RoomEvent
allOf:
- "$ref": "core-event-schema/room_event.json"
400:
description: "Bad pagination ``from`` parameter."
"/initialSync":
get:
summary: Get the user's current state.
description: |-
This returns the full state for this user, with an optional limit on the
number of messages per room to return.
security:
- accessToken: []
parameters:
- in: query
type: integer
name: limit
description: The maximum number of messages to return for each room.
required: false
x-example: "2"
responses:
200:
description: The user's current state.
examples:
application/json: |-
{
"end": "s3456_9_0",
"presence": [
{
"content": {
"avatar_url": "mxc://localhost/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
"displayname": "Bob",
"last_active_ago": 31053,
"presence": "online",
"user_id": "@bob:localhost"
},
"type": "m.presence"
}
],
"rooms": [
{
"membership": "join",
"messages": {
"chunk": [
{
"age": 343513403,
"content": {
"body": "foo",
"msgtype": "m.text"
},
"event_id": "$14328044851tzTJS:localhost",
"origin_server_ts": 1432804485886,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"type": "m.room.message",
"user_id": "@alice:localhost"
},
{
"age": 343511809,
"content": {
"body": "bar",
"msgtype": "m.text"
},
"event_id": "$14328044872spjFg:localhost",
"origin_server_ts": 1432804487480,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"type": "m.room.message",
"user_id": "@bob:localhost"
}
],
"end": "s3456_9_0",
"start": "t44-3453_9_0"
},
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"state": [
{
"age": 7148266897,
"content": {
"join_rule": "public"
},
"event_id": "$14259997323TLwtb:localhost",
"origin_server_ts": 1425999732392,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"state_key": "",
"type": "m.room.join_rules",
"user_id": "@alice:localhost"
},
{
"age": 6547561012,
"content": {
"avatar_url": "mxc://localhost/fzysBrHpPEeTGANCVLXWXNMI#auto",
"displayname": null,
"membership": "join"
},
"event_id": "$1426600438280zExKY:localhost",
"membership": "join",
"origin_server_ts": 1426600438277,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"state_key": "@alice:localhost",
"type": "m.room.member",
"user_id": "@alice:localhost"
},
{
"age": 7148267200,
"content": {
"creator": "@alice:localhost"
},
"event_id": "$14259997320KhbwJ:localhost",
"origin_server_ts": 1425999732089,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"state_key": "",
"type": "m.room.create",
"user_id": "@alice:localhost"
},
{
"age": 1622568720,
"content": {
"avatar_url": "mxc://localhost/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
"displayname": "Bob",
"membership": "join"
},
"event_id": "$1431525430134MxlLX:localhost",
"origin_server_ts": 1431525430569,
"replaces_state": "$142652023736BSXcM:localhost",
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"state_key": "@bob:localhost",
"type": "m.room.member",
"user_id": "@bob:localhost"
},
{
"age": 7148267004,
"content": {
"ban": 50,
"events": {
"m.room.name": 100,
"m.room.power_levels": 100
},
"events_default": 0,
"kick": 50,
"redact": 50,
"state_default": 50,
"users": {
"@alice:localhost": 100
},
"users_default": 0
},
"event_id": "$14259997322mqfaq:localhost",
"origin_server_ts": 1425999732285,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"state_key": "",
"type": "m.room.power_levels",
"user_id": "@alice:localhost"
}
],
"visibility": "private"
}
]
}
schema:
type: object
properties:
end:
type: string
description: |-
A token which correlates to the last value in ``chunk``. This
token should be used with the ``/events`` API to listen for new
events.
presence:
type: array
description: A list of presence events.
items:
type: object
title: Event
allOf:
- "$ref": "core-event-schema/event.json"
rooms:
type: array
items:
type: object
title: RoomInfo
properties:
room_id:
type: string
description: "The ID of this room."
membership:
type: string
description: "The user's membership state in this room."
enum: ["invite", "join", "leave", "ban"]
invite:
type: object
title: "InviteEvent"
description: "The invite event if ``membership`` is ``invite``"
allOf:
- "$ref": "v1-event-schema/m.room.member"
messages:
type: object
title: PaginationChunk
description: "The pagination chunk for this room."
properties:
start:
type: string
description: |-
A token which correlates to the first value in ``chunk``.
Used for pagination.
end:
type: string
description: |-
A token which correlates to the last value in ``chunk``.
Used for pagination.
chunk:
type: array
description: |-
If the user is a member of the room this will be a
list of the most recent messages for this room. If
the user has left the room this will be the
messages that preceeded them leaving. This array
will consist of at most ``limit`` elements.
items:
type: object
title: RoomEvent
allOf:
- "$ref": "core-event-schema/room_event.json"
required: ["start", "end", "chunk"]
state:
type: array
description: |-
If the user is a member of the room this will be the
current state of the room as a list of events. If the
user has left the room this will be the state of the
room when they left it.
items:
title: StateEvent
type: object
allOf:
- "$ref": "core-event-schema/state_event.json"
visibility:
type: string
enum: ["private", "public"]
description: |-
Whether this room is visible to the ``/publicRooms`` API
or not."
required: ["room_id", "membership"]
required: ["end", "rooms", "presence"]
404:
description: There is no avatar URL for this user or this user does not exist.
"/events/{eventId}":
get:
summary: Get a single event by event ID.
description: |-
Get a single event based on ``event_id``. You must have permission to
retrieve this event e.g. by being a member in the room for this event.
security:
- accessToken: []
parameters:
- 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",
"user_id": "@bob:matrix.org",
"event_id": "$asfDuShaf7Gafaw:matrix.org",
"type": "m.room.message"
}
schema:
allOf:
- "$ref": "core-event-schema/event.json"
404:
description: The event was not found or you do not have permission to read this event.

View file

@ -0,0 +1,77 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Typing API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/rooms/{roomId}/typing/{userId}":
put:
summary: Informs the server that the user has started or stopped typing.
description: |-
This tells the server that the user is typing for the next N
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.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
description: The user who has started to type.
required: true
x-example: "@alice:example.com"
- in: path
type: string
name: roomId
description: The room in which the user is typing.
required: true
x-example: "!wefh3sfukhs:example.com"
- in: body
name: typingState
description: The current typing state.
required: true
schema:
type: object
example: |-
{
"typing": true,
"timeout": 30000
}
properties:
typing:
type: boolean
description: |-
Whether the user is typing or not. If ``false``, the ``timeout``
key can be omitted.
timeout:
type: integer
description: The length of time in milliseconds to mark this user as typing.
required: ["typing"]
responses:
200:
description: The new typing state was set.
examples:
application/json: |-
{}
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"

View file

@ -0,0 +1 @@
../../../event-schemas/schema/v1

View file

@ -0,0 +1,68 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v1 Voice over IP API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/v1
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/turnServer":
get:
summary: Obtain TURN server credentials.
description: |-
This API provides credentials for the client to use when initiating
calls.
security:
- accessToken: []
responses:
200:
description: The TURN server credentials.
examples:
application/json: |-
{
"username":"1443779631:@user:example.com",
"password":"JlKfBy1QwLrO20385QyAtEyIv0=",
"uris":[
"turn:turn.example.com:3478?transport=udp",
"turn:10.20.30.40:3478?transport=tcp",
"turns:10.20.30.40:443?transport=tcp"
],
"ttl":86400
}
schema:
type: object
properties:
username:
type: string
description: |-
The username to use.
password:
type: string
description: |-
The password to use.
uris:
type: array
items:
type: string
description: A list of TURN URIs
ttl:
type: integer
description: The time-to-live in seconds
required: ["username", "password", "uris", "ttl"]
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"

View file

@ -0,0 +1,68 @@
swagger: '2.0'
info:
title: "Matrix Client-Server v2 Receipts API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/v2_alpha
consumes:
- application/json
produces:
- application/json
securityDefinitions:
accessToken:
type: apiKey
description: The user_id or application service access_token
name: access_token
in: query
paths:
"/rooms/{roomId}/receipt/{receiptType}/{eventId}":
post:
summary: Send a receipt for the given event ID.
description: |-
This API updates the marker for the given receipt type to the event ID
specified.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room in which to send the event.
required: true
x-example: "!wefuh21ffskfuh345:example.com"
- in: path
type: string
name: receiptType
description: The type of receipt to send.
required: true
x-example: "m.read"
enum: ["m.read"]
- in: path
type: string
name: eventId
description: The event ID to acknowledge up to.
required: true
x-example: "$1924376522eioj:example.com"
- in: body
description: |-
Extra receipt information to attach to ``content`` if any. The
server will automatically set the ``ts`` field.
schema:
type: object
example: |-
{}
responses:
200:
description: The receipt was sent.
examples:
application/json: |-
{}
schema:
type: object # empty json object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"

38
api/files/backbone-min.js vendored Normal file
View file

@ -0,0 +1,38 @@
// Backbone.js 0.9.2
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);

16
api/files/css Normal file
View file

@ -0,0 +1,16 @@
/* latin */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 400;
src: local('Droid Sans'), local('DroidSans'), url(http://fonts.gstatic.com/s/droidsans/v5/s-BiyweUPV0v-yRb-cjciPk_vArhqVIZ0nv9q090hN8.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* latin */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 700;
src: local('Droid Sans Bold'), local('DroidSans-Bold'), url(http://fonts.gstatic.com/s/droidsans/v5/EFpQQyG9GqCrobXxL-KRMYWiMMZ7xLd792ULpGE4W_Y.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

2
api/files/jquery-1.8.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

18
api/files/jquery.ba-bbq.min.js vendored Normal file
View file

@ -0,0 +1,18 @@
/*
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
* http://benalman.com/projects/jquery-bbq-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);

1
api/files/jquery.slideto.min.js vendored Normal file
View file

@ -0,0 +1 @@
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);

8
api/files/jquery.wiggle.min.js vendored Normal file
View file

@ -0,0 +1,8 @@
/*
jQuery Wiggle
Author: WonderGroup, Jordan Thomas
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
*/
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};

125
api/files/reset.css Normal file
View file

@ -0,0 +1,125 @@
/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

1221
api/files/screen.css Normal file

File diff suppressed because it is too large Load diff

2765
api/files/shred.bundle.js Normal file

File diff suppressed because it is too large Load diff

211
api/files/swagger-oauth.js Normal file
View file

@ -0,0 +1,211 @@
var appName;
var popupMask;
var popupDialog;
var clientId;
var realm;
function handleLogin() {
var scopes = [];
if(window.swaggerUi.api.authSchemes
&& window.swaggerUi.api.authSchemes.oauth2
&& window.swaggerUi.api.authSchemes.oauth2.scopes) {
scopes = window.swaggerUi.api.authSchemes.oauth2.scopes;
}
if(window.swaggerUi.api
&& window.swaggerUi.api.info) {
appName = window.swaggerUi.api.info.title;
}
if(popupDialog.length > 0)
popupDialog = popupDialog.last();
else {
popupDialog = $(
[
'<div class="api-popup-dialog">',
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
'<div class="api-popup-content">',
'<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
'<a href="#">Learn how to use</a>',
'</p>',
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
'<ul class="api-popup-scopes">',
'</ul>',
'<p class="error-msg"></p>',
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
'</div>',
'</div>'].join(''));
$(document.body).append(popupDialog);
popup = popupDialog.find('ul.api-popup-scopes').empty();
for (i = 0; i < scopes.length; i ++) {
scope = scopes[i];
str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
if (scope.description) {
str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
}
str += '</label></li>';
popup.append(str);
}
}
var $win = $(window),
dw = $win.width(),
dh = $win.height(),
st = $win.scrollTop(),
dlgWd = popupDialog.outerWidth(),
dlgHt = popupDialog.outerHeight(),
top = (dh -dlgHt)/2 + st,
left = (dw - dlgWd)/2;
popupDialog.css({
top: (top < 0? 0 : top) + 'px',
left: (left < 0? 0 : left) + 'px'
});
popupDialog.find('button.api-popup-cancel').click(function() {
popupMask.hide();
popupDialog.hide();
});
popupDialog.find('button.api-popup-authbtn').click(function() {
popupMask.hide();
popupDialog.hide();
var authSchemes = window.swaggerUi.api.authSchemes;
var host = window.location;
var redirectUrl = host.protocol + '//' + host.host + "/o2c.html";
var url = null;
var p = window.swaggerUi.api.authSchemes;
for (var key in p) {
if (p.hasOwnProperty(key)) {
var o = p[key].grantTypes;
for(var t in o) {
if(o.hasOwnProperty(t) && t === 'implicit') {
var dets = o[t];
url = dets.loginEndpoint.url + "?response_type=token";
window.swaggerUi.tokenName = dets.tokenName;
}
}
}
}
var scopes = []
var o = $('.api-popup-scopes').find('input:checked');
for(k =0; k < o.length; k++) {
scopes.push($(o[k]).attr("scope"));
}
window.enabledScopes=scopes;
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes);
window.open(url);
});
popupMask.show();
popupDialog.show();
return;
}
function handleLogout() {
for(key in window.authorizations.authz){
window.authorizations.remove(key)
}
window.enabledScopes = null;
$('.api-ic.ic-on').addClass('ic-off');
$('.api-ic.ic-on').removeClass('ic-on');
// set the info box
$('.api-ic.ic-warning').addClass('ic-error');
$('.api-ic.ic-warning').removeClass('ic-warning');
}
function initOAuth(opts) {
var o = (opts||{});
var errors = [];
appName = (o.appName||errors.push("missing appName"));
popupMask = (o.popupMask||$('#api-common-mask'));
popupDialog = (o.popupDialog||$('.api-popup-dialog'));
clientId = (o.clientId||errors.push("missing client id"));
realm = (o.realm||errors.push("missing realm"));
if(errors.length > 0){
log("auth unable initialize oauth: " + errors);
return;
}
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
$('.api-ic').click(function(s) {
if($(s.target).hasClass('ic-off'))
handleLogin();
else {
handleLogout();
}
false;
});
}
function onOAuthComplete(token) {
if(token) {
if(token.error) {
var checkbox = $('input[type=checkbox],.secured')
checkbox.each(function(pos){
checkbox[pos].checked = false;
});
alert(token.error);
}
else {
var b = token[window.swaggerUi.tokenName];
if(b){
// if all roles are satisfied
var o = null;
$.each($('.auth #api_information_panel'), function(k, v) {
var children = v;
if(children && children.childNodes) {
var requiredScopes = [];
$.each((children.childNodes), function (k1, v1){
var inner = v1.innerHTML;
if(inner)
requiredScopes.push(inner);
});
var diff = [];
for(var i=0; i < requiredScopes.length; i++) {
var s = requiredScopes[i];
if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
diff.push(s);
}
}
if(diff.length > 0){
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
$(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
// sorry, not all scopes are satisfied
$(o).find('.api-ic').addClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
else {
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
$(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
// all scopes are satisfied
$(o).find('.api-ic').addClass('ic-info');
$(o).find('.api-ic').removeClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
}
});
window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
}
}
}
}

2315
api/files/swagger-ui.js Normal file

File diff suppressed because it is too large Load diff

1604
api/files/swagger.js Normal file

File diff suppressed because it is too large Load diff

32
api/files/underscore-min.js vendored Normal file
View file

@ -0,0 +1,32 @@
// Underscore.js 1.3.3
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

15
api/package.json Normal file
View file

@ -0,0 +1,15 @@
{
"name": "swagger-cli-validator",
"version": "0.0.1",
"description": "",
"main": "validator.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"nopt": "^3.0.2",
"swagger-parser": "^3.2.1"
}
}

76
api/swagger.html Normal file
View file

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Matrix Client-Server API Documentation</title>
<link href="./files/css" rel="stylesheet" type="text/css">
<link href="./files/reset.css" media="screen" rel="stylesheet" type="text/css">
<link href="./files/screen.css" media="screen" rel="stylesheet" type="text/css">
<link href="./files/reset.css" media="print" rel="stylesheet" type="text/css">
<link href="./files/screen.css" media="print" rel="stylesheet" type="text/css">
<script type="text/javascript" src="./files/shred.bundle.js"></script>
<script src="./files/jquery-1.8.0.min.js" type="text/javascript"></script>
<script src="./files/jquery.slideto.min.js" type="text/javascript"></script>
<script src="./files/jquery.wiggle.min.js" type="text/javascript"></script>
<script src="./files/jquery.ba-bbq.min.js" type="text/javascript"></script>
<script src="./files/handlebars-1.0.0.js" type="text/javascript"></script>
<script src="./files/underscore-min.js" type="text/javascript"></script>
<script src="./files/backbone-min.js" type="text/javascript"></script>
<script src="./files/swagger.js" type="text/javascript"></script>
<script src="./files/swagger-ui.js" type="text/javascript"></script>
<script src="./files/highlight.7.3.pack.js" type="text/javascript"></script>
<!-- enabling this will enable oauth2 implicit scope support -->
<script src="./files/swagger-oauth.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
window.swaggerUi = new SwaggerUi({
url: "http://localhost:8000/client-server/api-docs",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function(swaggerApi, swaggerUi){
log("Loaded SwaggerUI");
if(typeof initOAuth == "function") {
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none"
});
$('#input_apiKey').change(function() {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if(key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("key", new ApiKeyAuthorization("access_token", key, "query"));
}
})
window.swaggerUi.load();
});
</script>
</head>
<body class="swagger-section">
<div id="swagger-header">
<div class="swagger-ui-wrap">
<form id="api_selector">
<div class="input"><input placeholder="access_token" id="input_apiKey" name="apiKey" type="text"></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap message-fail">Can't read from server. It may not have the appropriate access-control-origin settings.</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body></html>

71
api/validator.js Normal file
View file

@ -0,0 +1,71 @@
"use strict";
var fs = require("fs");
var nopt = require("nopt");
var parser = require("swagger-parser");
var path = require("path");
var opts = nopt({
"help": Boolean,
"schema": path
}, {
"h": "--help",
"s": "--schema"
});
if (opts.help) {
console.log(
"Use swagger-parser to validate against Swagger 2.0\n"+
"Usage:\n"+
" node validator.js -s <schema_file_or_folder>"
);
process.exit(0);
}
if (!opts.schema) {
console.error("No [s]chema specified.");
process.exit(1);
}
var errFn = function(err, api) {
if (!err) {
return;
}
console.error(err);
process.exit(1);
};
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);
}
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);
}
});
}
});
});
}
else{
parser.validate(opts.schema, function(err, api) {
if (!err) {
console.log("%s is valid", opts.schema);
}
else {
errFn(err, api);
}
});
};

View file

@ -1,63 +0,0 @@
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-cyrillic-ext-normal.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-cyrillic-normal.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-greek-ext-normal.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-greek-normal.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-vietnamese-normal.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-latin-ext-normal.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-latin-normal.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View file

@ -1,30 +0,0 @@
# Fonts
## Inter.css
`Inter.css` is a local copy of
https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap, modified to pull
font files (`.woff2`) from local sources. It was created
using `download_google_fonts_css.py`.
## download_google_fonts_css.py
`download_google_fonts_css.py` is a script that takes a google fonts CSS URL, downloads
the file and linked fonts, then saves the fonts locally along with a modified CSS file to
load them. Example call:
```sh
python3 download_google_fonts_css.py \
"https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap" \
../../../static/fonts \
../../fonts
```
Which would pop out a `Inter.css` file that should be `@import url("Inter.css")`d
somewhere in the site's SCSS (currently in
[/assets/scss/_variables_project.scss](/assets/scss/_variables_project.scss)).
Re-running the script and committing any new files is only necessary when a desired
font updates (not very often), or we want to change the font we're using. In that case,
remove the existing font files at `/static/fonts/*.woff2` and re-run the script with a
different URL.

View file

@ -1,131 +0,0 @@
#!/usr/bin/env python3
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script takes a google fonts CSS URL, downloads the referenced fonts locally
# and spits out a modified CSS file which now points to those local fonts.
#
# Purely for the purposes of converting a website to use local font files instead of
# making requests to Google Fonts.
import re
import requests
import subprocess
import sys
# Pull the font filename to process and output directory to point at
if len(sys.argv) < 4:
print(f"""
Error: Not enough arguments.
Usage: {sys.argv[0]} google_fonts_url font_directory css_font_path
* google fonts url: A URL leading to a google font css file (i.e https://fonts.googleapis.com/css?family=Inter)
* font directory: The location that font files will be downloaded to (i.e ../../fonts)
* font path: The directory the resulting CSS will be pointing to, relative to site root (i.e /unstable/css/fonts).
This is where the browser will look for fonts.
""")
sys.exit(1)
google_fonts_url = sys.argv[1]
font_output_dir = sys.argv[2]
css_font_path = sys.argv[3]
# Get font name
if google_fonts_url.count(":") > 1:
# Account for font weights specified in the URL
# (i.e https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i)
url_match = re.match(r".*family=(.*):", google_fonts_url)
else:
url_match = re.match(r".*family=(.*)", google_fonts_url)
if not url_match:
print("Unable to extract font name, invalid google fonts URL:", google_fonts_url)
print("URL should look something like: https://fonts.googleapis.com/css?family=Inter...")
sys.exit(1)
font_name = url_match.group(1)
# Ensure font paths end with a trailing slash
if not font_output_dir.endswith("/"):
font_output_dir += "/"
if not css_font_path.endswith("/"):
css_font_path += "/"
# Download the css file and split by newline
resp = requests.get(
google_fonts_url,
# We need to set a believable user-agent, else google fonts won't give us
# all of the font weights we requested for some reason
headers={
"User-Agent": "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:86.0) Gecko/20100101 Firefox/86.0"
}
)
if resp.status_code != 200:
print("Failed to download:", google_fonts_url, resp.status_code)
sys.exit(1)
original_contents = resp.text.split("\n")
# Download all referenced font files and write out new font file
new_css_file_lines = []
# Store metadata for helping give friendly names to each font file
font_lang = None
font_family = None
font_style = None
for line in original_contents:
# Check if this line contains a font URL
match = re.match(r".*url\((.*)\) format.*", line)
if match:
# Download the font file
font_url = match.group(1)
print("Downloading font:", font_url)
resp = requests.get(font_url)
if resp.status_code == 200:
# Save the font file
filename = "%s-%s-%s.woff2" % (
font_family, font_lang, font_style
)
font_filepath = font_output_dir + filename
with open(font_filepath, "wb") as f:
print("Writing font file:", font_filepath)
f.write(resp.content)
# Replace google URL with local URL and allow the browser to load the
# local font if it exists.
line = re.sub(r"url\(.+?\)", f"local('{font_family}'), url({css_font_path + filename})", line)
else:
print("Warning: failed to download font file:", font_url)
# Check for font metadata. If there is some, we'll note it down and use it to help
# name font files when we write them out.
# Makes for nicer font filenames than fvQtMwCp50KnMw2boKod... etc.
font_lang_match = re.match(r"^/\* (.+) \*/$", line)
if font_lang_match:
font_lang = font_lang_match.group(1)
font_family_match = re.match(r".*font-family: '(.+)';$", line)
if font_family_match:
font_family = font_family_match.group(1)
font_style_match = re.match(r".*font-style: (.+);$", line)
if font_style_match:
font_style = font_style_match.group(1)
# Append the potentially modified line to the new css file
new_css_file_lines.append(line)
# Write out the new font css file
with open(font_name + ".css", "w") as f:
new_css_file_contents = "\n".join(new_css_file_lines)
f.write(new_css_file_contents)

View file

@ -1 +0,0 @@
requests

View file

@ -1,23 +0,0 @@
# Spec diagrams
Non-ascii diagrams for the spec can be placed here for reference in the actual spec.
Please include source material so the diagram can be recreated by a future editor.
https://www.diagrams.net/ is a great ([open source](https://github.com/jgraph/drawio))
tool for these sorts of things - include your `.drawio` file next to your diagram.
Suggested settings for diagrams.net:
* Export as WebP.
* 200% size.
* `20` for a border width.
* Light appearance.
* No shadow, or grid.
To reference a diagram, use the `diagram` shortcode. For example:
```
{{% diagram name="membership" alt="Diagram presenting the possible membership state transitions" %}}
```
Where `name` is the file name without extension, and `alt` is a textual
replacement for the image, useful for accessibility.

View file

@ -1 +0,0 @@
<mxfile host="Electron" modified="2022-02-18T05:31:13.369Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36" etag="FwSKbslSItXwNPoLLuzM" version="16.5.1" type="device"><diagram id="4a_pTli-mcEMNPq0ciXK" name="Page-1">1VvZkto4FP0aqjIPdNnyBo9peklSnZmeoWayvKQECHC3sSghN5CvHxnLeFMbyeCFh1TQ9ZUX6Zyje6/UPWO02j0SuF5+xTPk9YA22/WMux4Auu4M2H+hZR9ZHE2LDAvizrhTYhi7vxE3xm6BO0ObjCPF2KPuOmucYt9HU5qxQULwNus2x172qWu4QAXDeAq9ovWbO6PLyDoATmL/hNzFMn6ybg+jKysYO/Mv2SzhDG9TJuO+Z4wIxjT6tdqNkBcOXjwuUb+Hd64eX4wgn8p0eNas3x+fbr2x/3O79n9ao2BI+zq/zRv0Av7F7A4v2PX5S9N9PBJoxgaGNzGhS7zAPvTuE+stwYE/Q+HjNNZKfJ4wXjOjzowviNI9n2UYUMxMS7ry+NXiJ/HX2+CATFHZd3BoQLJAtMQPRH7ht6QewAfsEeEVomTPHDh++9qNo5tG1IkgD1L3LQsMyPG1OPblt/tICNynHNZsTOkm9bTn0JA8Cgw5TjhRDDs3nWr+7Ef0Brne8evg+XzDRirbIzUEiekAIxVIFRDl+m8uRQVAZeGyXTKf8RoepnnLxEQSGm+IULSTmUymPZkRAxZvbxNi67E8LVOkjv1Ec50ZPeWhskXs8xBkIAMaJr2Q0g+v7vT1utgIJNloqrFRu9GG9qAKGZvEPwCiSZ3AVhQV7Vz6Pex+Y/HWj9SVux2/86Gxjxs+G4CoE7Di9rFb2Ej6HVpxx/oBYyjLt21Vk+9GEVMAjHABVtLLOfbpA1y5Xjg5Izb+Lgrl5E+0rUdMTbN1MR2IeBf4bTMvxTsWTVhy1NPTvIu71UI948JaLU2zsybbKMy1aJo7xRjLygVsoHXGCFeqSHs0d94Dtsde4XbChsFehL+g5+EtG0+gTfbFq2HHXyTwWLpWuPZhHUw8d9oDoxBDdPpHu4shqLQaOpnVUKuNkqYkJWWTmWYoqRc5ye7zTuzfxGy/N3GnwhixcoNy5W5gti3l4MfqeuSjOx2MlXWVeU9Qll2xQX0rtixgZIPlZuTBLMw0z3O7vGYb+SJL62u2MMhtUWSF+aVWiTL1MQYM69NYx7TPU9kYiPnqlGNlbxF9Je8lKgw2INfaiYBRO0aI+QAwDBi1q4kQs0uAbNKWw3ONSZs8oC+9BsiB17TlwKtcJAe553CSvFskL/gPcrxIFckvVs4R7qm0FdQkUXC1IFihcNgAntULgANg6LUItGXWg3Fdzz3HKN8IsrVS//Y2goBwd6MDsUo1cVdM/zOh6SfkvSHqTuF5LKkrVWQkN0GljRVVcBsxaeIoRysR5CbBanRKtIVhiDRQwdUAVV3OHcs6U84bEL5irvvqY8Embqdz3eN610Su+/W7/up/s/99HgXOuK/dYQT+FpwkCHfDhSN5ZamuZHUox4uKZLRkVw3zFPlMfQA6RD4haIAINOJSU/cTSkUpbwM1p0EzMIYdAk3p11740FLTm/CNnmgSsq+48l3qANNLsFrH/pBMz9Vw53wRl6zvXIaOsgnySToOHKuRSF83c6VzKxPpnyzV5PxbTGOHBUzf7ygiDHe98J0LhUwfH16M/Vuh1QSRYiUzQbauKCJS8bnCmYRcecwQ6AcQ6Idd1/aGIaww85gvKjEXR/xwuQ8nbHhLDyW0GTOCKnpzc5lELi2dPvZRbIre2mmoEq142DPM9WzTaUKqBrnExywvuJlmqX+LJ6+LStV+DaMR6BeDgwYiX/XixdDQzCYADYbZKptuqZWQc/4trr3C8yE8m8tLfdvn5c88a301RTt1IT9zh/yiOPuCbNsb//X58fafh/92v54o+fFZcJw0F3VUDTny19rFZaWkR/H0XHUASu9uqAPQzMhbv8VzcayZ/Mlf5J784aRx/z8=</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1 +0,0 @@
<mxfile host="app.diagrams.net" modified="2022-09-27T03:26:23.216Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="YZcXq9Sm_7Lqw5o2RvSU" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7ZpdU+IwFIZ/DZc6bdNWuJQCujPquOvOrl452TbQaNowIXztr9+EJv0goLCirYwyo81JGpL3PQ8nU2mBIFlcMDiOr2mESMuxokUL9FqO41i+I/7IyDKL2JbvZZERw5GKFYE7/BfpgSo6xRGaVAZySgnH42owpGmKQl6JQcbovDpsSEn1XcdwhIzAXQiJGf2NIx5n0bZzVsQvER7F+p1tv5P1JFAPVjuZxDCi81II9FsgYJTy7CpZBIhI9bQu2X2DLb35whhK+S43/PIf6YMfPSfXNzfx1WP3ez/2ToDaxwySqdrxz5ghGInYudQaJ4jgFKkt8KXWhdFpGiE5td0C3XmMObobw1D2zkUqiFjME6K6hzTlA5hgIrPgEpEZ4jiEsgMTElBC2WpS0O/Jl4jPEJMjyDnBo1T0cTpW09ypJajty4FosVURO9dZZCiiCeJsKYbo9NTWqOT0dHteOO2c+acqY+Oyz201FKr8GuWzFxaIC+XCPo742x3pHrsjAFQdcS3TEdu3Nvhhv5sfruHHNcTpsTuRq7zUHJhO5G59jBO2YcS5If0rYsPJOCsSQ7yQBq2LHAR9byDW2D2Egp2qgsDeoOAGAcG76dc25EKRKHaqSRmP6YimkPSLaLeay8WYKyrTbhV8QpwvVeWGU06rkqMF5vfi2lLXD/JafKRmrd6i1NVb6kYq9nuvJ5CN0l2yWdy2aun7Kiz9FHhOxHZv0Fz8/kETmObGyn2/bKuQiU5ZiF7QUx1tOGQjxF/LWzNNGCKQ41l1HQc33TGg6TYZGsdvGDQueDM0T9NkrMenNEU1cFRC56GM1RaOCPyDSBeGz6PVTkpeDwL5eqlsHRAw8BkAAwZgwZsBOwRIbn5u1afbjlczSs4XSnWh5O6IklMnSuZJu9cElMBaTXLdumuSewwg7Xm2awhI3o4ggTpB8gyQ+o0AyW5eTbINYb5Q+iCU/B1RcutEyXwaN2gCSu76g7K6axLoGEIlpwzBkGOaHgNje577IjiJ8+XLxi3kHLF0FXEstzkYnr2toqmEPLFObfFTSUpbfbjvTKqa/ZZisYliCB0OJ2Jp6ymaL+L/s9b878dFE+g2C6Xv1lwoQXsD3yjC/BjY3rN+fh62Ozuy7dVZYs3KcdkECF3QvNOqZQhz/LQ1BCX9ILrZj1D0KkswfWsETO6HPdcXzeJLFdlBofhuCuj/Aw==</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1 +0,0 @@
<mxfile host="app.diagrams.net" modified="2022-09-27T03:11:43.523Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="L_ujIRop4Jndk67DcTE9" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7VpbU+IwFP41PMqkTS/wKDfdGXXcdWdXn5wsDTTaNkwIAvvrN6UJbYlCF1gbXcYZTU5P0ub0u6S1DdiNFxcMTcJrGuCoYYNg0YC9hm3bwLPFnzSyzCIW8NwsMmYkkLE8cEd+Y5UoozMS4GkpkVMacTIpB4c0SfCQl2KIMTovp41oVD7rBI2xFrgbokiP/iQBD7Noy/bz+CUm41Cd2fLa2ZEYqWS5kmmIAjovhGC/AbuMUp614kUXR2n1VF2ycYM3jq4vjOGEVxnww3ukD17wHF/f3IRXj52v/dA9k7O8oGgmF3wur5YvVQnmIeH4boKGaX8u7nMDdkIeR6JniSaaTrLCj8gCi3N15JSYcbx481qtdQUEdjCNMWdLkaIGQFk0BRtH9uf5PVApYaH8KobkXR+vZ84LIxqyNn9Tp5ZWFhwIoMguZTykY5qgqJ9HO4zOkiAtyapOec4VpRMZfMKcLyXq0YzTcmnxgvB70Qay/ZC2m67s9RaFQ72l6iRivfdqgrRTGJV282Grnho3ogkfoJhEaeA7iQXpbHCD5+L3NxqjZH1j03Vvv62iTHTGhnhLPaUscMTGmO/Cpw4ThiPEyUv5Oo5+022NHB0TyGED08jhn8hxTHLAiuSwayWHr7EjbvKQYRQcjIanWTxR+QlN8IEAadpuASNWNYSApu8WQWLtgEiApuF6AWnnFnGOWbKK2MAR0Qj9wlEHDZ/Hq+V2aUTZqkBw0E1/NoF2iaMXzMkQ1QOvWrUXaujqGqG9vmna2z5p7zHJ4VQkB6xVe52Po71F6QXVALIpvWAHQj6O9FZFV63OroOrZ4L0Qsc06XVP0ntMcrgVyeHUKr3ux5He/ba99ifd9laF13YVOgNNB/pSIitDbjXdOWNoWUiYUJLwaeFst2kgP5WjZpSCp577B2/kW7C1LV80sivYGK0uh45GU1GYTYKsS7A/Z3TK9E0wFMeCTdcwS3FOlnJMznsVOe/Wain6e0ZjLWWv3bz9SXfzVdF16G5+L/dwwYa27XAPG7S25Zfd41jO4GnIHxjhDG3THjXgyReOyVy/InO9Wn1BZ0fcFK4w5IQmRjvDf/+KvSq+Dn2LuJ8ztMrOoH04sOkMbbgt/984g/7fpQsTnME17v2/fZASgJMzlOvZrshcv1ZnAK84Aw4If7ddwrvq/puKXcEQpkIeSDL+vloMyAOi54LXbbCA8wp2UgP2an1abWvQuzRBmD3XNGG2tLKchPkAcqgPQneyo10nOyxdmb+YQA///T6oFN38o9ZsO5h/Gwz7fwA=</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<style>
path { fill: #000000; }
@media (prefers-color-scheme: dark) {
path { fill: #ffffff; }
}
</style>
<path d="M 30,2.0000001 V 30 h -1 -2 v 2 h 5 V -3.3333334e-8 L 27,0 v 2 z"/>
<path d="M 9.9515939,10.594002 V 12.138 h 0.043994 c 0.3845141,-0.563728 0.8932271,-1.031728 1.4869981,-1.368 0.580003,-0.322998 1.244999,-0.485 1.993002,-0.485 0.72,0 1.376999,0.139993 1.971998,0.42 0.595,0.279004 1.047001,0.771001 1.355002,1.477001 0.338003,-0.500001 0.795999,-0.941 1.376999,-1.323001 0.579999,-0.382998 1.265998,-0.574 2.059998,-0.574 0.602003,0 1.160002,0.074 1.674002,0.220006 0.514,0.148006 0.953998,0.382998 1.321999,0.706998 0.36601,0.322999 0.653001,0.746 0.859,1.268002 0.205001,0.521998 0.307994,1.15 0.307994,1.887001 v 7.632997 h -3.127 v -6.463997 c 0,-0.383002 -0.01512,-0.743002 -0.04399,-1.082003 -0.02079,-0.3072 -0.103219,-0.607113 -0.242003,-0.881998 -0.133153,-0.25081 -0.335962,-0.457777 -0.584001,-0.596002 -0.257008,-0.146003 -0.605998,-0.220006 -1.046997,-0.220006 -0.440002,0 -0.796003,0.085 -1.068,0.253002 -0.272013,0.170003 -0.485001,0.390002 -0.639001,0.662003 -0.159119,0.287282 -0.263585,0.601602 -0.307994,0.926997 -0.05197,0.346923 -0.07801,0.697217 -0.07801,1.048002 v 6.353999 h -3.128005 v -6.398 c 0,-0.338003 -0.0072,-0.673001 -0.02116,-1.004001 -0.01134,-0.313663 -0.07487,-0.623229 -0.187994,-0.915999 -0.107943,-0.276623 -0.300435,-0.512126 -0.550001,-0.673001 -0.25799,-0.168 -0.636,-0.253002 -1.134999,-0.253002 -0.198123,0.0083 -0.394383,0.04195 -0.584002,0.100006 -0.258368,0.07446 -0.498455,0.201827 -0.704999,0.373985 -0.227981,0.183987 -0.421999,0.449 -0.583997,0.794003 -0.161008,0.345978 -0.242003,0.797998 -0.242003,1.356998 v 6.618999 H 6.99942 V 10.590001 Z"/>
<path d="M 2,2.0000001 V 30 h 3 v 2 H 0 V 9.2650922e-8 L 5,0 v 2 z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1,5 +0,0 @@
<svg width="75" height="32" xmlns="http://www.w3.org/2000/svg">
<g fill="#000" fill-rule="nonzero">
<path d="M.936.732V31.25H3.13v.732H.095V0h3.034v.732zM9.386 10.407v1.544h.044a4.461 4.461 0 0 1 1.487-1.368c.58-.323 1.245-.485 1.993-.485.72 0 1.377.14 1.972.42.595.279 1.047.771 1.355 1.477.338-.5.796-.941 1.377-1.323.58-.383 1.266-.574 2.06-.574.602 0 1.16.074 1.674.22.514.148.954.383 1.322.707.366.323.653.746.859 1.268.205.522.308 1.15.308 1.887v7.633H20.71v-6.464c0-.383-.015-.743-.044-1.082a2.305 2.305 0 0 0-.242-.882 1.473 1.473 0 0 0-.584-.596c-.257-.146-.606-.22-1.047-.22-.44 0-.796.085-1.068.253-.272.17-.485.39-.639.662a2.654 2.654 0 0 0-.308.927 7.074 7.074 0 0 0-.078 1.048v6.354h-3.128v-6.398c0-.338-.007-.673-.021-1.004a2.825 2.825 0 0 0-.188-.916 1.411 1.411 0 0 0-.55-.673c-.258-.168-.636-.253-1.135-.253a2.33 2.33 0 0 0-.584.1 1.94 1.94 0 0 0-.705.374c-.228.184-.422.449-.584.794-.161.346-.242.798-.242 1.357v6.619H6.434V10.407h2.952zM25.842 12.084a3.751 3.751 0 0 1 1.233-1.17 5.37 5.37 0 0 1 1.685-.629 9.579 9.579 0 0 1 1.884-.187c.573 0 1.153.04 1.74.121.588.081 1.124.24 1.609.475.484.235.88.562 1.19.981.308.42.462.975.462 1.666v5.934c0 .516.03 1.008.088 1.478.058.471.161.824.308 1.06H32.87a4.435 4.435 0 0 1-.22-1.104c-.5.515-1.087.876-1.762 1.081a7.084 7.084 0 0 1-2.071.31c-.544 0-1.05-.067-1.52-.2a3.472 3.472 0 0 1-1.234-.617 2.87 2.87 0 0 1-.826-1.059c-.199-.426-.298-.934-.298-1.522 0-.647.114-1.18.342-1.6.227-.419.52-.753.881-1.004.36-.25.771-.437 1.234-.562.462-.125.929-.224 1.399-.298.47-.073.932-.132 1.387-.176.456-.044.86-.11 1.212-.199.353-.088.631-.217.837-.386.206-.169.301-.415.287-.74 0-.337-.055-.606-.166-.804a1.217 1.217 0 0 0-.44-.464 1.737 1.737 0 0 0-.639-.22 5.292 5.292 0 0 0-.782-.055c-.617 0-1.101.132-1.454.397-.352.264-.558.706-.617 1.323h-3.128c.044-.735.227-1.345.55-1.83zm6.179 4.423a5.095 5.095 0 0 1-.639.165 9.68 9.68 0 0 1-.716.11c-.25.03-.5.067-.749.11a5.616 5.616 0 0 0-.694.177 2.057 2.057 0 0 0-.594.298c-.17.125-.305.284-.408.474-.103.192-.154.434-.154.728 0 .28.051.515.154.706.103.192.242.342.419.453.176.11.381.187.617.231.234.044.477.066.726.066.617 0 1.094-.102 1.432-.309.338-.205.587-.452.75-.739.16-.286.26-.576.297-.87.036-.295.055-.53.055-.707v-1.17a1.4 1.4 0 0 1-.496.277zM43.884 10.407v2.096h-2.291v5.647c0 .53.088.883.264 1.059.176.177.529.265 1.057.265.177 0 .345-.007.507-.022.161-.015.316-.037.463-.066v2.426a7.49 7.49 0 0 1-.882.089 21.67 21.67 0 0 1-.947.022c-.484 0-.944-.034-1.377-.1a3.233 3.233 0 0 1-1.145-.386 2.04 2.04 0 0 1-.782-.816c-.191-.353-.287-.816-.287-1.39v-6.728H36.57v-2.096h1.894v-3.42h3.129v3.42h2.29zM48.355 10.407v2.118h.044a3.907 3.907 0 0 1 1.454-1.754 4.213 4.213 0 0 1 1.036-.497 3.734 3.734 0 0 1 1.145-.176c.206 0 .433.037.683.11v2.912a5.862 5.862 0 0 0-.528-.077 5.566 5.566 0 0 0-.595-.033c-.573 0-1.058.096-1.454.287a2.52 2.52 0 0 0-.958.783 3.143 3.143 0 0 0-.518 1.158 6.32 6.32 0 0 0-.154 1.434v5.14h-3.128V10.407h2.973zM54.039 8.642V6.06h3.128v2.582H54.04zm3.128 1.765v11.405H54.04V10.407h3.128zM58.797 10.407h3.569l2.005 2.978 1.982-2.978h3.459l-3.745 5.339 4.208 6.067h-3.57l-2.378-3.596-2.38 3.596h-3.502l4.097-6.001zM74.094 31.25V.732H71.9V0h3.035v31.982H71.9v-.732z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -1,169 +0,0 @@
/*
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Only call the given function once every 250 milliseconds to avoid impacting
the performance of the browser.
Source: https://remysharp.com/2010/07/21/throttling-function-calls
*/
function throttle(fn) {
const threshold = 250;
let last = null;
let deferTimer = null;
return function (...args) {
const now = new Date();
if (last && now < last + threshold) {
// Hold on to it.
clearTimeout(deferTimer);
deferTimer = setTimeout(() => {
last = now;
fn.apply(this, args);
}, threshold);
} else {
last = now;
fn.apply(this, args);
}
}
}
/*
Get the list of headings that appear in the ToC.
This is not as simple as querying all the headings in the content, because
some headings are not rendered in the ToC (e.g. in the endpoint definitions).
*/
function getHeadings() {
let headings = [];
// First get the anchors in the ToC.
const toc_anchors = document.querySelectorAll("#toc nav a");
for (const anchor of toc_anchors) {
// Then get the heading from its selector in the anchor's href.
const selector = anchor.getAttribute("href");
if (!selector) {
console.error("Got ToC anchor without href");
continue;
}
const heading = document.querySelector(selector);
if (!heading) {
console.error("Heading not found for selector:", selector);
continue;
}
headings.push(heading);
}
return headings;
}
/*
Get the heading of the text visible at the top of the viewport.
This is the first heading above or at the top of the viewport.
*/
function getCurrentHeading(headings, headerOffset) {
const scrollTop = document.documentElement.scrollTop;
let prevHeading = null;
let currentHeading = null;
let index = 0;
for (const heading of headings) {
// Compute the position compared to the viewport.
const rect = heading.getBoundingClientRect();
if (rect.top >= headerOffset && rect.top <= headerOffset + 30) {
// This heading is at the top of the viewport, this is the current heading.
currentHeading = heading;
break;
}
if (rect.top >= headerOffset) {
// This is in or below the viewport, the current heading should be the
// previous one.
if (prevHeading) {
currentHeading = prevHeading;
} else {
// The first heading does not have a prevHeading.
currentHeading = heading;
}
break;
}
prevHeading = heading;
index += 1;
}
// At the bottom of the page we might not have a heading.
if (!currentHeading) {
currentHeading = prevHeading;
}
return currentHeading;
}
/*
Select the ToC entry that points to the given ID.
Clear any previously highlighted ToC items, select the new one,
and adjust the ToC scroll position.
*/
function selectTocEntry(id) {
// Deselect previously selected entries.
const activeEntries = document.querySelectorAll("#toc nav a.active");
for (const activeEntry of activeEntries) {
activeEntry.classList.remove('active');
}
// Find the new entry and select it.
const newEntry = document.querySelector(`#toc nav a[href="#${id}"]`);
if (!newEntry) {
console.error("ToC entry not found for ID:", id);
return;
}
newEntry.classList.add('active');
// Don't scroll the sidebar nav if the main content is not scrolled
const nav = document.querySelector("#td-section-nav");
const content = document.querySelector("html");
if (content.scrollTop !== 0) {
nav.scrollTop = newEntry.offsetTop - 100;
} else {
nav.scrollTop = 0;
}
}
/*
Track when the view is scrolled, and use this to update the highlight for the
corresponding ToC entry.
*/
window.addEventListener('DOMContentLoaded', () => {
// Part of the viewport is below the header so we should take it into account.
const headerOffset = document.querySelector("body > header > nav").clientHeight;
const headings = getHeadings();
const onScroll = throttle((_e) => {
// Update the ToC.
let heading = getCurrentHeading(headings, headerOffset);
selectTocEntry(heading.id);
});
// Initialize the state of the ToC.
onScroll();
// Listen to scroll and resizing changes.
document.addEventListener('scroll', onScroll, false);
document.addEventListener('resize', onScroll, false);
});

View file

@ -1,114 +0,0 @@
/*
Copyright 2025 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Determine the current version as defined in hugo.toml. This will either be
// "unstable" or "vX.X" and doesn't depend on the current URL.
//
// The oddity below is an attempt at producing a readable Hugo template while
// avoiding JS syntax errors in your IDE.
const currentVersion = `{{ if eq .Site.Params.version.status "unstable" }}
{{- /**/ -}}
unstable
{{- /**/ -}}
{{ else }}
{{- /**/ -}}
{{ printf "v%s.%s" .Site.Params.version.major .Site.Params.version.minor }}
{{- /**/ -}}
{{ end }}`;
// Determine the current version segment by regex matching the URL. This will either
// be "unstable", "latest", "vX.X" (production) or undefined (local & netlify).
const href = window.location.href;
const segmentMatches = href.match(/(?<=\/)unstable|latest|v\d+.\d+(?=\/)/);
const currentSegment = segmentMatches ? segmentMatches[0] : undefined;
// Determine the selected menu element. If we were able to obtain the version
// segment from the URL (production), use that. Otherwise (local & netlify),
// fall back to the version as defined in Hugo.
const selected = currentSegment ?? currentVersion;
function appendVersion(parent, name, segment, url) {
// The list item
const li = document.createElement("li");
if (segment === selected) {
li.classList.add("version-picker-selected");
}
if (segment === "latest") {
li.classList.add("version-picker-latest");
}
parent.appendChild(li);
// The link
const a = document.createElement("a");
a.classList.add("dropdown-item");
a.setAttribute("href", url);
li.appendChild(a);
// Handle clicks manually to preserve the current path / fragment
a.addEventListener("click", (ev) => {
// If the URL is a relative link (i.e. the historical versions changelog), just
// let the browser load it
if (url.startsWith("/")) {
return;
}
// If we couldn't determine the current segment, we cannot safely replace
// it and have to let the browser load the (root) URL instead
if (!currentSegment) {
return;
}
// Otherwise, stop further event handling and replace the segment
ev.preventDefault();
ev.stopPropagation();
window.location.href = href.replace(`/${currentSegment}/`, `/${segment}/`);
});
// The link text
const text = document.createTextNode(name);
a.appendChild(text);
}
// If we're in the unstable version, we're the latest thing and can just load
// versions.json from our own resources. Otherwise, we fall back to loading it
// from /unstable/versions.json, assuming we are on the spec.matrix.org deployment.
const url = currentVersion === "unstable"
? '{{ .Site.Home.Permalink }}versions.json'
: "/unstable/versions.json";
fetch(url)
.then(r => r.json())
.then(versions => {
// Find the surrounding list element
const ul = document.querySelector("ul#version-selector");
if (!ul) {
console.error("Cannot populate version selector: ul element not found");
return;
}
// Add a entries for the unstable version and the "latest" shortcut
appendVersion(ul, "unstable", "unstable", "https://spec.matrix.org/unstable");
const latestName = versions?.length ? `latest (${versions[0].name})` : "latest";
appendVersion(ul, latestName, "latest", "https://spec.matrix.org/latest");
// Add an entry for each proper version
for (const version of versions) {
appendVersion(ul, version.name, version.name, `https://spec.matrix.org/${version.name}`);
}
// For historical versions, simply link to the changelog
appendVersion(ul, "historical", "historical", '{{ (site.GetPage "changelog/historical").RelPermalink }}');
});

View file

@ -1,647 +0,0 @@
/*
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Custom SCSS for the Matrix spec
*/
/* Import the CSS classes for the syntax highlighter.
*
* This is generated with:
*
* hugo gen chromastyles --style=tango > assets/scss/syntax.scss
*/
@import "syntax.scss";
/* Overrides for the navbar */
.td-navbar {
box-shadow: 0px 0px 8px rgba(179, 179, 179, 0.25);
min-height: 5rem;
.navbar-brand {
font-size: 1.1rem;
/* Allow the text to wrap if it is wider than the viewport */
text-align: center;
white-space: normal;
.navbar-version {
color: $secondary;
}
}
.nav-link {
font-weight: normal;
}
a {
color: $black;
}
/* Make the version dropdown scroll if it's too large */
ul#version-selector {
max-height: 80vh;
overflow-y: auto;
}
ul#version-selector li.version-picker-selected a {
font-weight: bold;
}
ul#version-selector li.version-picker-latest a {
color: $secondary;
}
}
/* Styles for the sidebar nav */
.td-sidebar {
/* don't attempt to use the sidebar (or things in it) as a scroll anchor. */
overflow-anchor: none;
}
.td-sidebar-nav {
scroll-behavior: smooth;
overscroll-behavior: contain;
&>.td-sidebar-nav__section {
margin-top: 1rem;
}
.td-sidebar-nav__section .ul-1 ul {
padding-left: 0;
}
/* This is to make the width of the items that have sub-items (like room versions)
the same as the width of items that don't (like changelog) */
.pr-md-3, .px-md-3 {
padding-right: 0 !important;
}
.ul-1 > li > a {
padding-left: 1rem !important;
}
.ul-2 > li > a {
padding-left: 2rem !important;
}
a.td-sidebar-link.tree-root {
color: $gray-800;
font-weight: $font-weight-bold;
font-size: 1.3rem;
margin-bottom: 0;
border-bottom: none;
}
a, a.td-sidebar-link {
color: $gray-800;
font-weight: $font-weight-normal;
padding-top: .2rem;
padding-bottom: .2rem;
display: block;
transition: all 100ms ease-in-out;
&:hover {
background-color: $secondary-lighter-background;
color: $gray-800;
}
&.active, &active:hover {
background-color: $secondary-background;
}
}
}
@include media-breakpoint-up(md) {
@supports (position: sticky) {
.td-sidebar-nav {
/* This overrides calc(100vh - 10rem);, which gives us a blank space at the bottom of the sidebar */
max-height: calc(100vh - 6rem);
}
}
}
/* Customise footer */
.td-footer {
box-shadow: 0px 0px 8px rgba(179, 179, 179, 0.25);
padding-top: 2rem;
color: var(--bs-body-color);
background-color: var(--bs-body-color-bg);
}
/* Auto numbering for headings */
.td-content {
counter-reset: h2;
&> h2 {
counter-reset: h3
}
&> h3 {
counter-reset: h4
}
&> h4 {
counter-reset: h5
}
&> h5 {
counter-reset: h6
}
&> h2:not(.no-numbers):before {
display: inline; visibility: visible; counter-increment: h2; content: counter(h2) ". "
}
&> h3:not(.no-numbers):before {
display: inline; visibility: visible; counter-increment: h3; content: counter(h2) "." counter(h3) ". "
}
&> h4:not(.no-numbers):before {
display: inline; visibility: visible; counter-increment: h4; content: counter(h2) "." counter(h3) "." counter(h4) ". "
}
&> h5:not(.no-numbers):before {
display: inline; visibility: visible; counter-increment: h5; content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". "
}
&> h6:not(.no-numbers):before {
display: inline; visibility: visible; counter-increment: h6; content: counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". "
}
}
/* Remove some padding before the main content, when the sidebar is disabled */
.td-main main {
@include media-breakpoint-down(md) {
padding-top: 0;
}
}
/* Adjust the scroll margin for everything in the main content, so that
* it doesn't disappear behind the header bar */
.td-content * {
scroll-margin-top: 5.5rem;
}
/* Styles for the table of contents */
#toc {
padding-top: .5rem;
padding-left: 1.5rem;
ol {
padding-left: 1rem;
counter-reset: section;
list-style-type: none;
}
#TableOfContents {
&>ol>li {
margin-bottom: .5rem;
&>a {
font-weight: $font-weight-bold;
}
}
ol {
padding-left: 0;
}
&>ol>li>a {
padding-left: 1rem;
}
&>ol>li>ol>li>a {
padding-left: 2rem;
}
&>ol>li>ol>li>ol>li>a {
padding-left: 3rem;
}
&>ol>li>ol>li>ol>li>ol>li>a {
padding-left: 4rem;
}
&>ol>li>ol>li>ol>li>ol>li>ol>li>a {
padding-left: 5rem;
}
}
li a:before {
counter-increment: section;
content: counters(section, ".") " ";
}
#toc-title {
font-weight: $font-weight-bold;
font-size: 1.3rem;
}
}
.endpoints-toc {
summary {
cursor: pointer;
font-weight: $font-weight-bold;
font-size: 1.05rem;
margin-bottom: 0.5rem;
}
.endpoint-list {
list-style: none;
padding-left: 0;
margin: 0;
}
.endpoint-list li {
margin: 0.2rem 0;
}
.endpoint-list a {
text-decoration: none;
color: inherit;
padding: 0.05rem 0.25rem;
border-radius: 0.2rem;
&:hover {
background-color: $secondary-background;
}
}
.endpoint-list .http-api-method {
margin-right: 0.35rem;
font-weight: $font-weight-bold;
}
.endpoint-path {
font-family: $font-family-monospace;
color: $secondary;
}
.endpoint-deprecated {
color: $danger;
font-weight: $font-weight-bold;
margin-left: 0.35rem;
}
.endpoint-module {
&:not(:first-child) {
margin-top: 0.75rem;
}
}
.endpoint-module-title {
// font-weight: $font-weight-bold;
font-size: 1.20rem;
margin-bottom: 0.35rem;
}
}
.page-description {
margin-bottom: 1rem;
color: inherit;
}
/* Styles for alert boxes */
.alert {
&.note {
&:not(.omit-title):before {
content: "INFO: ";
font-weight: $font-weight-bold;
}
border: 2px solid $note;
border-left-width: 5px;
background: $note-background;
}
&.rationale {
&:not(.omit-title):before {
content: "RATIONALE: ";
font-weight: $font-weight-bold;
}
border: 2px solid $note;
border-left-width: 5px;
background: $note-background;
}
&.warning {
&:not(.omit-title):before {
content: "WARNING: ";
font-weight: $font-weight-bold;
}
border: 2px solid $warning;
border-left-width: 5px;
background: $warning-background;
}
}
/* Styles for sections that are rendered from data, such as HTTP APIs and event schemas */
.td-content .rendered-data {
background-color: $secondary-lightest-background;
padding: 0.85rem;
margin: 0.85rem 0;
details {
summary {
h1 {
margin: 0;
/* Ensure the disclosure control is vertically centred with the header text. */
vertical-align: middle;
}
}
p {
max-width: 80%;
}
}
.deprecated-inline {
&:after {
content: " — DEPRECATED";
color: $warning;
font-weight: $font-weight-bold;
}
}
h1 {
display: inline-block;
font-size: 1.3rem;
.endpoint {
color: $secondary;
}
}
h2 {
font-weight: $font-weight-bold;
font-size: 1.3rem;
margin: 1.5rem 0 1rem 0;
}
h3 {
font-weight: $font-weight-bold;
font-size: 1.1rem;
margin: 1.5rem 0 1rem 0;
}
/* Reduce top margin of h3 if previous sibling is a h2 */
h2 + h3 {
margin-top: 1rem;
}
hr {
border-bottom: 2px solid $dark;
margin-bottom: 1.5rem;
}
p code, table code {
background-color: transparent;
}
table {
@media (max-width: 800px) {
/* Docsy by default applies `overflow-x: auto;` to tables, which
* results in annoying horizontal scrolling on mobile, so we instead
* switch to a fixed table layout on a narrow browser width.
* (On a wider width the default auto table-layout provides better readability.)
*
* Docsy makes all tables "responsive tables", which causes Bootstrap 4 to create
* tables with a "display" property of "block".
* However, for "table-layout: fixed" to be effective, an element must have a
* "display" property of "table".
*
* Thus, we override the "display" property here. This may no longer be necessary once
* Docsy updates to Bootstrap v5+: https://github.com/google/docsy/issues/470.
* For more details, see
* https://github.com/matrix-org/matrix-spec/pull/1295/files#r1010759688 */
display: table;
table-layout: fixed;
width: 100%;
.col-name, .col-type, .col-status {
width: 25%;
}
.col-description {
width: 50%;
}
.col-status-description {
width: 75%;
}
}
caption {
caption-side: top;
color: $dark;
font-size: 1rem;
font-weight: $font-weight-bold;
}
th, td, caption {
padding: 1rem;
border-top: 1px $table-border-color solid;
}
td > p:last-child {
// Avoid unnecessary space at the bottom of the cells.
margin-bottom: 0;
}
&.object-table, &.response-table, &.content-type-table {
border: 1px $table-border-color solid;
caption {
// the caption is outside the table's border box,
// so we have to give it its own border.
border: 1px $table-border-color solid;
// ... but avoid double border between caption and table
border-bottom: 0;
background-color: $secondary-lighter-background;
}
tbody tr {
--bs-table-striped-bg: #{$secondary-lighter-background};
}
}
&.basic-info, &.basic-info th, &.basic-info td {
table-layout: fixed;
margin: 1rem 0 .5rem 0;
}
&.basic-info th {
width: 15rem;
}
/* Arrange rows vertically when horizontal space is constrained to avoid overflowing */
@include media-breakpoint-down(sm) {
/* Make cells full width without vertical margin */
&.basic-info th, &.basic-info td {
width: 100%;
display: inline-block;
margin-top: 0;
margin-bottom: 0;
}
/* Remove border and padding between header & data cells to make them appear like a single cell */
&.basic-info td {
padding-top: 0;
border-top: none;
}
&.basic-info th {
border-bottom: none;
}
/* Remove top border on all but the first header cell to prevent double borders between rows */
&.basic-info tr + tr th {
border-top: none;
}
}
}
/* Have consistent spacing around tables and examples */
table, .highlight {
margin-top: 0;
margin-bottom: 2rem;
/* We don't need the margin on the last child of the .rendered-data block */
&:last-child {
margin-bottom: 0;
}
}
pre {
border: 0;
border-left: solid 5px $secondary;
}
.http-api-method {
font-weight: $font-weight-bold;
}
}
/* Miscellaneous custom bits */
/* Update link colours for MAtrix style */
a, a:hover {
color: $secondary;
}
/* This is needed to stop the bottom of the Matrix icon from getting snipped off. */
.td-navbar .navbar-brand svg {
height: 32px;
}
/* Give code samples and pre elements full-width */
.td-content > .highlight, .td-content > pre {
max-width: 100%;
}
/* The default CSS applies a style for blockquotes but only to immediate children
of .td-content. This applies the same style to any blockquotes that descend from
.td-content. */
.td-content blockquote {
padding: 0 0 0 1rem;
margin-bottom: $spacer;
color: $gray-600;
border-left: 6px solid $secondary;
}
/*
Make padding symmetrical (this selector is used in the default styles to apply padding-left: 3rem)
*/
.pl-md-5, .px-md-5 {
@include media-breakpoint-up(md) {
padding-right: 3rem;
}
}
/* Adjust the width of math to match normal paragraphs */
@include media-breakpoint-up(lg) {
.katex-display {
max-width: 80%;
}
}
/* Adjust default styles for info banner */
.pageinfo-primary {
@include media-breakpoint-up(lg) {
max-width: 80%;
}
margin-top: 0;
margin-right: 0;
margin-left: 0;
border: 0;
border-left: solid 5px $secondary;
background-color: $gray-100;
}
.pageinfo-unstable {
background-image: url('../icons/unstable.png');
background-position: left 1rem center;
background-repeat: no-repeat;
padding-left: 100px;
}
/* Adjust the styling of definition lists. */
/* Add a little spacing between the term and its definition. */
dt {
margin-bottom: 0.15rem;
}
/* _reboot.scss deliberately sets margin-left to 0. Undo this. */
dd {
margin-left: 2rem;
}
/* docsy's _code.scss does only styles <code> elements matching
* .td-content { p code, li > code, table code }.
* Copy those styles here to apply to code <elements> in definition terms too. */
.td-content {
dt code {
color: inherit;
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
word-break: normal;
background-color: rgba($black, 0.05);
border-radius: 0.25rem; // was $border-radius, but that var isn't accessible here.
}
}
/* Style for breadcrumbs */
.td-breadcrumbs {
padding: .75rem 1rem;
background-color: #eee;
border-radius: .25rem;
margin-bottom: 1rem;
.breadcrumb {
margin: 0;
}
}

View file

@ -1,56 +0,0 @@
/*
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
$primary: #FFF;
$secondary: #0098D4;
$dark: #333;
$gray-100: #FBFBFB;
$secondary-background: #E5F5FB;
$secondary-lighter-background: #F4FAFC;
$secondary-lightest-background: #FBFDFD;
$warning: #FF6666;
$note: $secondary;
$note-background: $secondary-background;
$warning-background: #FFE0E0;
// colours for definition tables.
// the border colour matches that used for "highlight" divs
$table-border-color: rgba(black, .125);
$table-bg: $secondary-lightest-background;
/* Configure docsy to use the default system fonts instead of Google Fonts.
* See https://www.docsy.dev/docs/adding-content/lookandfeel/#fonts */
$td-enable-google-fonts: false;
/*
* The $font-family-sans-serif definition here overrides the default value set by docsy
* (https://github.com/matrix-org/docsy/blob/66a4e61d2d34edc7196b9df83a7d09cd4af14b47/assets/scss/_variables.scss#L68)
*/
$font-family-sans-serif:
// Add "Inter" to the front, making it the default. The font itself is loaded via stylesheet
// links in layouts/partials/hooks/head-end.html.
"Inter",
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
// Insert fonts suited for mathematical symbols on different platforms before "Arial"
"STIX Two Math", "Cambria Math", "Noto Sans Math", "Dejavu Sans",
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
// Disable smooth scrolling as it makes TOC highlighting jump during the transition.
$enable-smooth-scroll: false;

View file

@ -1,82 +0,0 @@
/* Background */ .chroma { background-color: #f8f8f8 }
/* Other */ .chroma .x { color: #000000 }
/* Error */ .chroma .err { color: #a40000 }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* Keyword */ .chroma .k { color: #204a87; font-weight: bold }
/* KeywordConstant */ .chroma .kc { color: #204a87; font-weight: bold }
/* KeywordDeclaration */ .chroma .kd { color: #204a87; font-weight: bold }
/* KeywordNamespace */ .chroma .kn { color: #204a87; font-weight: bold }
/* KeywordPseudo */ .chroma .kp { color: #204a87; font-weight: bold }
/* KeywordReserved */ .chroma .kr { color: #204a87; font-weight: bold }
/* KeywordType */ .chroma .kt { color: #204a87; font-weight: bold }
/* Name */ .chroma .n { color: #000000 }
/* NameAttribute */ .chroma .na { color: #c4a000 }
/* NameBuiltin */ .chroma .nb { color: #204a87 }
/* NameBuiltinPseudo */ .chroma .bp { color: #3465a4 }
/* NameClass */ .chroma .nc { color: #000000 }
/* NameConstant */ .chroma .no { color: #000000 }
/* NameDecorator */ .chroma .nd { color: #5c35cc; font-weight: bold }
/* NameEntity */ .chroma .ni { color: #ce5c00 }
/* NameException */ .chroma .ne { color: #cc0000; font-weight: bold }
/* NameFunction */ .chroma .nf { color: #000000 }
/* NameFunctionMagic */ .chroma .fm { color: #000000 }
/* NameLabel */ .chroma .nl { color: #f57900 }
/* NameNamespace */ .chroma .nn { color: #000000 }
/* NameOther */ .chroma .nx { color: #000000 }
/* NameProperty */ .chroma .py { color: #000000 }
/* NameTag */ .chroma .nt { color: #204a87; font-weight: bold }
/* NameVariable */ .chroma .nv { color: #000000 }
/* NameVariableClass */ .chroma .vc { color: #000000 }
/* NameVariableGlobal */ .chroma .vg { color: #000000 }
/* NameVariableInstance */ .chroma .vi { color: #000000 }
/* NameVariableMagic */ .chroma .vm { color: #000000 }
/* Literal */ .chroma .l { color: #000000 }
/* LiteralDate */ .chroma .ld { color: #000000 }
/* LiteralString */ .chroma .s { color: #4e9a06 }
/* LiteralStringAffix */ .chroma .sa { color: #4e9a06 }
/* LiteralStringBacktick */ .chroma .sb { color: #4e9a06 }
/* LiteralStringChar */ .chroma .sc { color: #4e9a06 }
/* LiteralStringDelimiter */ .chroma .dl { color: #4e9a06 }
/* LiteralStringDoc */ .chroma .sd { color: #8f5902; font-style: italic }
/* LiteralStringDouble */ .chroma .s2 { color: #4e9a06 }
/* LiteralStringEscape */ .chroma .se { color: #4e9a06 }
/* LiteralStringHeredoc */ .chroma .sh { color: #4e9a06 }
/* LiteralStringInterpol */ .chroma .si { color: #4e9a06 }
/* LiteralStringOther */ .chroma .sx { color: #4e9a06 }
/* LiteralStringRegex */ .chroma .sr { color: #4e9a06 }
/* LiteralStringSingle */ .chroma .s1 { color: #4e9a06 }
/* LiteralStringSymbol */ .chroma .ss { color: #4e9a06 }
/* LiteralNumber */ .chroma .m { color: #0000cf; font-weight: bold }
/* LiteralNumberBin */ .chroma .mb { color: #0000cf; font-weight: bold }
/* LiteralNumberFloat */ .chroma .mf { color: #0000cf; font-weight: bold }
/* LiteralNumberHex */ .chroma .mh { color: #0000cf; font-weight: bold }
/* LiteralNumberInteger */ .chroma .mi { color: #0000cf; font-weight: bold }
/* LiteralNumberIntegerLong */ .chroma .il { color: #0000cf; font-weight: bold }
/* LiteralNumberOct */ .chroma .mo { color: #0000cf; font-weight: bold }
/* Operator */ .chroma .o { color: #ce5c00; font-weight: bold }
/* OperatorWord */ .chroma .ow { color: #204a87; font-weight: bold }
/* Punctuation */ .chroma .p { color: #000000; font-weight: bold }
/* Comment */ .chroma .c { color: #8f5902; font-style: italic }
/* CommentHashbang */ .chroma .ch { color: #8f5902; font-style: italic }
/* CommentMultiline */ .chroma .cm { color: #8f5902; font-style: italic }
/* CommentSingle */ .chroma .c1 { color: #8f5902; font-style: italic }
/* CommentSpecial */ .chroma .cs { color: #8f5902; font-style: italic }
/* CommentPreproc */ .chroma .cp { color: #8f5902; font-style: italic }
/* CommentPreprocFile */ .chroma .cpf { color: #8f5902; font-style: italic }
/* Generic */ .chroma .g { color: #000000 }
/* GenericDeleted */ .chroma .gd { color: #a40000 }
/* GenericEmph */ .chroma .ge { color: #000000; font-style: italic }
/* GenericError */ .chroma .gr { color: #ef2929 }
/* GenericHeading */ .chroma .gh { color: #000080; font-weight: bold }
/* GenericInserted */ .chroma .gi { color: #00a000 }
/* GenericOutput */ .chroma .go { color: #000000; font-style: italic }
/* GenericPrompt */ .chroma .gp { color: #8f5902 }
/* GenericStrong */ .chroma .gs { color: #000000; font-weight: bold }
/* GenericSubheading */ .chroma .gu { color: #800080; font-weight: bold }
/* GenericTraceback */ .chroma .gt { color: #a40000; font-weight: bold }
/* GenericUnderline */ .chroma .gl { color: #000000; text-decoration: underline }
/* TextWhitespace */ .chroma .w { color: #f8f8f8; text-decoration: underline }

View file

@ -1,133 +0,0 @@
Abstract
========
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
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 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.
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 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).
Proposal
========
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:
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/
.. _exemplar characters: http://cldr.unicode.org/translation/characters#TOC-Exemplar-Characters
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/
.. _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:example.org (Cyrillic 'a', everything else English)
@@xn--eby-7cd:example.org (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:example.org``.
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.
- 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.
- 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. 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:example.org`` using
``@Bob:example.org``. It is up to ``example.org`` to map ``Bob`` to ``bob`` in
a sensible way.

View file

@ -1,76 +0,0 @@
(a stream of incoherent consciousness from matthew which should go somewhere)
Invite-graph based reputation data:
* Users need a reputation score to issue invites or join public rooms.
* A user can have many reputation scores in different audiences (and perhaps a global average?)
* A room (degenerate case: user) can align itself with a given audience in order to consume the reputation data for that audience.
* The people that a user invites inherits a proportion of their reputation.
* If your reputation in an audience is ever reduced, it similarly reduces the reputation you have ever conveyed to anyone else (which propagates through the invite graph).
* Users increase reputation by:
* Inviting someone.
* Upvoting their messages in a room (i.e. for the suitability of that audience)
* Users decrease reputation by:
* Blocking them.
* Downvoting their messages in a room (i.e. for the suitability of that audience)
Need to ensure the accounts are of a decent quality - making it harder to create sockpuppet accounts and associating them with real people is more important than the actual reputation problem.
Build a war game simulation to test?
Problems:
* How are audiences defined? Just a given unique set of users? Which then makes inheriting reputation easy between audiences - if the overlap is significant, the chances are the reputation rules are the same.
* But is it possible to have the same set of users in two different rooms have different rules for reputation? Probably yes, as the potential audience may include future invitees or indeed the general public, so history visibility rules should probably also contribute to this. But given privacy rules can change over time, each room should effectively define its own audience. So in the end, an audience === a room.
* Create a large network of fake users, and go and have them all vote up each other's score for a given audience.
* This can be solved if the root inviter is penalised, which then destroys all the reputation they conveyed to their graph.
Could Reputation == Power Level (!?!?!)
Inheritence semantics for reputation between different audiences is hard.
* You should base the reputation of a stranger on their reputation in other communities that you or your communities have some overlap with.
* Do you consider 2nd hand reputation data at all from private rooms? Or do you look only at the public reputation data?
How do you do these calculations in a byzantine world?
How do you do these calculations whilst preserving privacy?
* Only consider reputation data from rooms you are actually in?
* Store reputation data in room state?
* Have a function (HS? client? AS? spider?) that aggregates reputation data (and proves that the aggregation is accurate, almost like blockchain mining?)?
* Or have a separate reputation global db seperate from room state that people contribute metrics into (which gathers the aggregate data into a single place, and makes it easier to query reputation data for strangers)
How do you avoid backstabbing? (People maliciously ganging up on someone to downvote them)?
How do you avoid a voting war? (Community fragments; different factions turn up and try to downvote the other)?
* This is effectively two different audiences emerging in a single room.
* Perhaps this means we should model audiences separately from rooms.
* Perhaps audiences are literally ACL groups? And eventually, one might change the ACLs of a room to eject one of the groups?
* Or do you just synthesise audiences based on cliques of people who support each other? The act of upvoting someone is effectively aligning yourself as being part of the same audience?
So:
* Gather all public upvote/downvotes/invites/blocks in a global DB.
* Partition this into audiences based on who votes on who. Stuff which is read and not complained about could provide a small implicit approval? Although this makes it easy to flood content to boost your reputation, so bad idea.
* Partitioning algorithm could be quite subtle.
* You could end up with lots of small audiences (including invalid ones), and it's fairly unclear how they get aggregated into a single view. How should you treat a stranger who you have no audience-overlap with at all? Treat them as effectively having zero reputation from your perspective?
Problem:
* If the douchebag who invites spammers never says anything, how do you go vote on their reputation? Should there be some kind of back-propagation? Or is there explicitly a "this person invited a douchebag" downvote? Or hang on - how can they ever get reputation in the first place to invite their sockpuppets if they don't say anything (beyond the initial invite)?
* What if users simply don't talk in public? Is it right that we prevent them issuing invites just because they stick to private rooms? What about inviting people into those private rooms? I guess the point is that if these are public invites, then they need to have some kind of public reputation, or rely on out-of-band private invitation to establish trust?
* Are we rewarding people who don't change their habits? There's no time component considered here, and we punish people's entire history of invites and rep if they misbehave. The only way to escape is to create a new identity atm. Is this a feature or a bug?
* How does this handle people's accounts getting 0wn3d and doing things which wipe out their reputation? => This is always a risk; ignore it.
* Do you need a particular level of reputation to be able to vote on people?
Summary?
* Partition the global population into multiple overlapping clusters called 'audiences' based on mutual(?) upvote/downvote relationships in public rooms.
* Clusters of the same people but in different rooms could be modelled as separate (but overlapping) clusters.
* Each audience builds up a reputation score for the global population, blending in damped scores from overlapping audiences.
* Anyone can upvote/downvote, but the votes will not contribute to your personal opinion unless the voter overlaps with your audience's scoresheet.
* A room could adopt a given audience (that of the moderators'?) for considering the reputation of who can join, invite people, etc.
* A user uses their own 'audience of one' scoresheet to put a threshold on filtering out contact from other users (invites, messages, etc).
* Their personal scoresheet is presumably a blend of all the audiences they are already in.
* The act of inviting someone gives them some reputation, within your audiences, proportional to your own. Similarly blocking reduces reputation.
* If you are downvoted, it retrospectively reduces the weight of all of your upvote/downvotes (at least for audiences that the downvoter's opinion contributes to). Similarly for upvoting.
* This penalisation process is transitive.
Do we even need the penalisation stuff if audience partitioning works?

View file

@ -1,76 +0,0 @@
URL Previews
============
Design notes on a URL previewing service for Matrix:
Options are:
1. Have an AS which listens for URLs, downloads them, and inserts an event that describes their metadata.
* Pros:
* Decouples the implementation entirely from Synapse.
* Uses existing Matrix events & content repo to store the metadata.
* Cons:
* Which AS should provide this service for a room, and why should you trust it?
* Doesn't work well with E2E; you'd have to cut the AS into every room
* the AS would end up subscribing to every room anyway.
2. Have a generic preview API (nothing to do with Matrix) that provides a previewing service:
* Pros:
* Simple and flexible; can be used by any clients at any point
* Cons:
* If each HS provides one of these independently, all the HSes in a room may needlessly DoS the target URI
* We need somewhere to store the URL metadata rather than just using Matrix itself
* We can't piggyback on matrix to distribute the metadata between HSes.
3. Make the synapse of the sending user responsible for spidering the URL and inserting an event asynchronously which describes the metadata.
* Pros:
* Works transparently for all clients
* Piggy-backs nicely on using Matrix for distributing the metadata.
* No confusion as to which AS
* Cons:
* Doesn't work with E2E
* We might want to decouple the implementation of the spider from the HS, given spider behaviour can be quite complicated and evolve much more rapidly than the HS. It's more like a bot than a core part of the server.
4. Make the sending client use the preview API and insert the event itself when successful.
* Pros:
* Works well with E2E
* No custom server functionality
* Lets the client customise the preview that they send (like on FB)
* Cons:
* Entirely specific to the sending client, whereas it'd be nice if /any/ URL was correctly previewed if clients support it.
5. Have the option of specifying a shared (centralised) previewing service used by a room, to avoid all the different HSes in the room DoSing the target.
Best solution is probably a combination of both 2 and 4.
* Sending clients do their best to create and send a preview at the point of sending the message, perhaps delaying the message until the preview is computed? (This also lets the user validate the preview before sending)
* Receiving clients have the option of going and creating their own preview if one doesn't arrive soon enough (or if the original sender didn't create one)
This is a bit magical though in that the preview could come from two entirely different sources - the sending HS or your local one. However, this can always be exposed to users: "Generate your own URL previews if none are available?"
This is tantamount also to senders calculating their own thumbnails for sending in advance of the main content - we are trusting the sender not to lie about the content in the thumbnail. Whereas currently thumbnails are calculated by the receiving homeserver to avoid this attack.
However, this kind of phishing attack does exist whether we let senders pick their thumbnails or not, in that a malicious sender can send normal text messages around the attachment claiming it to be legitimate. We could rely on (future) reputation/abuse management to punish users who phish (be it with bogus metadata or bogus descriptions). Bogus metadata is particularly bad though, especially if it's avoidable.
As a first cut, let's do #2 and have the receiver hit the API to calculate its own previews (as it does currently for image thumbnails). We can then extend/optimise this to option 4 as a special extra if needed.
API
---
```
GET /_matrix/media/r0/preview_url?url=http://wherever.com
200 OK
{
"og:type" : "article"
"og:url" : "https://twitter.com/matrixdotorg/status/684074366691356672"
"og:title" : "Matrix on Twitter"
"og:image" : "https://pbs.twimg.com/profile_images/500400952029888512/yI0qtFi7_400x400.png"
"og:description" : "“Synapse 0.12 is out! Lots of polishing, performance &amp;amp; bugfixes: /sync API, /r0 prefix, fulltext search, 3PID invites https://t.co/5alhXLLEGP”"
"og:site_name" : "Twitter"
}
```
* Downloads the URL
* If HTML, just stores it in RAM and parses it for OG meta tags
* Download any media OG meta tags to the media repo, and refer to them in the OG via mxc:// URIs.
* If a media filetype we know we can thumbnail: store it on disk, and hand it to the thumbnailer. Generate OG meta tags from the thumbnailer contents.
* Otherwise, don't bother downloading further.

View file

@ -1,288 +0,0 @@
WebSockets API
==============
Introduction
------------
This document is a proposal for a WebSockets-based client-server API. It is not
intended to replace the REST API, but rather to complement it and provide an
alternative interface for certain operations.
The primary goal is to offer a more efficient interface than the REST API: by
using a bidirectional protocol such as WebSockets we can avoid the overheads
involved in long-polling (SSL negotiation, HTTP headers, etc). In doing so we
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
``/_matrix/client/rN/stream``, passing the query parameters ``access_token``
and ``since``, and optionally ``filter`` - all of which have the same
meaning as for ``/sync``.
* The client sets the ``Sec-WebSocket-Protocol`` to ``m.json``. (Servers may
offer alternative encodings; at present only the JSON encoding is
specified but in future we will specify alternative encodings.)
#. The server returns the websocket handshake; the socket is then connected.
If the server does not return a valid websocket handshake, this indicates that
the server or an intermediate proxy does not support WebSockets. In this case,
the client should fall back to polling the ``/sync`` REST endpoint.
Example
~~~~~~~
Client request:
.. code:: http
GET /_matrix/client/v2_alpha/stream?access_token=123456&since=s72594_4483_1934 HTTP/1.1
Host: matrix.org
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: m.json
Sec-WebSocket-Version: 13
Origin: https://matrix.org
Server response:
.. code:: http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: m.json
Update Notifications
--------------------
Once the socket is connected, the server begins streaming updates over the
websocket. The server sends Update notifications about new messages or state
changes. To make it easy for clients to parse, Update notifications have the
same structure as the response to ``/sync``: an object with the following
members:
============= ========== ===================================================
Key Type Description
============= ========== ===================================================
next_batch string The batch token to supply in the ``since`` param of
the next /sync request. This is not required for
streaming of events over the WebSocket, but is
provided so that clients can reconnect if the
socket is disconnected.
presence Presence The updates to the presence status of other users.
rooms Rooms Updates to rooms.
============= ========== ===================================================
Example
~~~~~~~
Message from the server:
.. code:: json
{
"next_batch": "s72595_4483_1934",
"presence": {
"events": []
},
"rooms": {
"join": {},
"invite": {},
"leave": {}
}
}
Client-initiated operations
---------------------------
The client can perform certain operations by sending a websocket message to
the server. Such a "Request" message should be a JSON-encoded object with
the following members:
============= ========== ===================================================
Key Type Description
============= ========== ===================================================
id string A unique identifier for this request
method string Specifies the name of the operation to be
performed; see below for available operations
param object The parameters for the requested operation.
============= ========== ===================================================
The server responds to a client Request with a Response message. This is a
JSON-encoded object with the following members:
============= ========== ===================================================
Key Type Description
============= ========== ===================================================
id string The same as the value in the corresponding Request
object. The presence of the ``id`` field
distinguishes a Response message from an Update
notification.
result object On success, the results of the request.
error object On error, an object giving the resons for the
error. This has the same structure as the "standard
error response" for the Matrix API: an object with
the fields ``errcode`` and ``error``.
============= ========== ===================================================
Request methods
~~~~~~~~~~~~~~~
It is not intended that all operations which are available via the REST API
will be available via the WebSockets API, but a few simple, common operations
will be exposed. The initial operations will be as follows.
``ping``
^^^^^^^^
This is a no-op which clients may use to keep their connection alive.
The request ``params`` and the response ``result`` should be empty.
``send``
^^^^^^^^
Send a message event to a room. The parameters are as follows:
============= ========== ===================================================
Parameter Type Description
============= ========== ===================================================
room_id string **Required.** The room to send the event to
event_type string **Required.** The type of event to send.
content object **Required.** The content of the event.
============= ========== ===================================================
The result is as follows:
============= ========== ===================================================
Key Type Description
============= ========== ===================================================
event_id string A unique identifier for the event.
============= ========== ===================================================
The ``id`` from the Request message is used as the transaction ID by the
server.
``state``
^^^^^^^^^
Update the state on a room.
============= ========== ===================================================
Parameter Type Description
============= ========== ===================================================
room_id string **Required.** The room to set the state in
event_type string **Required.** The type of event to send.
state_key string **Required.** The state_key for the state to send.
content object **Required.** The content of the event.
============= ========== ===================================================
The result is as follows:
============= ========== ===================================================
Key Type Description
============= ========== ===================================================
event_id string A unique identifier for the event.
============= ========== ===================================================
Example
~~~~~~~
Client request:
.. code:: json
{
"id": "12345",
"method": "send",
"params": {
"room_id": "!d41d8cd:matrix.org",
"event_type": "m.room.message",
"content": {
"msgtype": "m.text",
"body": "hello"
}
}
}
Server response:
.. code:: json
{
"id": "12345",
"result": {
"event_id": "$66697273743031:matrix.org"
}
}
Alternative server response, in case of error:
.. code:: json
{
"id": "12345",
"error": {
"errcode": "M_MISSING_PARAM",
"error": "Missing parameter: event_type"
}
}
Rationale
---------
Alternatives to WebSockets include HTTP/2, CoAP, and simply rolling our own
protocol over raw TCP sockets. However, the need to implement browser-based
clients essentially reduces our choice to WebSockets. HTTP/2 streams will
probably provide an interesting alternative in the future, but current browsers
do not appear to give javascript applications low-level access to the protocol.
Concerning the continued use of the JSON encoding: we prefer to focus on the
transition to WebSockets initially. Replacing JSON with a compact
representation such as CBOR, MessagePack, or even just compressed JSON will be
a likely extension for the future. The support for negotiation of subprotocols
within WebSockets should make this a simple transition once time permits.
The number of methods available for client requests is deliberately limited, as
each method requires code to be written to map it onto the equivalent REST
implementation. Some REST methods - for instance, user registration and login -
would be pointless to expose via WebSockets. It is likely, however, that we
will increate the number of methods available via the WebSockets API as it
becomes clear which would be most useful.
Open questions
--------------
Throttling
~~~~~~~~~~
At least in v2 sync, clients are inherently self-throttling - if they do not
poll quickly enough, events will be dropped from the next result. This proposal
raises the possibility that events will be produced more quickly than they can
be sent to the client; backlogs will build up on the server and/or in the
intermediate network, which will not only lead to high latency on events being
delivered, but will lead to responses to client requests also being delayed.
We may need to implement some sort of throttling mechanism by which the server
can start to drop events. The difficulty is in knowing when to start dropping
events. A few ideas:
* Use websocket pings to measure the RTT; if it starts to increase, start
dropping events. But this requires knowledge of the base RTT, and a useful
model of what constitutes an excessive increase.
* Have the client acknowledge each batch of events, and use a window to ensure
the number of outstanding batches is limited. This is annoying as it requires
the client to have to acknowledge batches - and it's not clear what the right
window size is: we want a big window for long fat networks (think of mobile
clients), but a small one for one with lower latency.
* Start dropping events if the server's TCP buffer starts filling up. This has
the advantage of delegating the congestion-detection to TCP (which already
has a number of algorithms to deal with it, to greater or lesser
effectiveness), but relies on homeservers being hosted on OSes which use
sensible TCP congestion-avoidance algorithms, and more critically, an ability
to read the fill level of the TCP send buffer.

View file

@ -1,17 +1,17 @@
Registration and Login
----------------------
Clients must register with a homeserver in order to use Matrix. After
Clients must register with a home server in order to use Matrix. After
registering, the client will be given an access token which must be used in ALL
requests to that homeserver as a query parameter 'access_token'.
requests to that home server as a query parameter 'access_token'.
If the client has already registered, they need to be able to login to their
account. The homeserver may provide many different ways of logging in, such as
account. The home server may provide many different ways of logging in, such as
user/password auth, login via a social network (OAuth2), login by confirming a
token sent to their email address, etc. This specification does not define how
homeservers should authorise their users who want to login to their existing
home servers should authorise their users who want to login to their existing
accounts, but instead defines the standard interface which implementations
should follow so that ANY client can login to ANY homeserver. Clients login
should follow so that ANY client can login to ANY home server. Clients login
using the |login|_ API. Clients register using the |register|_ API.
Registration follows the same general procedure as login, but the path requests
are sent to and the details contained in them are different.
@ -26,7 +26,7 @@ In order to determine up-front what the server's requirements are, the client
can request from the server a complete description of all of its acceptable
flows of the registration or login process. It can then inspect the list of
returned flows looking for one for which it believes it can complete all of the
required stages, and perform it. As each homeserver may have different ways of
required stages, and perform it. As each home server may have different ways of
logging in, the client needs to know how they should login. All distinct login
stages MUST have a corresponding ``type``. A ``type`` is a namespaced string
which details the mechanism for logging in.
@ -64,12 +64,12 @@ ID and a new access token MUST be returned::
"access_token": "abcdef0123456789"
}
The ``user_id`` key is particularly useful if the homeserver wishes to support
The ``user_id`` key is particularly useful if the home server wishes to support
localpart entry of usernames (e.g. "user" rather than "@user:matrix.org"), as
the client may not be able to determine its ``user_id`` in this case.
If the flow has multiple stages to it, the homeserver may wish to create a
session to store context between requests. If a homeserver responds with a
If the flow has multiple stages to it, the home server may wish to create a
session to store context between requests. If a home server responds with a
``session`` key to a request, clients MUST submit it in subsequent requests
until the flow is completed::
@ -99,7 +99,7 @@ To respond to this type, reply with::
"password": "<password>"
}
The homeserver MUST respond with either new credentials, the next stage of the
The home server MUST respond with either new credentials, the next stage of the
login process, or a standard error response.
Captcha-based
@ -123,7 +123,7 @@ To respond to this type, reply with::
Recaptcha.get_challenge();
Recaptcha.get_response();
The homeserver MUST respond with either new credentials, the next stage of the
The home server MUST respond with either new credentials, the next stage of the
login process, or a standard error response.
OAuth2-based
@ -146,24 +146,24 @@ The server MUST respond with::
"uri": <Authorization Request URI OR service selection URI>
}
The homeserver acts as a 'confidential' client for the purposes of OAuth2. If
The home server acts as a 'confidential' client for the purposes of OAuth2. If
the uri is a ``sevice selection URI``, it MUST point to a webpage which prompts
the user to choose which service to authorize with. On selection of a service,
this MUST link through to an ``Authorization Request URI``. If there is only 1
service which the homeserver accepts when logging in, this indirection can be
service which the home server accepts when logging in, this indirection can be
skipped and the "uri" key can be the ``Authorization Request URI``.
The client then visits the ``Authorization Request URI``, which then shows the
OAuth2 Allow/Deny prompt. Hitting 'Allow' returns the ``redirect URI`` with the
auth code. Homeservers can choose any path for the ``redirect URI``. The
auth code. Home servers can choose any path for the ``redirect URI``. The
client should visit the ``redirect URI``, which will then finish the OAuth2
login process, granting the homeserver an access token for the chosen service.
When the homeserver gets this access token, it verifies that the cilent has
login process, granting the home server an access token for the chosen service.
When the home server gets this access token, it verifies that the cilent has
authorised with the 3rd party, and can now complete the login. The OAuth2
``redirect URI`` (with auth code) MUST respond with either new credentials, the
next stage of the login process, or a standard error response.
For example, if a homeserver accepts OAuth2 from Google, it would return the
For example, if a home server accepts OAuth2 from Google, it would return the
Authorization Request URI for Google::
{
@ -171,7 +171,7 @@ Authorization Request URI for Google::
client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos"
}
The client then visits this URI and authorizes the homeserver. The client then
The client then visits this URI and authorizes the home server. The client then
visits the REDIRECT_URI with the auth code= query parameter which returns::
{
@ -195,7 +195,7 @@ To respond to this type, reply with::
"email": "<email address>"
}
After validating the email address, the homeserver MUST send an email
After validating the email address, the home server MUST send an email
containing an authentication code and return::
{
@ -212,7 +212,7 @@ code::
"code": "<code in email sent>"
}
The homeserver MUST respond to this with either new credentials, the next
The home server MUST respond to this with either new credentials, the next
stage of the login process, or a standard error response.
Email-based (url)
@ -231,7 +231,7 @@ To respond to this type, reply with::
"email": "<email address>"
}
After validating the email address, the homeserver MUST send an email
After validating the email address, the home server MUST send an email
containing an authentication URL and return::
{
@ -247,7 +247,7 @@ client should perform another request::
"session": "<session id>"
}
The homeserver MUST respond to this with either new credentials, the next
The home server MUST respond to this with either new credentials, the next
stage of the login process, or a standard error response.
A common client implementation will be to periodically poll until the link is
@ -264,7 +264,7 @@ Email-based (identity server)
Prior to submitting this, the client should authenticate with an identity
server. After authenticating, the session information should be submitted to
the homeserver.
the home server.
To respond to this type, reply with::
@ -293,7 +293,7 @@ of a previous login stage::
"next": "<next login type>"
}
If a homeserver implements N-factor authentication, it MUST respond with all
If a home server implements N-factor authentication, it MUST respond with all
``stages`` when initially queried for their login requirements::
{

View file

@ -1,15 +0,0 @@
# 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.
## Generating the changelog
Please see the [release docs](../meta/releasing.md) for more information.
## Creating a new changelog
There are a few places you'll have to update:
* `/layouts/shortcodes/changelog/changelog-changes.html` to account for the new changelog.
* `/scripts/generate-changelog.sh` to render the changelog for releases.
* Supporting documentation such as the contributing guidelines.

View file

@ -1 +0,0 @@
!.gitignore

View file

@ -1 +0,0 @@
!.gitignore

View file

@ -1 +0,0 @@
!.gitignore

View file

@ -1 +0,0 @@
!.gitignore

View file

@ -1 +0,0 @@
Replace the Twitter link in the footer with our BlueSky and Mastodon socials.

View file

@ -1,26 +0,0 @@
r0.1.2
======
Spec Clarifications
-------------------
- Clearer wording for the legacy routes section. (`#2160 <https://github.com/matrix-org/matrix-doc/issues/2160>`_)
r0.1.1
======
Spec Clarifications
-------------------
- Change examples to use example.org instead of a real domain. (`#1650 <https://github.com/matrix-org/matrix-doc/issues/1650>`_)
- Add missing definition for how appservices verify requests came from a homeserver. (`#2037 <https://github.com/matrix-org/matrix-doc/issues/2037>`_)
r0.1.0
======
This is the first release of the Application Service specification. It
includes support for application services being able to interact with
homeservers and bridge third-party networks, such as IRC, over to Matrix
in a standard and accessible way.

View file

@ -1,575 +0,0 @@
r0.6.1
======
New Endpoints
-------------
- Added ``/rooms/{roomId}/aliases`` for retrieving local aliases for a room. (`#2562 <https://github.com/matrix-org/matrix-doc/issues/2562>`_)
Backwards Compatible Changes
----------------------------
- Added data structures for defining moderation policies in rooms per `MSC2313 <https://github.com/matrix-org/matrix-doc/pull/2313>`_. (`#2434 <https://github.com/matrix-org/matrix-doc/issues/2434>`_)
- Optionally invalidate other access tokens during password modification per `MSC2457 <https://github.com/matrix-org/matrix-doc/pull/2457>`_. (`#2523 <https://github.com/matrix-org/matrix-doc/issues/2523>`_)
- Add User-Interactive Authentication for SSO-backed homeserver per `MSC2454 <https://github.com/matrix-org/matrix-doc/pull/2454>`_. (`#2532 <https://github.com/matrix-org/matrix-doc/issues/2532>`_)
- Add soft-logout support per `MSC1466 <https://github.com/matrix-org/matrix-doc/issues/1466>`_. (`#2546 <https://github.com/matrix-org/matrix-doc/issues/2546>`_)
- Replaced legacy room alias handling with a more sustainable solution per `MSC2432 <https://github.com/matrix-org/matrix-doc/pull/2432>`_. (`#2562 <https://github.com/matrix-org/matrix-doc/issues/2562>`_)
Spec Clarifications
-------------------
- List available enum values for the room versions capability. (`#2245 <https://github.com/matrix-org/matrix-doc/issues/2245>`_)
- Fix various spelling errors throughout the specification. (`#2351 <https://github.com/matrix-org/matrix-doc/issues/2351>`_, `#2415 <https://github.com/matrix-org/matrix-doc/issues/2415>`_, `#2453 <https://github.com/matrix-org/matrix-doc/issues/2453>`_, `#2524 <https://github.com/matrix-org/matrix-doc/issues/2524>`_, `#2553 <https://github.com/matrix-org/matrix-doc/issues/2553>`_, `#2569 <https://github.com/matrix-org/matrix-doc/issues/2569>`_)
- Minor clarifications to token-based User-Interactive Authentication. (`#2369 <https://github.com/matrix-org/matrix-doc/issues/2369>`_)
- Minor clarification for what the user directory searches. (`#2381 <https://github.com/matrix-org/matrix-doc/issues/2381>`_)
- Fix key export format example to match the specification. (`#2430 <https://github.com/matrix-org/matrix-doc/issues/2430>`_)
- Clarify the IV data type for encrypted files. (`#2492 <https://github.com/matrix-org/matrix-doc/issues/2492>`_)
- Fix the ``.m.rule.contains_user_name`` default push rule to set the highlight tweak. (`#2519 <https://github.com/matrix-org/matrix-doc/issues/2519>`_)
- Clarify that an ``event_id`` is returned when sending events. (`#2525 <https://github.com/matrix-org/matrix-doc/issues/2525>`_)
- Fix some numbers in the specification to match their explanation text. (`#2554 <https://github.com/matrix-org/matrix-doc/issues/2554>`_)
- Move redaction algorithm into the room version specifications. (`#2563 <https://github.com/matrix-org/matrix-doc/issues/2563>`_)
- Clarify signature object structures for encryption. (`#2566 <https://github.com/matrix-org/matrix-doc/issues/2566>`_)
- Clarify which events are created as part of ``/createRoom``. (`#2571 <https://github.com/matrix-org/matrix-doc/issues/2571>`_)
- Remove claims that the homeserver is exclusively responsible for profile information in membership events. (`#2574 <https://github.com/matrix-org/matrix-doc/issues/2574>`_)
r0.6.0
======
Breaking Changes
----------------
- Add ``id_access_token`` as a required request parameter to a few endpoints which require an ``id_server`` parameter as part of `MSC2140 <https://github.com/matrix-org/matrix-doc/pull/2140>`_. (`#2255 <https://github.com/matrix-org/matrix-doc/issues/2255>`_)
New Endpoints
-------------
- Add ``POST /account/3pid/unbind`` for removing a 3PID from an identity server. (`#2282 <https://github.com/matrix-org/matrix-doc/issues/2282>`_)
Backwards Compatible Changes
----------------------------
- Add ``M_USER_DEACTIVATED`` error code. (`#2234 <https://github.com/matrix-org/matrix-doc/issues/2234>`_)
- Remove ``bind_msisdn`` and ``bind_email`` from ``/register`` now that the identity server's bind endpoint requires authentication. (`#2279 <https://github.com/matrix-org/matrix-doc/issues/2279>`_)
- Add ``m.identity_server`` account data for tracking the user's preferred identity server. (`#2281 <https://github.com/matrix-org/matrix-doc/issues/2281>`_)
- Deprecate ``id_server`` and make it optional in several places. (`#2310 <https://github.com/matrix-org/matrix-doc/issues/2310>`_)
Spec Clarifications
-------------------
- Add missing format fields to ``m.room.message$m.notice`` schema. (`#2125 <https://github.com/matrix-org/matrix-doc/issues/2125>`_)
- Remove "required" designation from the ``url`` field of certain ``m.room.message`` msgtypes. (`#2129 <https://github.com/matrix-org/matrix-doc/issues/2129>`_)
- Fix various typos throughout the specification. (`#2131 <https://github.com/matrix-org/matrix-doc/issues/2131>`_, `#2136 <https://github.com/matrix-org/matrix-doc/issues/2136>`_, `#2148 <https://github.com/matrix-org/matrix-doc/issues/2148>`_, `#2215 <https://github.com/matrix-org/matrix-doc/issues/2215>`_)
- Clarify the distinction between ``m.key.verification.start`` and its ``m.sas.v1`` variant. (`#2132 <https://github.com/matrix-org/matrix-doc/issues/2132>`_)
- Fix link to Olm signing specification. (`#2133 <https://github.com/matrix-org/matrix-doc/issues/2133>`_)
- Clarify the conditions for the ``.m.rule.room_one_to_one`` push rule. (`#2152 <https://github.com/matrix-org/matrix-doc/issues/2152>`_)
- Clarify the encryption algorithms supported by the device of the device keys example. (`#2157 <https://github.com/matrix-org/matrix-doc/issues/2157>`_)
- Clarify that ``/rooms/:roomId/event/:eventId`` returns a Matrix error. (`#2204 <https://github.com/matrix-org/matrix-doc/issues/2204>`_)
- Add a missing ``state_key`` check on ``.m.rule.tombstone``. (`#2223 <https://github.com/matrix-org/matrix-doc/issues/2223>`_)
- Fix the ``m.room_key_request`` ``action`` value, setting it from ``cancel_request`` to ``request_cancellation``. (`#2247 <https://github.com/matrix-org/matrix-doc/issues/2247>`_)
- Clarify that the ``submit_url`` field is without authentication. (`#2341 <https://github.com/matrix-org/matrix-doc/issues/2341>`_)
- Clarify the expected phone number format. (`#2342 <https://github.com/matrix-org/matrix-doc/issues/2342>`_)
- Clarify that clients should consider not requesting URL previews in encrypted rooms. (`#2343 <https://github.com/matrix-org/matrix-doc/issues/2343>`_)
- Add missing information on how filters are meant to work with ``/context``. (`#2344 <https://github.com/matrix-org/matrix-doc/issues/2344>`_)
- Clarify what the keys are for rooms in ``/sync``. (`#2345 <https://github.com/matrix-org/matrix-doc/issues/2345>`_)
r0.5.0
======
Breaking Changes
----------------
- Add a new ``submit_url`` field to the responses of ``/requestToken`` which older clients will not be able to handle correctly. (`#2101 <https://github.com/matrix-org/matrix-doc/issues/2101>`_)
Deprecations
------------
- Remove references to presence lists. (`#1817 <https://github.com/matrix-org/matrix-doc/issues/1817>`_)
New Endpoints
-------------
- ``GET /account_data`` routes. (`#1873 <https://github.com/matrix-org/matrix-doc/issues/1873>`_)
Backwards Compatible Changes
----------------------------
- Add megolm session export format. (`#1701 <https://github.com/matrix-org/matrix-doc/issues/1701>`_)
- Add support for advertising experimental features to clients. (`#1786 <https://github.com/matrix-org/matrix-doc/issues/1786>`_)
- Add a generic SSO login API. (`#1789 <https://github.com/matrix-org/matrix-doc/issues/1789>`_)
- Add a mechanism for servers to redirect clients to an alternative homeserver after logging in. (`#1790 <https://github.com/matrix-org/matrix-doc/issues/1790>`_)
- Add room version upgrades. (`#1791 <https://github.com/matrix-org/matrix-doc/issues/1791>`_, `#1875 <https://github.com/matrix-org/matrix-doc/issues/1875>`_)
- Support optional features by having clients query for capabilities. (`#1829 <https://github.com/matrix-org/matrix-doc/issues/1829>`_, `#1879 <https://github.com/matrix-org/matrix-doc/issues/1879>`_)
- Add ``M_RESOURCE_LIMIT_EXCEEDED`` as an error code for when homeservers exceed limits imposed on them. (`#1874 <https://github.com/matrix-org/matrix-doc/issues/1874>`_)
- Emit ``M_UNSUPPORTED_ROOM_VERSION`` error codes where applicable on ``/createRoom`` and ``/invite`` APIs. (`#1908 <https://github.com/matrix-org/matrix-doc/issues/1908>`_)
- Add a ``.m.rule.tombstone`` default push rule for room upgrade notifications. (`#2020 <https://github.com/matrix-org/matrix-doc/issues/2020>`_)
- Add support for sending server notices to clients. (`#2026 <https://github.com/matrix-org/matrix-doc/issues/2026>`_)
- Add MSISDN (phone number) support to User-Interactive Authentication. (`#2030 <https://github.com/matrix-org/matrix-doc/issues/2030>`_)
- Add the option to lazy-load room members for increased client performance. (`#2035 <https://github.com/matrix-org/matrix-doc/issues/2035>`_)
- Add ``id_server`` to ``/deactivate`` and ``/3pid/delete`` endpoints to unbind from a specific identity server. (`#2046 <https://github.com/matrix-org/matrix-doc/issues/2046>`_)
- Add support for Olm sessions becoming un-stuck. (`#2059 <https://github.com/matrix-org/matrix-doc/issues/2059>`_)
- Add interactive device verification, including a common framework for device verification. (`#2072 <https://github.com/matrix-org/matrix-doc/issues/2072>`_)
Spec Clarifications
-------------------
- Change examples to use example.org instead of a real domain. (`#1650 <https://github.com/matrix-org/matrix-doc/issues/1650>`_)
- Clarify that ``state_default`` in ``m.room.power_levels`` always defaults to 50. (`#1656 <https://github.com/matrix-org/matrix-doc/issues/1656>`_)
- Add missing ``status_msg`` to ``m.presence`` schema. (`#1744 <https://github.com/matrix-org/matrix-doc/issues/1744>`_)
- Fix various spelling mistakes throughout the specification. (`#1838 <https://github.com/matrix-org/matrix-doc/issues/1838>`_, `#1853 <https://github.com/matrix-org/matrix-doc/issues/1853>`_, `#1860 <https://github.com/matrix-org/matrix-doc/issues/1860>`_, `#1933 <https://github.com/matrix-org/matrix-doc/issues/1933>`_, `#1969 <https://github.com/matrix-org/matrix-doc/issues/1969>`_, `#1988 <https://github.com/matrix-org/matrix-doc/issues/1988>`_, `#1989 <https://github.com/matrix-org/matrix-doc/issues/1989>`_, `#1991 <https://github.com/matrix-org/matrix-doc/issues/1991>`_, `#1992 <https://github.com/matrix-org/matrix-doc/issues/1992>`_)
- Add the missing ``m.push_rules`` event schema. (`#1889 <https://github.com/matrix-org/matrix-doc/issues/1889>`_)
- Clarify how modern day local echo is meant to be solved by clients. (`#1891 <https://github.com/matrix-org/matrix-doc/issues/1891>`_)
- Clarify that ``width`` and ``height`` are required parameters on ``/_matrix/media/r0/thumbnail/{serverName}/{mediaId}``. (`#1975 <https://github.com/matrix-org/matrix-doc/issues/1975>`_)
- Clarify how ``m.login.dummy`` can be used to disambiguate login flows. (`#1999 <https://github.com/matrix-org/matrix-doc/issues/1999>`_)
- Remove ``prev_content`` from the redaction algorithm's essential keys list. (`#2016 <https://github.com/matrix-org/matrix-doc/issues/2016>`_)
- Fix the ``third_party_signed`` definitions for the join APIs. (`#2025 <https://github.com/matrix-org/matrix-doc/issues/2025>`_)
- Clarify why User Interactive Auth is used on password changes and how access tokens are handled. (`#2027 <https://github.com/matrix-org/matrix-doc/issues/2027>`_)
- Clarify that devices are deleted upon logout. (`#2028 <https://github.com/matrix-org/matrix-doc/issues/2028>`_)
- Add ``M_NOT_FOUND`` error definition for deleting room aliases. (`#2029 <https://github.com/matrix-org/matrix-doc/issues/2029>`_)
- Add missing ``reason`` to ``m.call.hangup``. (`#2031 <https://github.com/matrix-org/matrix-doc/issues/2031>`_)
- Clarify how redactions affect room state. (`#2032 <https://github.com/matrix-org/matrix-doc/issues/2032>`_)
- Clarify that ``FAIL_ERROR`` in autodiscovery is not limited to just homeservers. (`#2036 <https://github.com/matrix-org/matrix-doc/issues/2036>`_)
- Fix example ``Content-Type`` for ``/media/upload`` request. (`#2041 <https://github.com/matrix-org/matrix-doc/issues/2041>`_)
- Clarify that login flows are meant to be completed in order. (`#2042 <https://github.com/matrix-org/matrix-doc/issues/2042>`_)
- Clarify that clients should not send read receipts for their own messages. (`#2043 <https://github.com/matrix-org/matrix-doc/issues/2043>`_)
- Use consistent examples of events throughout the specification. (`#2051 <https://github.com/matrix-org/matrix-doc/issues/2051>`_)
- Clarify which push rule condition kinds exist. (`#2052 <https://github.com/matrix-org/matrix-doc/issues/2052>`_)
- Clarify the required fields on ``m.file`` (and similar) messages. (`#2053 <https://github.com/matrix-org/matrix-doc/issues/2053>`_)
- Clarify that User-Interactive Authentication stages cannot be attempted more than once. (`#2054 <https://github.com/matrix-org/matrix-doc/issues/2054>`_)
- Clarify which parameters apply in what scenarios on ``/register``. (`#2055 <https://github.com/matrix-org/matrix-doc/issues/2055>`_)
- Clarify how to interpret changes of ``membership`` over time. (`#2056 <https://github.com/matrix-org/matrix-doc/issues/2056>`_)
- Clarify exactly what invite_room_state consists of. (`#2067 <https://github.com/matrix-org/matrix-doc/issues/2067>`_)
- Clarify how the content repository works, and what it is used for. (`#2068 <https://github.com/matrix-org/matrix-doc/issues/2068>`_)
- Clarify the order events in chunk are returned in for ``/messages``. (`#2069 <https://github.com/matrix-org/matrix-doc/issues/2069>`_)
- Clarify the key object definition for the key management API. (`#2083 <https://github.com/matrix-org/matrix-doc/issues/2083>`_)
- Reorganize information about events into a common section. (`#2087 <https://github.com/matrix-org/matrix-doc/issues/2087>`_)
- De-duplicate ``/state/<event_type>`` endpoints, clarifying that the ``<state_key>`` is optional. (`#2088 <https://github.com/matrix-org/matrix-doc/issues/2088>`_)
- Clarify when and where CORS headers should be returned. (`#2089 <https://github.com/matrix-org/matrix-doc/issues/2089>`_)
- Clarify when authorization and rate-limiting are not applicable. (`#2090 <https://github.com/matrix-org/matrix-doc/issues/2090>`_)
- Clarify that ``/register`` must produce valid Matrix User IDs. (`#2091 <https://github.com/matrix-org/matrix-doc/issues/2091>`_)
- Clarify how ``unread_notifications`` is calculated. (`#2097 <https://github.com/matrix-org/matrix-doc/issues/2097>`_)
- Clarify what a "module" is and update feature profiles for clients. (`#2098 <https://github.com/matrix-org/matrix-doc/issues/2098>`_)
r0.4.0
======
New Endpoints
-------------
- ``POST /user_directory/search`` (`#1096 <https://github.com/matrix-org/matrix-doc/issues/1096>`_)
- ``GET /rooms/{roomId}/event/{eventId}`` (`#1110 <https://github.com/matrix-org/matrix-doc/issues/1110>`_)
- ``POST /delete_devices`` (`#1239 <https://github.com/matrix-org/matrix-doc/issues/1239>`_)
- ``GET /thirdparty/*`` Endpoints (`#1353 <https://github.com/matrix-org/matrix-doc/issues/1353>`_)
- ``POST /account/3pid/msisdn/requestToken``, ``POST /register/msisdn/requestToken``, and ``POST /account/password/msisdn/requestToken`` (`#1507 <https://github.com/matrix-org/matrix-doc/issues/1507>`_)
- ``POST /account/3pid/delete`` (`#1567 <https://github.com/matrix-org/matrix-doc/issues/1567>`_)
- ``POST /rooms/{roomId}/read_markers`` (`#1635 <https://github.com/matrix-org/matrix-doc/issues/1635>`_)
Backwards Compatible Changes
----------------------------
- Add more presence options to the ``set_presence`` parameter of ``/sync``. (Thanks @mujx!) (`#780 <https://github.com/matrix-org/matrix-doc/issues/780>`_)
- Add ``token`` parameter to the ``/keys/query`` endpoint (`#1104 <https://github.com/matrix-org/matrix-doc/issues/1104>`_)
- Add the room visibility options for the room directory (`#1141 <https://github.com/matrix-org/matrix-doc/issues/1141>`_)
- Add spec for ignoring users (`#1142 <https://github.com/matrix-org/matrix-doc/issues/1142>`_)
- Add the ``/register/available`` endpoint for username availability (`#1151 <https://github.com/matrix-org/matrix-doc/issues/1151>`_)
- Add sticker messages (`#1158 <https://github.com/matrix-org/matrix-doc/issues/1158>`_)
- Specify how to control the power level required for ``@room`` (`#1176 <https://github.com/matrix-org/matrix-doc/issues/1176>`_)
- Document ``/logout/all`` endpoint (`#1263 <https://github.com/matrix-org/matrix-doc/issues/1263>`_)
- Add report content API (`#1264 <https://github.com/matrix-org/matrix-doc/issues/1264>`_)
- Add ``allow_remote`` to the content repo to avoid routing loops (`#1265 <https://github.com/matrix-org/matrix-doc/issues/1265>`_)
- Document `highlights` field in /search response (`#1274 <https://github.com/matrix-org/matrix-doc/issues/1274>`_)
- 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. (`#1284 <https://github.com/matrix-org/matrix-doc/issues/1284>`_)
- Add ``.well-known`` server discovery method (`#1359 <https://github.com/matrix-org/matrix-doc/issues/1359>`_)
- Document the GET version of ``/login`` (`#1361 <https://github.com/matrix-org/matrix-doc/issues/1361>`_)
- Document the ``server_name`` parameter on ``/join/{roomIdOrAlias}`` (`#1364 <https://github.com/matrix-org/matrix-doc/issues/1364>`_)
- Document the CORS/preflight headers (`#1365 <https://github.com/matrix-org/matrix-doc/issues/1365>`_)
- Add new user identifier object for logging in (`#1390 <https://github.com/matrix-org/matrix-doc/issues/1390>`_)
- Document message formats on ``m.text`` and ``m.emote`` messages (`#1397 <https://github.com/matrix-org/matrix-doc/issues/1397>`_)
- Encrypt file attachments (`#1420 <https://github.com/matrix-org/matrix-doc/issues/1420>`_)
- Share room decryption keys between devices (`#1465 <https://github.com/matrix-org/matrix-doc/issues/1465>`_)
- Document and improve client interaction with pushers. (`#1506 <https://github.com/matrix-org/matrix-doc/issues/1506>`_)
- Add support for Room Versions. (`#1516 <https://github.com/matrix-org/matrix-doc/issues/1516>`_)
- Guests can now call /context and /event to fetch events (`#1542 <https://github.com/matrix-org/matrix-doc/issues/1542>`_)
- Add a common standard for user, room, and group mentions in messages. (`#1547 <https://github.com/matrix-org/matrix-doc/issues/1547>`_)
- Add server ACLs as an option for controlling federation in a room. (`#1550 <https://github.com/matrix-org/matrix-doc/issues/1550>`_)
- Add new push rules for encrypted events and ``@room`` notifications. (`#1551 <https://github.com/matrix-org/matrix-doc/issues/1551>`_)
- Add third-party network room directories, as provided by application services. (`#1554 <https://github.com/matrix-org/matrix-doc/issues/1554>`_)
- Document the ``validated_at`` and ``added_at`` fields on ``GET /acount/3pid``. (`#1567 <https://github.com/matrix-org/matrix-doc/issues/1567>`_)
- Add an ``inhibit_login`` registration option. (`#1589 <https://github.com/matrix-org/matrix-doc/issues/1589>`_)
- Recommend that servers set a Content Security Policy for the content repository. (`#1600 <https://github.com/matrix-org/matrix-doc/issues/1600>`_)
- Add "rich replies" - a way for users to better represent the conversation thread they are referencing in their messages. (`#1617 <https://github.com/matrix-org/matrix-doc/issues/1617>`_)
- Add support for read markers. (`#1635 <https://github.com/matrix-org/matrix-doc/issues/1635>`_)
Spec Clarifications
-------------------
- Mark ``home_server`` return field for ``/login`` and ``/register`` endpoints as deprecated (`#1097 <https://github.com/matrix-org/matrix-doc/issues/1097>`_)
- Fix response format of ``/keys/changes`` endpoint (`#1106 <https://github.com/matrix-org/matrix-doc/issues/1106>`_)
- Clarify default values for some fields on the ``/search`` API (`#1109 <https://github.com/matrix-org/matrix-doc/issues/1109>`_)
- Fix the representation of ``m.presence`` events (`#1137 <https://github.com/matrix-org/matrix-doc/issues/1137>`_)
- Clarify that ``m.tag`` ordering is done with numbers, not strings (`#1139 <https://github.com/matrix-org/matrix-doc/issues/1139>`_)
- Clarify that ``/account/whoami`` should consider application services (`#1152 <https://github.com/matrix-org/matrix-doc/issues/1152>`_)
- 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. (`#1158 <https://github.com/matrix-org/matrix-doc/issues/1158>`_)
- Mark ``GET /rooms/{roomId}/members`` as requiring authentication (`#1245 <https://github.com/matrix-org/matrix-doc/issues/1245>`_)
- Clarify ``changed`` field behaviour in device tracking process (`#1284 <https://github.com/matrix-org/matrix-doc/issues/1284>`_)
- Describe ``StateEvent`` for ``/createRoom`` (`#1329 <https://github.com/matrix-org/matrix-doc/issues/1329>`_)
- Describe how the ``reason`` is handled for kicks/bans (`#1362 <https://github.com/matrix-org/matrix-doc/issues/1362>`_)
- Mark ``GET /presence/{userId}/status`` as requiring authentication (`#1371 <https://github.com/matrix-org/matrix-doc/issues/1371>`_)
- Describe the rate limit error response schema (`#1373 <https://github.com/matrix-org/matrix-doc/issues/1373>`_)
- Clarify that clients must leave rooms before forgetting them (`#1378 <https://github.com/matrix-org/matrix-doc/issues/1378>`_)
- Document guest access in ``/createRoom`` presets (`#1379 <https://github.com/matrix-org/matrix-doc/issues/1379>`_)
- Define what a ``RoomEvent`` is on ``/rooms/{roomId}/messages`` (`#1380 <https://github.com/matrix-org/matrix-doc/issues/1380>`_)
- Clarify the request and result types on ``/search`` (`#1381 <https://github.com/matrix-org/matrix-doc/issues/1381>`_)
- Clarify some of the properties on the search result (`#1400 <https://github.com/matrix-org/matrix-doc/issues/1400>`_)
- Clarify how access tokens are meant to be supplied to the homeserver. (`#1517 <https://github.com/matrix-org/matrix-doc/issues/1517>`_)
- Document additional parameters on the ``/createRoom`` API. (`#1518 <https://github.com/matrix-org/matrix-doc/issues/1518>`_)
- Clarify that new push rules should be enabled by default, and that unrecognised conditions should not match. (`#1551 <https://github.com/matrix-org/matrix-doc/issues/1551>`_)
- Update all event examples to be accurate representations of their associated events. (`#1558 <https://github.com/matrix-org/matrix-doc/issues/1558>`_)
- Clarify the supported HTML features for room messages. (`#1562 <https://github.com/matrix-org/matrix-doc/issues/1562>`_)
- Move the ``invite_room_state`` definition under ``unsigned`` where it actually resides. (`#1568 <https://github.com/matrix-org/matrix-doc/issues/1568>`_)
- Clarify the homeserver's behaviour for searching users. (`#1569 <https://github.com/matrix-org/matrix-doc/issues/1569>`_)
- Clarify the object structures and defaults for Filters. (`#1570 <https://github.com/matrix-org/matrix-doc/issues/1570>`_)
- Clarify instances of ``type: number`` in the swagger/OpenAPI schema definitions. (`#1571 <https://github.com/matrix-org/matrix-doc/issues/1571>`_)
- Clarify that left rooms also have account data in ``/sync``. (`#1572 <https://github.com/matrix-org/matrix-doc/issues/1572>`_)
- Clarify the event fields used in the ``/sync`` response. (`#1573 <https://github.com/matrix-org/matrix-doc/issues/1573>`_)
- Fix naming of the body field in ``PUT /directory/room``. (`#1574 <https://github.com/matrix-org/matrix-doc/issues/1574>`_)
- Clarify the filter object schema used in room searching. (`#1577 <https://github.com/matrix-org/matrix-doc/issues/1577>`_)
- Document the 403 error for sending state events. (`#1590 <https://github.com/matrix-org/matrix-doc/issues/1590>`_)
- specify how to handle multiple olm sessions with the same device (`#1596 <https://github.com/matrix-org/matrix-doc/issues/1596>`_)
- Add the other keys that redactions are expected to preserve. (`#1602 <https://github.com/matrix-org/matrix-doc/issues/1602>`_)
- Clarify that clients should not be generating invalid HTML for formatted events. (`#1605 <https://github.com/matrix-org/matrix-doc/issues/1605>`_)
- Clarify the room tag structure (thanks @KitsuneRal!) (`#1606 <https://github.com/matrix-org/matrix-doc/issues/1606>`_)
- Add a note that clients may use the transaction ID to avoid flickering when doing local echo. (`#1619 <https://github.com/matrix-org/matrix-doc/issues/1619>`_)
- Include the request and response structures for the various ``/requestToken`` endpoints. (`#1636 <https://github.com/matrix-org/matrix-doc/issues/1636>`_)
- Clarify the available error codes, and when to prefer the HTTP status code over the ``errcode``. (`#1637 <https://github.com/matrix-org/matrix-doc/issues/1637>`_)
- Clarify and generalise the language used for describing pagination. (`#1642 <https://github.com/matrix-org/matrix-doc/issues/1642>`_)
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
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
(`#373 <https://github.com/matrix-org/matrix-doc/pull/373>`_).
- Remove ``/tokenrefresh`` from the API
(`#395 <https://github.com/matrix-org/matrix-doc/pull/395>`_).
- Remove requirement that tokens used in token-based login be macaroons
(`#395 <https://github.com/matrix-org/matrix-doc/pull/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 <https://github.com/matrix-org/matrix-doc/pull/723>`_).
- Changes to the API which will be backwards-compatible for clients:
- Add ``filename`` parameter to ``POST /_matrix/media/r0/upload``
(`#364 <https://github.com/matrix-org/matrix-doc/pull/364>`_).
- Document CAS-based client login and the use of ``m.login.token`` in
``/login`` (`#367 <https://github.com/matrix-org/matrix-doc/pull/367>`_).
- Make ``origin_server_ts`` a mandatory field of room events
(`#379 <https://github.com/matrix-org/matrix-doc/pull/370>`_).
- Add top-level ``account_data`` key to the responses to ``GET /sync`` and
``GET /initialSync``
(`#380 <https://github.com/matrix-org/matrix-doc/pull/380>`_).
- Add ``is_direct`` flag to ``POST /createRoom`` and invite member event.
Add 'Direct Messaging' module
(`#389 <https://github.com/matrix-org/matrix-doc/pull/389>`_).
- Add ``contains_url`` option to ``RoomEventFilter``
(`#390 <https://github.com/matrix-org/matrix-doc/pull/390>`_).
- Add ``filter`` optional query param to ``/messages``
(`#390 <https://github.com/matrix-org/matrix-doc/pull/390>`_).
- Add 'Send-to-Device messaging' module
(`#386 <https://github.com/matrix-org/matrix-doc/pull/386>`_).
- Add 'Device management' module
(`#402 <https://github.com/matrix-org/matrix-doc/pull/402>`_).
- Require that User-Interactive auth fallback pages call
``window.postMessage`` to notify apps of completion
(`#398 <https://github.com/matrix-org/matrix-doc/pull/398>`_).
- Add pagination and filter support to ``/publicRooms``. Change response to
omit fields rather than return ``null``. Add estimate of total number of
rooms in list.
(`#388 <https://github.com/matrix-org/matrix-doc/pull/388>`_).
- Allow guest accounts to use a number of endpoints which are required for
end-to-end encryption.
(`#751 <https://github.com/matrix-org/matrix-doc/pull/751>`_).
- Add key distribution APIs, for use with end-to-end encryption.
(`#894 <https://github.com/matrix-org/matrix-doc/pull/894>`_).
- Add ``m.room.pinned_events`` state event for rooms.
(`#1007 <https://github.com/matrix-org/matrix-doc/pull/1007>`_).
- Add mention of ability to send Access Token via an Authorization Header.
- Add ``guest_can_join`` parameter to ``POST /createRoom``
(`#1093 <https://github.com/matrix-org/matrix-doc/pull/1093>`_).
- New endpoints:
- ``GET /joined_rooms``
(`#999 <https://github.com/matrix-org/matrix-doc/pull/999>`_).
- ``GET /rooms/{roomId}/joined_members``
(`#999 <https://github.com/matrix-org/matrix-doc/pull/999>`_).
- ``GET /account/whoami``
(`#1063 <https://github.com/matrix-org/matrix-doc/pull/1063>`_).
- ``GET /media/{version}/preview_url``
(`#1064 <https://github.com/matrix-org/matrix-doc/pull/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 <https://github.com/matrix-org/matrix-doc/pull/997>`_)
- Fix "membership" property on third-party invite upgrade example
(`#995 <https://github.com/matrix-org/matrix-doc/pull/995>`_)
- Fix response format and 404 example for room alias lookup
(`#960 <https://github.com/matrix-org/matrix-doc/pull/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 <https://github.com/matrix-org/matrix-doc/pull/950>`_).
- Spell out the way that state is handled by ``POST /createRoom``
(`#362 <https://github.com/matrix-org/matrix-doc/pull/362>`_).
- Clarify the fields which are applicable to different types of push rule
(`#365 <https://github.com/matrix-org/matrix-doc/pull/365>`_).
- A number of clarifications to authentication
(`#371 <https://github.com/matrix-org/matrix-doc/pull/371>`_).
- Correct references to ``user_id`` which should have been ``sender``
(`#376 <https://github.com/matrix-org/matrix-doc/pull/376>`_).
- Correct inconsistent specification of ``redacted_because`` fields and their
values (`#378 <https://github.com/matrix-org/matrix-doc/pull/378>`_).
- Mark required fields in response objects as such
(`#394 <https://github.com/matrix-org/matrix-doc/pull/394>`_).
- Make ``m.notice`` description a bit harder in its phrasing to try to
dissuade the same issues that occurred with IRC
(`#750 <https://github.com/matrix-org/matrix-doc/pull/750>`_).
- ``GET /user/{userId}/filter/{filterId}`` requires authentication
(`#1003 <https://github.com/matrix-org/matrix-doc/pull/1003>`_).
- Add some clarifying notes on the behaviour of rooms with no
``m.room.power_levels`` event
(`#1026 <https://github.com/matrix-org/matrix-doc/pull/1026>`_).
- Clarify the relationship between ``username`` and ``user_id`` in the
``/register`` API
(`#1032 <https://github.com/matrix-org/matrix-doc/pull/1032>`_).
- Clarify rate limiting and security for content repository.
(`#1064 <https://github.com/matrix-org/matrix-doc/pull/1064>`_).
r0.2.0
======
- Spec clarifications:
- Room aliases (`#337 <https://github.com/matrix-org/matrix-doc/pull/337>`_):
- Make it clear that ``GET /directory/room/{roomAlias}`` must work for
federated aliases.
- ``GET /directory/room/{roomAlias}`` cannot return a 409; the ``PUT``
endpoint can, however.
- Power levels:
- Clarify the defaults to be used for various fields of the
``m.room.power_levels`` event
(`#286 <https://github.com/matrix-org/matrix-doc/pull/286>`_,
`#341 <https://github.com/matrix-org/matrix-doc/pull/341>`_).
- Add suggestions for mapping of names to power levels
(`#336 <https://github.com/matrix-org/matrix-doc/pull/336>`_).
- Clarify the room naming algorithm in certain edge cases
(`#351 <https://github.com/matrix-org/matrix-doc/pull/351>`_).
- Remove outdated references to the pre-r0 ``/events`` API, and clarify the
section on syncing
(`#352 <https://github.com/matrix-org/matrix-doc/pull/352>`_).
- Changes to the API which will be backwards-compatible for clients:
- New endpoints:
- ``POST /register/email/requestToken``
(`#343 <https://github.com/matrix-org/matrix-doc/pull/343>`_).
- ``POST /account/3pid/email/requestToken``
(`#346 <https://github.com/matrix-org/matrix-doc/pull/346>`_).
- ``POST /account/password/email/requestToken``
(`#346 <https://github.com/matrix-org/matrix-doc/pull/346>`_).
- ``POST /account/deactivate``
(`#361 <https://github.com/matrix-org/matrix-doc/pull/361>`_).
- Updates to the Presence module
(`#278 <https://github.com/matrix-org/matrix-doc/pull/278>`_,
`#342 <https://github.com/matrix-org/matrix-doc/pull/342>`_):
- Remove unused ``free_for_chat`` presence state
- Add ``currently_active`` flag to the ``m.presence`` event and the ``GET
/presence/{userId}/status`` response.
- Make idle timeout the responsibility of the server
- Remove requirements on servers to propagate profile information via
``m.presence`` events.
- Add new predefined push rules
(`#274 <https://github.com/matrix-org/matrix-doc/pull/274>`_,
`#340 <https://github.com/matrix-org/matrix-doc/pull/340/files>`_).
- ``/sync`` should always return a ``prev_batch`` token
(`#345 <https://github.com/matrix-org/matrix-doc/pull/345>`_).
- add ``to`` parameter to ``GET /rooms/{roomId}/messages`` API
(`#348 <https://github.com/matrix-org/matrix-doc/pull/348>`_).
r0.1.0
======
This release includes the following changes since r0.0.1:
- Breaking changes to the API [#]_:
- ``POST /rooms/{roomId}/join`` no longer permits use of a room alias instead
of a room id. (``POST /join/{roomIdOrAlias}`` continues to allow either.)
- ``POST /account/3pid``: correct the name of the ``three_pid_creds``
parameter
- The "Push Rules" module no longer supports device-specific rules:
- ``GET /pushrules`` no longer returns a ``device`` property
- ``device/{profile_tag}`` is no longer a valid ``scope`` for push rules
- ``profile_tag`` is no longer a valid kind of condition on push rules.
(Device-specific push rules will be reintroduced in the future; in the
meantime, their specification has been moved to a `draft branch`__.)
__ https://matrix.org/speculator/spec/drafts%2Freinstate_device_push_rules/
- Changes to the API which will be backwards-compatible for clients:
- New endpoints:
- ``POST /logout``
- ``POST /rooms/{roomId}/unban``
- ``POST /rooms/{roomId}/kick``
- ``GET /pushers``
- ``GET /pushrules/{scope}/{kind}/{ruleId}/enabled``
(previously ``PUT``-only)
- ``GET`` and ``PUT /pushrules/{scope}/{kind}/{ruleId}/actions``
- Add ``third_party_signed`` parameter to ``POST /rooms/{roomId}/join``
- Add ``M_INVALID_USERNAME`` as valid response to ``POST /register``
- Add ``unread_notifications`` field to ``GET /sync`` response
- Add optional ``invite`` property to ``m.room.power_levels`` state event
- Add optional ``public_key`` and ``public_keys`` to
``m.room.third_party_invite`` state event
- Password-based ``/login`` may now use a third-party identifier instead of
a matrix user id.
- Spec clarifications
- Make the state diagram for room membership explicit
- Note that a user may not be invited to a room while banned
- Clarify the expected order of events in the response to
``GET /rooms/{roomId}/context/{eventId}``, as well as correcting the
example for that API
- Clarify the behaviour of the "Room History Visibility" module; in
particular, the behaviour of the ``shared`` history visibility, and how
events at visibility boundaries should be handled
- Separate the "Room Previews" module from "Guest access"
- Reword the description of the ``profile_tag`` property in
``PUT /pushers/set``, and note that it is not mandatory.
.. [#] Our `versioning policy <../index.html#specification-versions>`_ would
strictly require that a breaking change be denoted by a new major
specification version. However we are not aware of any clients which
rely on the old behaviour here, nor server implementations which offer
it, so we have chosen to retain the r0 designation on this occasion.
r0.0.1
======
This release includes the following changes since r0.0.0:
- API changes:
- Added new ``/versions`` API
- ``/createRoom`` takes an optional ``invite_3pid`` parameter
- ``/publicRooms`` returns an ``avatar_url`` result
- The following APIs are now deprecated:
- ``/initialSync``
- ``/events``
- ``/events/:eventId``
- ``/rooms/:roomId/initialSync``
- Spec clarifications
- Document inter-version compatibility
- Document the parameters to the ``/user/:userId/filter`` API
- Document the ``next_batch`` parameter on ``/search``
- Document the membership states on ``m.room.member`` events
- Minor clarifications/corrections to:
- Guest access module
- Search module
- ``/login`` API
- ``/rooms/:roomId/send/:eventType/:txnId`` API
- ``/rooms/:roomId/context/:eventId`` API
r0.0.0
======
This is the first release of the client-server specification. It is largely a dump of what has currently been implemented, and there are several inconsistencies.
An upcoming minor release will deprecate many of these inconsistencies, and they will be removed in the next major release.
Since the draft stage, the following major changes have been made:
- /api/v1 and /v2_alpha path segments have been replaced with the major version of the release (i.e. 'r0').
- Some POST versions of APIs with both POST and PUT have been removed.
- The specification has been split into one specification per API. This is the client-server API. The server-server API can be found documented separately.
- All APIs are now documented using Swagger
- The following modules have been added:
- Content repository
- Instant messaging
- Push notification
- History visibility
- Search
- Invites based on third-party identifiers
- Room tagging
- Guest access
- Client config
- The following APIs were added:
- ``/sync``
- ``/publicRooms``
- ``/rooms/{roomId}/forget``
- ``/admin/whois``
- ``/rooms/{roomId}/redact``
- ``/user/{userId}/filter``
- The following APIs have been significantly modified:
- Invitations now contain partial room state
- Invitations can now be rejected
- ``/directory``
- The following events have been added:
- ``m.room.avatar``
- Example signed json is included for reference
- Commentary on display name calculation was added

View file

@ -1,54 +0,0 @@
r0.3.0
======
New Endpoints
-------------
- Add ``/account``, ``/account/register``, and ``/account/logout`` to authenticate with the identity server. (`#2255 <https://github.com/matrix-org/matrix-doc/issues/2255>`_)
- Add endpoints for accepting and handling terms of service. (`#2258 <https://github.com/matrix-org/matrix-doc/issues/2258>`_)
- Add ``/hash_details`` and a new ``/lookup`` endpoint for performing hashed association lookups. (`#2287 <https://github.com/matrix-org/matrix-doc/issues/2287>`_)
Backwards Compatible Changes
----------------------------
- Deprecate the v1 API in favour of an authenticated v2 API. (`#2254 <https://github.com/matrix-org/matrix-doc/issues/2254>`_)
r0.2.1
======
Spec Clarifications
-------------------
- Remove incorrect ``id_server`` parameter from ``/requestToken`` endpoints. (`#2124 <https://github.com/matrix-org/matrix-doc/issues/2124>`_)
- Clarify that identity servers can return 403 for unbind requests. (`#2126 <https://github.com/matrix-org/matrix-doc/issues/2126>`_)
r0.2.0
======
New Endpoints
-------------
- Add ``/3pid/unbind`` for removing 3PIDs. (`#2046 <https://github.com/matrix-org/matrix-doc/issues/2046>`_)
Spec Clarifications
-------------------
- Fix various spelling mistakes throughout the specification. (`#1853 <https://github.com/matrix-org/matrix-doc/issues/1853>`_)
- Fix route for ``/3pid/bind``. (`#1967 <https://github.com/matrix-org/matrix-doc/issues/1967>`_)
- Add missing aesthetic parameters to ``/store-invite``. (`#2049 <https://github.com/matrix-org/matrix-doc/issues/2049>`_)
- Clarify what the client should receive upon sending an identical email validation request multiple times. (`#2057 <https://github.com/matrix-org/matrix-doc/issues/2057>`_)
- Clarify that the default transport is JSON over HTTP. (`#2086 <https://github.com/matrix-org/matrix-doc/issues/2086>`_)
r0.1.0
======
This is the first release of the Identity Service API. With this API, clients and
homeservers can store bindings between third-party identifiers such as email addresses
and phone numbers, associating them with Matrix user IDs. Additionally, identity
servers offer the ability to invite third-party users to Matrix rooms by storing
the invite until the identifier is bound.

View file

@ -1,15 +0,0 @@
r0.1.1
======
Spec Clarifications
-------------------
- Fix ``event_id`` field in push request body. (`#2151 <https://github.com/matrix-org/matrix-doc/issues/2151>`_)
r0.1.0
======
The first release of the Push Gateway specification. This release contains
a single endpoint, ``/notify``, that pushers may use to send push notifications
to clients.

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