Merge branch 'master' into travis/spec/MSC2320-identity-versions
This commit is contained in:
commit
fc6aa30000
62 changed files with 1858 additions and 261 deletions
|
@ -89,8 +89,8 @@ To create a changelog entry, create a file named in the format ``prNumber.type``
|
|||
the ``newsfragments`` directory. The ``type`` can be one of the following:
|
||||
|
||||
* ``new`` - Used when adding new endpoints. Please have the file contents be the
|
||||
method and route being added, surrounded in RST code tags. For example: ``POST
|
||||
/accounts/whoami``
|
||||
method and route being added, surrounded in markdown code tags. For example: \`POST
|
||||
/accounts/whoami\`.
|
||||
|
||||
* ``feature`` - Used when adding backwards-compatible changes to the API.
|
||||
|
||||
|
@ -103,7 +103,7 @@ the ``newsfragments`` directory. The ``type`` can be one of the following:
|
|||
|
||||
All news fragments must have a brief summary explaining the change in the
|
||||
contents of the file. The summary must end in a full stop to be in line with
|
||||
the style guide and and formatting must be done using Markdown.
|
||||
the style guide and formatting must be done using Markdown.
|
||||
|
||||
Changes that do not change the spec, such as changes to the build script, formatting,
|
||||
CSS, etc should not get a news fragment.
|
||||
|
|
35
README.md
35
README.md
|
@ -2,10 +2,11 @@
|
|||
|
||||
This repository contains the Matrix Specification, rendered at [spec.matrix.org](http://spec.matrix.org/).
|
||||
|
||||
Developers looking to use Matrix should join [#matrix-dev:matrix.org](http://matrix.to/#/#matrix-dev:matrix.org)
|
||||
Developers looking to use Matrix should join [#matrix-dev:matrix.org](https://matrix.to/#/#matrix-dev:matrix.org)
|
||||
on Matrix for help.
|
||||
|
||||
Spec authors and proposal writers are welcome to join [#matrix-spec:matrix.org](http://matrix.to/#/#matrix-spec:matrix.org). We welcome contributions! See [CONTRIBUTING.rst](./CONTRIBUTING.rst) for details.
|
||||
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
|
||||
|
||||
|
@ -21,7 +22,7 @@ The Matrix spec is compiled with [Hugo](https://gohugo.io/) (a static site gener
|
|||
|
||||
* `/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
|
||||
parse them. This is also where our Swagger/OpenAPI definitions and schemas are.
|
||||
|
||||
* `/layouts`: this contains [Hugo templates](https://gohugo.io/templates/). Some templates define the overall layout of
|
||||
a page: for example, whether it has header, footer, sidebar, and so on.
|
||||
|
@ -35,8 +36,8 @@ The Matrix spec is compiled with [Hugo](https://gohugo.io/) (a static site gener
|
|||
|
||||
* `/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 them 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 this
|
||||
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:
|
||||
|
@ -48,10 +49,9 @@ 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.
|
||||
* `/event-schemas`: [JSON Schema](http://json-schema.org/) definitions for the spec.
|
||||
* `/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.
|
||||
* `/scripts`: Various scripts for generating the spec and validating its contents.
|
||||
* `/proposals`: Matrix Spec Change (MSC) proposals. See <https://spec.matrix.org/unstable/proposals/>.
|
||||
|
||||
## Authoring changes to the spec
|
||||
|
@ -62,7 +62,8 @@ 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>
|
||||
2. Run `git submodule update --init --recursive` for good measure.
|
||||
3. Run `npm i` to install the dependencies. Note that this will require NodeJS to be installed.
|
||||
4. Run `npm run get-proposals` to seed the proposals data. This is not required.
|
||||
4. Run `npm run get-proposals` to seed proposal data. This is merely for populating the content of the "Spec Change Proposals"
|
||||
page and is not required.
|
||||
5. Run `hugo serve` to run a local webserver which builds whenever a file change is detected. If watching doesn't appear
|
||||
to be working for you, try `hugo serve --disableFastRender` instead.
|
||||
6. Edit the specification 🙂
|
||||
|
@ -73,18 +74,18 @@ Awesome. If you're looking at making design-related changes to the spec site, pl
|
|||
|
||||
## Building the specification
|
||||
|
||||
If for some reason you're not a CI/CD system and want to render the spec 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.
|
||||
If for some reason you're not a CI/CD system and want to render a static version of the spec for yourself, follow the above
|
||||
steps for authoring changes to the specification and instead of `hugo serve` run `hugo -d "spec"` - this will generate the
|
||||
spec to `/spec`. If you'd like to serve the spec off a path instead of a domain root (eg: `/unstable`), add `--baseURL "/unstable"`
|
||||
to the `hugo -d "spec"` command.
|
||||
|
||||
For building the swagger definitions, create a python3 virtualenv and activate it. Then run `pip install -r ./scripts/requirements.txt` and finally `python ./scripts/dump-swagger.py` to generate it to `./scripts/swagger/api-docs.json`.
|
||||
To make use of the generated file, there are a number of options:
|
||||
For building the swagger definitions, create a python3 virtualenv and activate it. Then run `pip install -r ./scripts/requirements.txt`
|
||||
and finally `python ./scripts/dump-swagger.py` to generate it to `./scripts/swagger/api-docs.json`. To make use of the generated file,
|
||||
there are a number of options:
|
||||
|
||||
* It can be uploaded from your filesystem to an online editor/viewer such as
|
||||
http://editor.swagger.io/
|
||||
* It can be uploaded from your filesystem to an online editor/viewer such as [on the swagger website](http://editor.swagger.io/).
|
||||
* You can run a local HTTP server by running `./scripts/swagger-http-server.py`, and then view the documentation via an
|
||||
online viewer; for example, at <http://petstore.swagger.io/?url=http://localhost:8000/api-docs.json>
|
||||
online viewer; for example, at <http://petstore.swagger.io/?url=http://localhost:8000/api-docs.json>.
|
||||
* You can host the swagger UI yourself. See <https://github.com/swagger-api/swagger-ui#how-to-run> for advice on how to
|
||||
do so.
|
||||
|
||||
|
|
1
changelogs/client_server/newsfragments/3098.feature
Normal file
1
changelogs/client_server/newsfragments/3098.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add support for spoilers ([MSC2010](https://github.com/matrix-org/matrix-doc/pull/2010) and [MSC2557](https://github.com/matrix-org/matrix-doc/pull/2557)), and `color` attribute ([MSC2422](https://github.com/matrix-org/matrix-doc/pull/2422)).
|
|
@ -0,0 +1 @@
|
|||
Clarify that event bodies are untrusted, as per [MSC2801](https://github.com/matrix-org/matrix-doc/pull/2801).
|
1
changelogs/client_server/newsfragments/3100.feature
Normal file
1
changelogs/client_server/newsfragments/3100.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add `<details>` and `<summary>` to the suggested HTML subset as per [MSC2184](https://github.com/matrix-org/matrix-doc/pull/2184).
|
|
@ -0,0 +1 @@
|
|||
Fix various typos throughout the specification.
|
|
@ -0,0 +1 @@
|
|||
Fix the maximum event size restriction (65535 bytes -> 65536).
|
1
changelogs/client_server/newsfragments/3139.breaking
Normal file
1
changelogs/client_server/newsfragments/3139.breaking
Normal file
|
@ -0,0 +1 @@
|
|||
Add `m.key.verification.ready` and `m.key.verification.done` to key verification framework as per [MSC2366](https://github.com/matrix-org/matrix-doc/pull/2366).
|
1
changelogs/client_server/newsfragments/3139.feature
Normal file
1
changelogs/client_server/newsfragments/3139.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add key verification using in-room messages as per [MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241).
|
1
changelogs/client_server/newsfragments/3147.feature
Normal file
1
changelogs/client_server/newsfragments/3147.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add information about using SSSS for cross-signing and key backup.
|
1
changelogs/client_server/newsfragments/3149.feature
Normal file
1
changelogs/client_server/newsfragments/3149.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add key verification method using QR codes ([MSC1544](https://github.com/matrix-org/matrix-doc/pull/1544)).
|
1
changelogs/client_server/newsfragments/3150.feature
Normal file
1
changelogs/client_server/newsfragments/3150.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add key verification using in-room messages as per [MSC2241](https://github.com/matrix-org/matrix-doc/pull/2241).
|
1
changelogs/client_server/newsfragments/3151.feature
Normal file
1
changelogs/client_server/newsfragments/3151.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Document how clients can simplify usage of Secure Secret Storage ([MSC2874](https://github.com/uhoreg/matrix-doc/pull/new/single_ssss_spec)).
|
1
changelogs/client_server/newsfragments/3154.feature
Normal file
1
changelogs/client_server/newsfragments/3154.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add support for knocking, as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
1
changelogs/client_server/newsfragments/3154.new
Normal file
1
changelogs/client_server/newsfragments/3154.new
Normal file
|
@ -0,0 +1 @@
|
|||
Add `/knock` endpoint as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
|
@ -0,0 +1 @@
|
|||
Fix various typos throughout the specification.
|
|
@ -0,0 +1 @@
|
|||
Fix various typos throughout the specification.
|
1
changelogs/server_server/newsfragments/3154.feature
Normal file
1
changelogs/server_server/newsfragments/3154.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Add support for knocking, as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
1
changelogs/server_server/newsfragments/3154.new
Normal file
1
changelogs/server_server/newsfragments/3154.new
Normal file
|
@ -0,0 +1 @@
|
|||
Add `/make_knock` and `/send_knock` endpoints as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
|
@ -232,6 +232,18 @@ reserved for events defined in the Matrix specification - for instance
|
|||
`m.room.message` is the event type for instant messages. Events are
|
||||
usually sent in the context of a "Room".
|
||||
|
||||
{{% boxes/warning %}}
|
||||
Event bodies are considered untrusted data. This means that any application using
|
||||
Matrix must validate that the event body is of the expected shape/schema
|
||||
before using the contents verbatim.
|
||||
|
||||
**It is not safe to assume that an event body will have all the expected
|
||||
fields of the expected types.**
|
||||
|
||||
See [MSC2801](https://github.com/matrix-org/matrix-doc/pull/2801) for more
|
||||
detail on why this assumption is unsafe.
|
||||
{{% /boxes/warning %}}
|
||||
|
||||
### Event Graphs
|
||||
|
||||
Events exchanged in the context of a room are stored in a directed
|
||||
|
@ -493,7 +505,7 @@ stable and unstable periodically for a variety of reasons, including
|
|||
discovered security vulnerabilities and age.
|
||||
|
||||
Clients should not ask room administrators to upgrade their rooms if the
|
||||
room is running a stable version. Servers SHOULD use room version 6 as
|
||||
room is running a stable version. Servers SHOULD use **room version 6** as
|
||||
the default room version when creating new rooms.
|
||||
|
||||
The available room versions are:
|
||||
|
@ -510,6 +522,7 @@ The available room versions are:
|
|||
signing key validity periods.
|
||||
- [Version 6](/rooms/v6) - **Stable**. Alters several
|
||||
authorization rules for events.
|
||||
- [Version 7](/rooms/v7) - **Stable**. Introduces knocking.
|
||||
|
||||
## Specification Versions
|
||||
|
||||
|
|
|
@ -1381,6 +1381,18 @@ opaque string. No changes should be required to support the currently
|
|||
available room versions.
|
||||
{{% /boxes/warning %}}
|
||||
|
||||
{{% boxes/warning %}}
|
||||
Event bodies are considered untrusted data. This means that any application using
|
||||
Matrix must validate that the event body is of the expected shape/schema
|
||||
before using the contents verbatim.
|
||||
|
||||
**It is not safe to assume that an event body will have all the expected
|
||||
fields of the expected types.**
|
||||
|
||||
See [MSC2801](https://github.com/matrix-org/matrix-doc/pull/2801) for more
|
||||
detail on why this assumption is unsafe.
|
||||
{{% /boxes/warning %}}
|
||||
|
||||
### Types of room events
|
||||
|
||||
Room events are split into two categories:
|
||||
|
@ -1435,7 +1447,7 @@ following fields.
|
|||
|
||||
### Size limits
|
||||
|
||||
The complete event MUST NOT be larger than 65535 bytes, when formatted
|
||||
The complete event MUST NOT be larger than 65536 bytes, when formatted
|
||||
as a [PDU for the Server-Server
|
||||
protocol](/server-server-api/#pdus), including any
|
||||
signatures, and encoded as [Canonical
|
||||
|
@ -1451,7 +1463,7 @@ There are additional restrictions on sizes per key:
|
|||
|
||||
Some event types have additional size restrictions which are specified
|
||||
in the description of the event. Additional keys have no limit other
|
||||
than that implied by the total 65 KB limit on events.
|
||||
than that implied by the total 64 KiB limit on events.
|
||||
|
||||
### Room Events
|
||||
|
||||
|
@ -1766,15 +1778,24 @@ in that room. There are several states in which a user may be, in
|
|||
relation to a room:
|
||||
|
||||
- Unrelated (the user cannot send or receive events in the room)
|
||||
- Knocking (the user has requested to participate in the room, but has
|
||||
not yet been allowed to)
|
||||
- Invited (the user has been invited to participate in the room, but
|
||||
is not yet participating)
|
||||
- Joined (the user can send and receive events in the room)
|
||||
- Banned (the user is not allowed to join the room)
|
||||
|
||||
There is an exception to the requirement that a user join a room before
|
||||
sending events to it: users may send an `m.room.member` event to a room
|
||||
with `content.membership` set to `leave` to reject an invitation if they
|
||||
have currently been invited to a room but have not joined it.
|
||||
There are a few notable exceptions which allow non-joined members of the
|
||||
room to send events in the room:
|
||||
|
||||
- Users wishing to reject an invite would send `m.room.member` events with
|
||||
`content.membership` of `leave`. They must have been invited first.
|
||||
|
||||
- If the room allows, users can send `m.room.member` events with `content.membership`
|
||||
of `knock` to knock on the room. This is a request for an invite by the user.
|
||||
|
||||
- To retract a previous knock, a user would send a `leave` event similar to
|
||||
rejecting an invite.
|
||||
|
||||
Some rooms require that users be invited to it before they can join;
|
||||
others allow anyone to join. Whether a given room is an "invite-only"
|
||||
|
@ -1787,36 +1808,14 @@ This room is free for anyone to join without an invite.
|
|||
`invite`
|
||||
This room can only be joined if you were invited.
|
||||
|
||||
`knock`
|
||||
This room can only be joined if you were invited, and allows anyone to
|
||||
request an invite to the room. Note that this join rule is only available
|
||||
to rooms based upon [room version 7](/rooms/v7).
|
||||
|
||||
The allowable state transitions of membership are:
|
||||
|
||||
```
|
||||
/ban
|
||||
+------------------------------------------------------+
|
||||
| |
|
||||
| +----------------+ +----------------+ |
|
||||
| | /leave | | | |
|
||||
| | v v | |
|
||||
/invite +--------+ +-------+ | |
|
||||
------------>| invite |<----------| leave |----+ | |
|
||||
+--------+ /invite +-------+ | | |
|
||||
| | ^ | | |
|
||||
| | | | | |
|
||||
/join | +---------------+ | | | |
|
||||
| | /join if | | | |
|
||||
| | join_rules | | /ban | /unban |
|
||||
| | public /leave | | | |
|
||||
v v or | | | |
|
||||
+------+ /kick | | | |
|
||||
------------>| join |-------------------+ | | |
|
||||
/join +------+ v | |
|
||||
if | +-----+ | |
|
||||
join_rules +-------------------------->| ban |-----+ |
|
||||
public /ban +-----+ |
|
||||
^ ^ |
|
||||
| | |
|
||||
----------------------------------------------+ +----------------------+
|
||||
/ban
|
||||
```
|
||||

|
||||
|
||||
{{% http-api spec="client-server" api="list_joined_rooms" %}}
|
||||
|
||||
|
@ -1826,14 +1825,51 @@ The allowable state transitions of membership are:
|
|||
|
||||
{{% http-api spec="client-server" api="joining" %}}
|
||||
|
||||
##### Knocking on rooms
|
||||
|
||||
<!--
|
||||
This section is here because it's most similar to being invited/joining a
|
||||
room, though has added complexity which needs to be explained. Otherwise
|
||||
this will have been just the API definition and nothing more (like invites).
|
||||
-->
|
||||
|
||||
If the join rules allow, external users to the room can `/knock` on it to
|
||||
request permission to join. Users with appropriate permissions within the
|
||||
room can then approve (`/invite`) or deny (`/kick`, `/ban`, or otherwise
|
||||
set membership to `leave`) the knock. Knocks can be retracted by calling
|
||||
`/leave` or otherwise setting membership to `leave`.
|
||||
|
||||
Users who are currently in the room, already invited, or banned cannot
|
||||
knock on the room.
|
||||
|
||||
To accept another user's knock, the user must have permission to invite
|
||||
users to the room. To reject another user's knock, the user must have
|
||||
permission to either kick or ban users (whichever is being performed).
|
||||
Note that setting another user's membership to `leave` is kicking them.
|
||||
|
||||
The knocking homeserver should assume that an invite to the room means
|
||||
that the knock was accepted, even if the invite is not explicitly related
|
||||
to the knock.
|
||||
|
||||
Homeservers are permitted to automatically accept invites as a result of
|
||||
knocks as they should be aware of the user's intent to join the room. If
|
||||
the homeserver is not auto-accepting invites (or there was an unrecoverable
|
||||
problem with accepting it), the invite is expected to be passed down normally
|
||||
to the client to handle. Clients can expect to see the join event if the
|
||||
server chose to auto-accept.
|
||||
|
||||
{{% http-api spec="client-server" api="knocking" %}}
|
||||
|
||||
#### Leaving rooms
|
||||
|
||||
A user can leave a room to stop receiving events for that room. A user
|
||||
must have been invited to or have joined the room before they are
|
||||
eligible to leave the room. Leaving a room to which the user has been
|
||||
invited rejects the invite. Once a user leaves a room, it will no longer
|
||||
appear in the response to the [`/sync`](/client-server-api/#get_matrixclientr0sync) API unless it is explicitly
|
||||
requested via a filter with the `include_leave` field set to `true`.
|
||||
invited rejects the invite, and can retract a knock. Once a user leaves
|
||||
a room, it will no longer appear in the response to the
|
||||
[`/sync`](/client-server-api/#get_matrixclientr0sync) API unless it is
|
||||
explicitly requested via a filter with the `include_leave` field set
|
||||
to `true`.
|
||||
|
||||
Whether or not they actually joined the room, if the room is an
|
||||
"invite-only" room the user will need to be re-invited before they can
|
||||
|
@ -1841,7 +1877,7 @@ re-join the room.
|
|||
|
||||
A user can also forget a room which they have left. Rooms which have
|
||||
been forgotten will never appear the response to the [`/sync`](/client-server-api/#get_matrixclientr0sync) API,
|
||||
until the user re-joins or is re-invited.
|
||||
until the user re-joins, is re-invited, or knocks.
|
||||
|
||||
A user may wish to force another user to leave a room. This can be done
|
||||
by 'kicking' the other user. To do so, the user performing the kick MUST
|
||||
|
|
|
@ -247,8 +247,7 @@ file type. The key is sent using the [JSON Web
|
|||
Key](https://tools.ietf.org/html/rfc7517#appendix-A.3) format, with a
|
||||
[W3C extension](https://w3c.github.io/webcrypto/#iana-section-jwk).
|
||||
|
||||
Extensions to `m.room.message` msgtypes
|
||||
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
###### Extensions to `m.room.message` msgtypes
|
||||
|
||||
This module adds `file` and `thumbnail_file` properties, of type
|
||||
`EncryptedFile`, to `m.room.message` msgtypes that reference files, such
|
||||
|
@ -357,8 +356,8 @@ out-of-band channel: there is no way to do it within Matrix without
|
|||
trusting the administrators of the homeservers.
|
||||
|
||||
In Matrix, verification works by Alice meeting Bob in person, or
|
||||
contacting him via some other trusted medium, and use [SAS
|
||||
Verification](#SAS Verification) to interactively verify Bob's devices.
|
||||
contacting him via some other trusted medium, and using one of the
|
||||
verification methods defined below to interactively verify Bob's devices.
|
||||
Alice and Bob may also read aloud their unpadded base64 encoded Ed25519
|
||||
public key, as returned by `/keys/query`.
|
||||
|
||||
|
@ -390,60 +389,70 @@ decrypted by such a device. For the Olm protocol, this is documented at
|
|||
Verifying keys manually by reading out the Ed25519 key is not very
|
||||
user-friendly, and can lead to errors. In order to help mitigate errors,
|
||||
and to make the process easier for users, some verification methods are
|
||||
supported by the specification. The methods all use a common framework
|
||||
supported by the specification and use messages exchanged by the user's devices
|
||||
to assist in the verification. The methods all use a common framework
|
||||
for negotiating the key verification.
|
||||
|
||||
To use this framework, Alice's client would send
|
||||
`m.key.verification.request` events to Bob's devices. All of the
|
||||
`to_device` messages sent to Bob MUST have the same `transaction_id` to
|
||||
indicate they are part of the same request. This allows Bob to reject
|
||||
the request on one device, and have it apply to all of his devices.
|
||||
Similarly, it allows Bob to process the verification on one device
|
||||
without having to involve all of his devices.
|
||||
Verification messages can be sent either in a room shared by the two parties,
|
||||
which should be a [direct messaging](#direct-messaging) room between the two
|
||||
parties, or by using [to-device](#send-to-device-messaging) messages sent
|
||||
directly between the two devices involved. In both cases, the messages
|
||||
exchanged are similar, with minor differences as detailed below. Verifying
|
||||
between two different users should be performed using in-room messages, whereas
|
||||
verifying two devices belonging to the same user should be performed using
|
||||
to-device messages.
|
||||
|
||||
When Bob's device receives an `m.key.verification.request`, it should
|
||||
prompt Bob to verify keys with Alice using one of the supported methods
|
||||
in the request. If Bob's device does not understand any of the methods,
|
||||
it should not cancel the request as one of his other devices may support
|
||||
the request. Instead, Bob's device should tell Bob that an unsupported
|
||||
method was used for starting key verification. The prompt for Bob to
|
||||
accept/reject Alice's request (or the unsupported method prompt) should
|
||||
be automatically dismissed 10 minutes after the `timestamp` field or 2
|
||||
minutes after Bob's client receives the message, whichever comes first,
|
||||
if Bob does not interact with the prompt. The prompt should additionally
|
||||
be hidden if an appropriate `m.key.verification.cancel` message is
|
||||
received.
|
||||
A key verification session is identified by an ID that is established by the
|
||||
first message sent in that session. For verifications using in-room messages,
|
||||
the ID is the event ID of the initial message, and for verifications using
|
||||
to-device messages, the first message contains a `transaction_id` field that is
|
||||
shared by the other messages of that session.
|
||||
|
||||
If Bob rejects the request, Bob's client must send an
|
||||
`m.key.verification.cancel` message to Alice's device. Upon receipt,
|
||||
Alice's device should tell her that Bob does not want to verify her
|
||||
device and send `m.key.verification.cancel` messages to all of Bob's
|
||||
devices to notify them that the request was rejected.
|
||||
In general, verification operates as follows:
|
||||
|
||||
If Bob accepts the request, Bob's device starts the key verification
|
||||
process by sending an `m.key.verification.start` message to Alice's
|
||||
device. Upon receipt of this message, Alice's device should send an
|
||||
`m.key.verification.cancel` message to all of Bob's other devices to
|
||||
indicate the process has been started. The start message must use the
|
||||
same `transaction_id` from the original key verification request if it
|
||||
is in response to the request. The start message can be sent
|
||||
independently of any request.
|
||||
- Alice requests a key verification with Bob by sending an
|
||||
`m.key.verification.request` event. This event indicates the verification
|
||||
methods that Alice's client supports. (Note that "Alice" and "Bob" may in
|
||||
fact be the same user, in the case where a user is verifying their own
|
||||
devices.)
|
||||
- Bob's client prompts Bob to accept the key verification. When Bob accepts
|
||||
the verification, Bob's client sends an `m.key.verification.ready` event.
|
||||
This event indicates the verification methods, corresponding to the
|
||||
verification methods supported by Alice's client, that Bob's client supports.
|
||||
- Alice's or Bob's devices allow their users to select one of the verification
|
||||
methods supported by both devices to use for verification. When Alice or Bob
|
||||
selects a verification method, their device begins the verification by
|
||||
sending an `m.key.verification.start` event, indicating the selected
|
||||
verification method. Note that if there is only one verification method in
|
||||
common between the devices then the receiver's device (Bob) can auto-select
|
||||
it.
|
||||
- Alice and Bob complete the verification as defined by the selected
|
||||
verification method. This could involve their clients exchanging messages,
|
||||
Alice and Bob exchanging information out-of-band, and/or Alice and Bob
|
||||
interacting with their devices.
|
||||
- Alice's and Bob's clients send `m.key.verification.done` events to indicate
|
||||
that the verification was successful.
|
||||
|
||||
Individual verification methods may add additional steps, events, and
|
||||
properties to the verification messages. Event types for methods defined
|
||||
in this specification must be under the `m.key.verification` namespace
|
||||
and any other event types must be namespaced according to the Java
|
||||
package naming convention.
|
||||
Verifications can be cancelled by either device at any time by sending an
|
||||
`m.key.verification.cancel` event with a `code` field that indicates the reason
|
||||
it was cancelled.
|
||||
|
||||
Any of Alice's or Bob's devices can cancel the key verification request
|
||||
or process at any time with an `m.key.verification.cancel` message to
|
||||
all applicable devices.
|
||||
|
||||
This framework yields the following handshake, assuming both Alice and
|
||||
Bob each have 2 devices, Bob's first device accepts the key verification
|
||||
request, and Alice's second device initiates the request. Note how
|
||||
Alice's first device is not involved in the request or verification
|
||||
process.
|
||||
When using to-device messages, Alice may not know which of Bob's devices to
|
||||
verify, or may not want to choose a specific device. In this case, Alice will
|
||||
send `m.key.verification.request` events to all of Bob's devices. All of these
|
||||
events will use the same transaction ID. When Bob accepts or declines the
|
||||
verification on one of his devices (sending either an
|
||||
`m.key.verification.ready` or `m.key.verification.cancel` event), Alice will
|
||||
send an `m.key.verification.cancel` event to Bob's other devices with a `code`
|
||||
of `m.accepted` in the case where Bob accepted the verification, or `m.user` in
|
||||
the case where Bob rejected the verification. This yields the following
|
||||
handshake when using to-device messages, assuming both Alice and Bob each have
|
||||
2 devices, Bob's first device accepts the key verification request, and Alice's
|
||||
second device initiates the request. Note how Alice's first device is not
|
||||
involved in the request or verification process. Also note that, although in
|
||||
this example, Bob's device sends the `m.key.verification.start`, Alice's device
|
||||
could also send that message. As well, the order of the
|
||||
`m.key.verification.done` messages could be reversed.
|
||||
|
||||
```
|
||||
+---------------+ +---------------+ +-------------+ +-------------+
|
||||
|
@ -456,20 +465,85 @@ process.
|
|||
| | m.key.verification.request | |
|
||||
| |-------------------------------------------------->|
|
||||
| | | |
|
||||
| | m.key.verification.start | |
|
||||
| | m.key.verification.ready | |
|
||||
| |<----------------------------------| |
|
||||
| | | |
|
||||
| | m.key.verification.cancel | |
|
||||
| |-------------------------------------------------->|
|
||||
| | | |
|
||||
| | m.key.verification.start | |
|
||||
| |<----------------------------------| |
|
||||
| | | |
|
||||
.
|
||||
. (verification messages)
|
||||
.
|
||||
| | | |
|
||||
| | m.key.verification.done | |
|
||||
| |<----------------------------------| |
|
||||
| | | |
|
||||
| | m.key.verification.done | |
|
||||
| |---------------------------------->| |
|
||||
| | | |
|
||||
```
|
||||
|
||||
After the handshake, the verification process begins.
|
||||
When using in-room messages and the room has encryption enabled, clients should
|
||||
ensure that encryption does not hinder the verification. For example, if the
|
||||
verification messages are encrypted, clients must ensure that all the
|
||||
recipient's unverified devices receive the keys necessary to decrypt the
|
||||
messages, even if they would normally not be given the keys to decrypt messages
|
||||
in the room. Alternatively, verification messages may be sent unencrypted,
|
||||
though this is not encouraged.
|
||||
|
||||
Upon receipt of Alice's `m.key.verification.request` message, if Bob's device
|
||||
does not understand any of the methods, it should not cancel the request as one
|
||||
of his other devices may support the request. Instead, Bob's device should tell
|
||||
Bob that no supported method was found, and allow him to manually reject the
|
||||
request.
|
||||
|
||||
The prompt for Bob to accept/reject Alice's request (or the unsupported method
|
||||
prompt) should be automatically dismissed 10 minutes after the `timestamp` (in
|
||||
the case of to-device messages) or `origin_ts` (in the case of in-room
|
||||
messages) field or 2 minutes after Bob's client receives the message, whichever
|
||||
comes first, if Bob does not interact with the prompt. The prompt should
|
||||
additionally be hidden if an appropriate `m.key.verification.cancel` message is
|
||||
received.
|
||||
|
||||
If Bob rejects the request, Bob's client must send an
|
||||
`m.key.verification.cancel` event with `code` set to `m.user`. Upon receipt,
|
||||
Alice's device should tell her that Bob does not want to verify her device and,
|
||||
if the request was sent as a to-device message, send
|
||||
`m.key.verification.cancel` messages to all of Bob's devices to notify them
|
||||
that the request was rejected.
|
||||
|
||||
If Alice's and Bob's clients both send an `m.key.verification.start` message,
|
||||
and both specify the same verification method, then the
|
||||
`m.key.verification.start` message sent by the user whose ID is the
|
||||
lexicographically largest user ID should be ignored, and the situation should
|
||||
be treated the same as if only the user with the lexicographically smallest
|
||||
user ID had sent the `m.key.verification.start` message. In the case where the
|
||||
user IDs are the same (that is, when a user is verifying their own device),
|
||||
then the device IDs should be compared instead. If the two
|
||||
`m.key.verification.start` messages do not specify the same verification
|
||||
method, then the verification should be cancelled with a `code` of
|
||||
`m.unexpected_message`.
|
||||
|
||||
An `m.key.verification.start` message can also be sent independently of any
|
||||
request, specifying the verification method to use.
|
||||
|
||||
Individual verification methods may add additional steps, events, and
|
||||
properties to the verification messages. Event types for methods defined
|
||||
in this specification must be under the `m.key.verification` namespace
|
||||
and any other event types must be namespaced according to the Java
|
||||
package naming convention.
|
||||
|
||||
{{% event event="m.key.verification.request" %}}
|
||||
|
||||
{{% event event="m.key.verification.ready" %}}
|
||||
|
||||
{{% event event="m.key.verification.start" %}}
|
||||
|
||||
{{% event event="m.key.verification.done" %}}
|
||||
|
||||
{{% event event="m.key.verification.cancel" %}}
|
||||
|
||||
##### Short Authentication String (SAS) verification
|
||||
|
@ -493,8 +567,11 @@ example, if we verify 40 bits, then an attacker has a 1 in
|
|||
success. A failed attack would result in a mismatched Short
|
||||
Authentication String, alerting users to the attack.
|
||||
|
||||
The verification process takes place over [to-device](#send-to-device-messaging) messages in two
|
||||
phases:
|
||||
To advertise support for this method, clients use the name `m.sas.v1` in the
|
||||
`methods` fields of the `m.key.verification.request` and
|
||||
`m.key.verification.ready` events.
|
||||
|
||||
The verification process takes place in two phases:
|
||||
|
||||
1. Key agreement phase (based on [ZRTP key
|
||||
agreement](https://tools.ietf.org/html/rfc6189#section-4.4.1)).
|
||||
|
@ -505,63 +582,62 @@ The process between Alice and Bob verifying each other would be:
|
|||
1. Alice and Bob establish a secure out-of-band connection, such as
|
||||
meeting in-person or a video call. "Secure" here means that either
|
||||
party cannot be impersonated, not explicit secrecy.
|
||||
2. Alice and Bob communicate which devices they'd like to verify with
|
||||
each other.
|
||||
3. Alice selects Bob's device from the device list and begins
|
||||
verification.
|
||||
4. Alice's client ensures it has a copy of Bob's device key.
|
||||
5. Alice's device sends Bob's device an `m.key.verification.start`
|
||||
message.
|
||||
6. Bob's device receives the message and selects a key agreement
|
||||
2. Alice and Bob begin a key verification using the key verification
|
||||
framework as described above.
|
||||
3. Alice's device sends Bob's device an `m.key.verification.start`
|
||||
message. Alice's device ensures it has a copy of Bob's device key.
|
||||
4. Bob's device receives the message and selects a key agreement
|
||||
protocol, hash algorithm, message authentication code, and SAS
|
||||
method supported by Alice's device.
|
||||
7. Bob's device ensures it has a copy of Alice's device key.
|
||||
8. Bob's device creates an ephemeral Curve25519 key pair
|
||||
5. Bob's device ensures it has a copy of Alice's device key.
|
||||
6. Bob's device creates an ephemeral Curve25519 key pair
|
||||
(*K<sub>B</sub><sup>private</sup>*, *K<sub>B</sub><sup>public</sup>*),
|
||||
and calculates the hash (using the chosen algorithm) of the public
|
||||
key *K<sub>B</sub><sup>public</sup>*.
|
||||
9. Bob's device replies to Alice's device with an
|
||||
7. Bob's device replies to Alice's device with an
|
||||
`m.key.verification.accept` message.
|
||||
10. Alice's device receives Bob's message and stores the commitment hash
|
||||
8. Alice's device receives Bob's message and stores the commitment hash
|
||||
for later use.
|
||||
11. Alice's device creates an ephemeral Curve25519 key pair
|
||||
9. Alice's device creates an ephemeral Curve25519 key pair
|
||||
(*K<sub>A</sub><sup>private</sup>*, *K<sub>A</sub><sup>public</sup>*)
|
||||
and replies to Bob's device with an `m.key.verification.key`,
|
||||
sending only the public key
|
||||
*K<sub>A</sub><sup>public</sup>*.
|
||||
12. Bob's device receives Alice's message and replies with its own
|
||||
10. Bob's device receives Alice's message and replies with its own
|
||||
`m.key.verification.key` message containing its public key
|
||||
*K<sub>B</sub><sup>public</sup>*.
|
||||
13. Alice's device receives Bob's message and verifies the commitment
|
||||
11. Alice's device receives Bob's message and verifies the commitment
|
||||
hash from earlier matches the hash of the key Bob's device just sent
|
||||
and the content of Alice's `m.key.verification.start` message.
|
||||
14. Both Alice and Bob's devices perform an Elliptic-curve
|
||||
12. Both Alice and Bob's devices perform an Elliptic-curve
|
||||
Diffie-Hellman
|
||||
(*ECDH(K<sub>A</sub><sup>private</sup>*, *K<sub>B</sub><sup>public</sup>*)),
|
||||
using the result as the shared secret.
|
||||
15. Both Alice and Bob's devices display a SAS to their users, which is
|
||||
13. Both Alice and Bob's devices display a SAS to their users, which is
|
||||
derived from the shared key using one of the methods in this
|
||||
section. If multiple SAS methods are available, clients should allow
|
||||
the users to select a method.
|
||||
16. Alice and Bob compare the strings shown by their devices, and tell
|
||||
14. Alice and Bob compare the strings shown by their devices, and tell
|
||||
their devices if they match or not.
|
||||
17. Assuming they match, Alice and Bob's devices calculate the HMAC of
|
||||
15. Assuming they match, Alice and Bob's devices calculate the HMAC of
|
||||
their own device keys and a comma-separated sorted list of the key
|
||||
IDs that they wish the other user to verify, using SHA-256 as the
|
||||
hash function. HMAC is defined in [RFC
|
||||
2104](https://tools.ietf.org/html/rfc2104). The key for the HMAC is
|
||||
different for each item and is calculated by generating 32 bytes
|
||||
(256 bits) using [the key verification HKDF](#hkdf-calculation).
|
||||
18. Alice's device sends Bob's device an `m.key.verification.mac`
|
||||
16. Alice's device sends Bob's device an `m.key.verification.mac`
|
||||
message containing the MAC of Alice's device keys and the MAC of her
|
||||
key IDs to be verified. Bob's device does the same for Bob's device
|
||||
keys and key IDs concurrently with Alice.
|
||||
19. When the other device receives the `m.key.verification.mac` message,
|
||||
17. When the other device receives the `m.key.verification.mac` message,
|
||||
the device calculates the HMAC of its copies of the other device's
|
||||
keys given in the message, as well as the HMAC of the
|
||||
comma-separated, sorted, list of key IDs in the message. The device
|
||||
compares these with the HMAC values given in the message, and if
|
||||
everything matches then the device keys are verified.
|
||||
18. Alice and Bob's devices send `m.key.verification.done` messages to complete
|
||||
the verification.
|
||||
|
||||
The wire protocol looks like the following between Alice and Bob's
|
||||
devices:
|
||||
|
@ -865,6 +941,13 @@ example, if Alice and Bob verify each other using SAS, Alice's
|
|||
`mac` property. Servers therefore must ensure that device IDs will not
|
||||
collide with cross-signing public keys.
|
||||
|
||||
The cross-signing private keys can be stored on the server or shared with other
|
||||
devices using the [Secrets](#secrets) module. When doing so, the master,
|
||||
user-signing, and self-signing keys are identified using the names
|
||||
`m.cross_signing.master`, `m.cross_signing.user_signing`, and
|
||||
`m.cross_signing.self_signing`, respectively, and the keys are base64-encoded
|
||||
before being encrypted.
|
||||
|
||||
###### Key and signature security
|
||||
|
||||
A user's master key could allow an attacker to impersonate that user to
|
||||
|
@ -907,6 +990,135 @@ user-signing keys.
|
|||
|
||||
{{% http-api spec="client-server" api="cross_signing" %}}
|
||||
|
||||
##### QR codes
|
||||
|
||||
Verifying by QR codes provides a quick way to verify when one of the parties
|
||||
has a device capable of scanning a QR code. The QR code encodes both parties'
|
||||
master signing keys as well as a random shared secret that is used to allow
|
||||
bi-directional verification from a single scan.
|
||||
|
||||
To advertise the ability to show a QR code, clients use the names
|
||||
`m.qr_code.show.v1` and `m.reciprocate.v1` in the `methods` fields of the
|
||||
`m.key.verification.request` and `m.key.verification.ready` events. To
|
||||
advertise the ability to scan a QR code, clients use the names
|
||||
`m.qr_code.scan.v1` and `m.reciprocate.v1` in the `methods` fields of the
|
||||
`m.key.verification.request` and `m.key.verification.ready` events.
|
||||
Clients that support both showing and scanning QR codes would advertise
|
||||
`m.qr_code.show.v1`, `m.qr_code.scan.v1`, and `m.reciprocate.v1` as
|
||||
methods.
|
||||
|
||||
The process between Alice and Bob verifying each other would be:
|
||||
|
||||
1. Alice and Bob meet in person, and want to verify each other's keys.
|
||||
2. Alice and Bob begin a key verification using the key verification
|
||||
framework as described above.
|
||||
3. Alice's client displays a QR code that Bob is able to scan if Bob's client
|
||||
indicated the ability to scan, an option to scan Bob's QR code if her client
|
||||
is able to scan. Bob's client prompts displays a QR code that Alice can
|
||||
scan if Alice's client indicated the ability to scan, and an option to scan
|
||||
Alice's QR code if his client is able to scan. The format for the QR code
|
||||
is described below. Other options, like starting SAS Emoji verification,
|
||||
can be presented alongside the QR code if the devices have appropriate
|
||||
support.
|
||||
5. Alice scans Bob's QR code.
|
||||
6. Alice's device ensures that the keys encoded in the QR code match the
|
||||
expected values for the keys. If not, Alice's device displays an error
|
||||
message indicating that the code is incorrect, and sends a
|
||||
`m.key.verification.cancel` message to Bob's device.
|
||||
|
||||
Otherwise, at this point:
|
||||
- Alice's device has now verified Bob's key, and
|
||||
- Alice's device knows that Bob has the correct key for her.
|
||||
|
||||
Thus for Bob to verify Alice's key, Alice needs to tell Bob that he has the
|
||||
right key.
|
||||
7. Alice's device displays a message saying that the verification was
|
||||
successful because the QR code's keys will have matched the keys
|
||||
expected for Bob. Bob's device hasn't had a chance to verify Alice's
|
||||
keys yet so wouldn't show the same message. Bob will know that
|
||||
he has the right key for Alice because Alice's device will have shown
|
||||
this message, as otherwise the verification would be cancelled.
|
||||
8. Alice's device sends an `m.key.verification.start` message with `method` set
|
||||
to `m.reciprocate.v1` to Bob (see below). The message includes the shared
|
||||
secret from the QR code. This signals to Bob's device that Alice has
|
||||
scanned Bob's QR code.
|
||||
|
||||
This message is merely a signal for Bob's device to proceed to the next
|
||||
step, and is not used for verification purposes.
|
||||
9. Upon receipt of the `m.key.verification.start` message, Bob's device ensures
|
||||
that the shared secret matches.
|
||||
|
||||
If the shared secret does not match, it should display an error message
|
||||
indicating that an attack was attempted. (This does not affect Alice's
|
||||
verification of Bob's keys.)
|
||||
|
||||
If the shared secret does match, it asks Bob to confirm that Alice
|
||||
has scanned the QR code.
|
||||
10. Bob sees Alice's device confirm that the key matches, and presses the button
|
||||
on his device to indicate that Alice's key is verified.
|
||||
|
||||
Bob's verification of Alice's key hinges on Alice telling Bob the result of
|
||||
her scan. Since the QR code includes what Bob thinks Alice's key is,
|
||||
Alice's device can check whether Bob has the right key for her. Alice has
|
||||
no motivation to lie about the result, as getting Bob to trust an incorrect
|
||||
key would only affect communications between herself and Bob. Thus Alice
|
||||
telling Bob that the code was scanned successfully is sufficient for Bob to
|
||||
trust Alice's key, under the assumption that this communication is done
|
||||
over a trusted medium (such as in-person).
|
||||
11. Both devices send an `m.key.verification.done` message.
|
||||
|
||||
###### QR code format
|
||||
|
||||
The QR codes to be displayed and scanned using this format will encode binary
|
||||
strings in the general form:
|
||||
|
||||
- the ASCII string `MATRIX`
|
||||
- one byte indicating the QR code version (must be `0x02`)
|
||||
- one byte indicating the QR code verification mode. Should be one of the
|
||||
following values:
|
||||
- `0x00` verifying another user with cross-signing
|
||||
- `0x01` self-verifying in which the current device does trust the master key
|
||||
- `0x02` self-verifying in which the current device does not yet trust the
|
||||
master key
|
||||
- the event ID or `transaction_id` of the associated verification
|
||||
request event, encoded as:
|
||||
- two bytes in network byte order (big-endian) indicating the length in
|
||||
bytes of the ID as a UTF-8 string
|
||||
- the ID as a UTF-8 string
|
||||
- the first key, as 32 bytes. The key to use depends on the mode field:
|
||||
- if `0x00` or `0x01`, then the current user's own master cross-signing public key
|
||||
- if `0x02`, then the current device's device key
|
||||
- the second key, as 32 bytes. The key to use depends on the mode field:
|
||||
- if `0x00`, then what the device thinks the other user's master
|
||||
cross-signing key is
|
||||
- if `0x01`, then what the device thinks the other device's device key is
|
||||
- if `0x02`, then what the device thinks the user's master cross-signing key
|
||||
is
|
||||
- a random shared secret, as a byte string. It is suggested to use a secret
|
||||
that is about 8 bytes long. Note: as we do not share the length of the
|
||||
secret, and it is not a fixed size, clients will just use the remainder of
|
||||
binary string as the shared secret.
|
||||
|
||||
For example, if Alice displays a QR code encoding the following binary string:
|
||||
|
||||
```
|
||||
"MATRIX" |ver|mode| len | event ID
|
||||
4D 41 54 52 49 58 02 00 00 2D 21 41 42 43 44 ...
|
||||
| user's cross-signing key | other user's cross-signing key | shared secret
|
||||
00 01 02 03 04 05 06 07 ... 10 11 12 13 14 15 16 17 ... 20 21 22 23 24 25 26 27
|
||||
```
|
||||
|
||||
this indicates that Alice is verifying another user (say Bob), in response to
|
||||
the request from event "$ABCD...", her cross-signing key is
|
||||
`0001020304050607...` (which is "AAECAwQFBg..." in base64), she thinks that
|
||||
Bob's cross-signing key is `1011121314151617...` (which is "EBESExQVFh..." in
|
||||
base64), and the shared secret is `2021222324252627` (which is "ICEiIyQlJic" in
|
||||
base64).
|
||||
|
||||
###### Verification messages specific to QR codes
|
||||
|
||||
{{% event event="m.key.verification.start$m.reciprocate.v1" %}}
|
||||
|
||||
#### Sharing keys between devices
|
||||
|
||||
If Bob has an encrypted conversation with Alice on his computer, and
|
||||
|
@ -1004,6 +1216,11 @@ as follows:
|
|||
When reading in a recovery key, clients must disregard whitespace, and
|
||||
perform the reverse of steps 1 through 3.
|
||||
|
||||
The recovery key can also be stored on the server or shared with other devices
|
||||
using the [Secrets](#secrets) module. When doing so, it is identified using the
|
||||
name `m.megolm_backup.v1`, and the key is base64-encoded before being
|
||||
encrypted.
|
||||
|
||||
###### Backup algorithm: `m.megolm_backup.v1.curve25519-aes-sha2`
|
||||
|
||||
When a backup is created with the `algorithm` set to
|
||||
|
|
|
@ -53,7 +53,7 @@ tags to permit, denying the use and rendering of anything else, is:
|
|||
`font`, `del`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `blockquote`, `p`,
|
||||
`a`, `ul`, `ol`, `sup`, `sub`, `li`, `b`, `i`, `u`, `strong`, `em`,
|
||||
`strike`, `code`, `hr`, `br`, `div`, `table`, `thead`, `tbody`, `tr`,
|
||||
`th`, `td`, `caption`, `pre`, `span`, `img`.
|
||||
`th`, `td`, `caption`, `pre`, `span`, `img`, `details`, `summary`.
|
||||
|
||||
Not all attributes on those tags should be permitted as they may be
|
||||
avenues for other disruption attempts, such as adding `onclick` handlers
|
||||
|
@ -63,10 +63,11 @@ are listed, clients should translate the value (a 6-character hex color
|
|||
code) to the appropriate CSS/attributes for the tag.
|
||||
|
||||
`font`
|
||||
`data-mx-bg-color`, `data-mx-color`
|
||||
`data-mx-bg-color`, `data-mx-color`, `color`
|
||||
|
||||
`span`
|
||||
`data-mx-bg-color`, `data-mx-color`
|
||||
`data-mx-bg-color`, `data-mx-color`, `data-mx-spoiler` (see
|
||||
[spoiler messages](#spoiler-messages))
|
||||
|
||||
`a`
|
||||
`name`, `target`, `href` (provided the value is not relative and has a
|
||||
|
@ -461,6 +462,52 @@ For `m.image`, the text should be `"sent an image."`. For `m.video`, the
|
|||
text should be `"sent a video."`. For `m.audio`, the text should be
|
||||
`"sent an audio file"`.
|
||||
|
||||
##### Spoiler messages
|
||||
|
||||
Parts of a message can be hidden visually from the user through use of spoilers.
|
||||
This does not affect the server's representation of the event content - it
|
||||
is simply a visual cue to the user that the message may reveal important
|
||||
information about something, spoiling any relevant surprise.
|
||||
|
||||
To send spoilers clients MUST use the `formatted_body` and therefore the
|
||||
`org.matrix.custom.html` format, described above. This makes spoilers valid on
|
||||
any `msgtype` which can support this format appropriately.
|
||||
|
||||
Spoilers themselves are contained with `span` tags, with the reason (optionally)
|
||||
being in the `data-mx-spoiler` attribute. Spoilers without a reason must at least
|
||||
specify the attribute, though the value may be empty/undefined.
|
||||
|
||||
An example of a spoiler is:
|
||||
|
||||
```json
|
||||
{
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"body": "Alice [Spoiler](mxc://example.org/abc123) in the movie.",
|
||||
"formatted_body": "Alice <span data-mx-spoiler>lived happily ever after</span> in the movie."
|
||||
}
|
||||
```
|
||||
|
||||
If a reason were to be supplied, it would look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"body": "Alice [Spoiler for health of Alice](mxc://example.org/abc123) in the movie.",
|
||||
"formatted_body": "Alice <span data-mx-spoiler='health of alice'>lived happily ever after</span> in the movie."
|
||||
}
|
||||
```
|
||||
|
||||
When sending a spoiler, clients SHOULD provide the plain text fallback in the `body`
|
||||
as shown above (including the reason). The fallback SHOULD omit the spoiler text verbatim
|
||||
since `body` might show up in text-only clients or in notifications. To prevent spoilers
|
||||
showing up in such situations, clients are strongly encouraged to first upload the plaintext
|
||||
to the media repository then reference the MXC URI in a markdown-style link, as shown above.
|
||||
|
||||
Clients SHOULD render spoilers differently with some sort of disclosure. For example, the
|
||||
client could blur the actual text and ask the user to click on it for it to be revealed.
|
||||
|
||||
#### Server behaviour
|
||||
|
||||
Homeservers SHOULD reject `m.room.message` events which don't have a
|
||||
|
|
|
@ -42,7 +42,7 @@ passphrases](#deriving-keys-from-passphrases).
|
|||
|
||||
| Parameter | Type | Description
|
||||
|------------|-----------|-------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| name | string | **Required.** The name of the key. |
|
||||
| name | string | Optional. The name of the key. If not given, the client may use a generic name such as "Unnamed key", or "Default key" if the key is marked as the default key (see below). |
|
||||
| algorithm | string | **Required.** The encryption algorithm to be used for this key. Currently, only `m.secret_storage.v1.aes-hmac-sha2` is supported. |
|
||||
| passphrase | string | See [deriving keys from passphrases](#deriving-keys-from-passphrases) section for a description of this property. |
|
||||
|
||||
|
@ -56,6 +56,18 @@ will be used to encrypt all secrets that the user would expect to be
|
|||
available on all their clients. Unless the user specifies otherwise,
|
||||
clients will try to use the default key to decrypt secrets.
|
||||
|
||||
Clients that want to present a simplified interface to users by not supporting
|
||||
multiple keys should use the default key if one is specified. If not default
|
||||
key is specified, the client may behave as if there is no key is present at
|
||||
all. When such a client creates a key, it should mark that key as being the
|
||||
default key.
|
||||
|
||||
`DefaultKey`
|
||||
|
||||
| Parameter | Type | Description
|
||||
|------------|-----------|------------------------------------------|
|
||||
| key | string | **Required.** The ID of the default key. |
|
||||
|
||||
##### Secret storage
|
||||
|
||||
Encrypted data is stored in the user's account\_data using the event
|
||||
|
@ -118,6 +130,16 @@ and the key descriptions for the keys would be:
|
|||
}
|
||||
```
|
||||
|
||||
If `key_id_1` is the default key, then we also have:
|
||||
|
||||
`m.secret_storage.default_key`:
|
||||
|
||||
```
|
||||
{
|
||||
"key": "key_id_1"
|
||||
}
|
||||
```
|
||||
|
||||
###### `m.secret_storage.v1.aes-hmac-sha2`
|
||||
|
||||
Secrets encrypted using the `m.secret_storage.v1.aes-hmac-sha2`
|
||||
|
|
|
@ -10,3 +10,4 @@ weight: 60
|
|||
* [Room Version 4](v4)
|
||||
* [Room Version 5](v5)
|
||||
* [Room Version 6](v6)
|
||||
* [Room Version 7](v7)
|
||||
|
|
52
content/rooms/v7.md
Normal file
52
content/rooms/v7.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Room Version 7
|
||||
type: docs
|
||||
weight: 60
|
||||
---
|
||||
|
||||
This room version builds on [version 6](/rooms/v6) to introduce knocking
|
||||
as a possible join rule and membership state.
|
||||
|
||||
## Client considerations
|
||||
|
||||
This is the first room version to support knocking completely. As such,
|
||||
users will not be able to knock on rooms which are not based off v7.
|
||||
|
||||
## Server implementation components
|
||||
|
||||
{{% boxes/warning %}}
|
||||
The information contained in this section is strictly for server
|
||||
implementors. Applications which use the Client-Server API are generally
|
||||
unaffected by the intricacies contained here. The section above
|
||||
regarding client considerations is the resource that Client-Server API
|
||||
use cases should reference.
|
||||
{{% /boxes/warning %}}
|
||||
|
||||
Room version 7 adds new authorization rules for events to support knocking.
|
||||
[Room version 6](/rooms/v6) has details of other authorization rule changes,
|
||||
as do the versions v6 is based upon.
|
||||
|
||||
For checks perfomed upon `m.room.member` events, the following conditions
|
||||
are added in context:
|
||||
|
||||
If type is `m.room.member`:
|
||||
|
||||
...
|
||||
|
||||
* If `membership` is `ban`:
|
||||
|
||||
...
|
||||
|
||||
* If `membership` is `knock`:
|
||||
|
||||
i. If the `join_rule` is anything other than `knock`, reject.
|
||||
|
||||
ii. If `sender` does not match `state_key`, reject.
|
||||
|
||||
iii. If the `sender`'s current membership is not `ban`, `invite`, or `join`, allow.
|
||||
|
||||
iv. Otherwise, reject.
|
||||
|
||||
...
|
||||
|
||||
The remaining rules are the same as in [room version 6](/rooms/v6#authorization-rules-for-events).
|
|
@ -720,6 +720,24 @@ other servers participating in the room.
|
|||
|
||||
{{% http-api spec="server-server" api="joins-v2" %}}
|
||||
|
||||
## Knocking upon a room
|
||||
|
||||
Rooms can permit knocking through the join rules, and if permitted this
|
||||
gives users a way to request to join (be invited) to the room. Users who
|
||||
knock on a room where the server is already a resident of the room can
|
||||
just send the knock event directly without using this process, however
|
||||
much like [joining rooms](/server-server-api/#joining-rooms) the server
|
||||
must handshake their way into having the knock sent on its behalf.
|
||||
|
||||
The handshake is largely the same as the joining rooms handshake, where
|
||||
instead of a "joining server" there is a "knocking server", and the APIs
|
||||
to be called are different (`/make_knock` and `/send_knock`).
|
||||
|
||||
Servers can retract knocks over federation by leaving the room, as described
|
||||
below for rejecting invites.
|
||||
|
||||
{{% http-api spec="server-server" api="knocks" %}}
|
||||
|
||||
## Inviting to a room
|
||||
|
||||
When a user on a given homeserver invites another user on the same
|
||||
|
@ -728,6 +746,10 @@ the process defined here. However, when a user invites another user on a
|
|||
different homeserver, a request to that homeserver to have the event
|
||||
signed and verified must be made.
|
||||
|
||||
Note that invites are used to indicate that knocks were accepted. As such,
|
||||
receiving servers should be prepared to manually link up a previous knock
|
||||
to an invite if the invite event does not directly reference the knock.
|
||||
|
||||
{{% http-api spec="server-server" api="invites-v1" %}}
|
||||
|
||||
{{% http-api spec="server-server" api="invites-v2" %}}
|
||||
|
@ -735,10 +757,10 @@ signed and verified must be made.
|
|||
## Leaving Rooms (Rejecting Invites)
|
||||
|
||||
Normally homeservers can send appropriate `m.room.member` events to have
|
||||
users leave the room, or to reject local invites. Remote invites from
|
||||
other homeservers do not involve the server in the graph and therefore
|
||||
need another approach to reject the invite. Joining the room and
|
||||
promptly leaving is not recommended as clients and servers will
|
||||
users leave the room, to reject local invites, or to retract a knock.
|
||||
Remote invites/knocks from other homeservers do not involve the server in the
|
||||
graph and therefore need another approach to reject the invite. Joining
|
||||
the room and promptly leaving is not recommended as clients and servers will
|
||||
interpret that as accepting the invite, then leaving the room rather
|
||||
than rejecting the invite.
|
||||
|
||||
|
@ -1009,6 +1031,8 @@ The following endpoint prefixes MUST be protected:
|
|||
- `/_matrix/federation/v2/send_leave`
|
||||
- `/_matrix/federation/v1/invite`
|
||||
- `/_matrix/federation/v2/invite`
|
||||
- `/_matrix/federation/v1/make_knock`
|
||||
- `/_matrix/federation/v1/send_knock`
|
||||
- `/_matrix/federation/v1/state`
|
||||
- `/_matrix/federation/v1/state_ids`
|
||||
- `/_matrix/federation/v1/backfill`
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"Pizza": "Pizza",
|
||||
"Cake": "Gâteau",
|
||||
"Heart": "Cœur",
|
||||
"Hat": "Châpeau",
|
||||
"Hat": "Chapeau",
|
||||
"Glasses": "Lunettes",
|
||||
"Hourglass": "Sablier",
|
||||
"Book": "Livre",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"Robot": "Robot",
|
||||
"Spanner": "Clé à molette",
|
||||
"Santa": "Père Noël",
|
||||
"Thumbs Up": "Pouce en l'air",
|
||||
"Thumbs Up": "Pouce en l’air",
|
||||
"Umbrella": "Parapluie",
|
||||
"Clock": "Réveil",
|
||||
"Gift": "Cadeau",
|
||||
|
|
28
data-definitions/sas-emoji-v1-i18n/tzm.json
Normal file
28
data-definitions/sas-emoji-v1-i18n/tzm.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"Folder": "Asdaw",
|
||||
"Guitar": "Agiṭaṛ",
|
||||
"Ball": "Tcama",
|
||||
"Flag": "Acenyal",
|
||||
"Telephone": "Atilifun",
|
||||
"Key": "Tasarut",
|
||||
"Book": "Adlis",
|
||||
"Hat": "Taraza",
|
||||
"Robot": "Aṛubu",
|
||||
"Heart": "Ul",
|
||||
"Apple": "Tadeffuyt",
|
||||
"Banana": "Tabanant",
|
||||
"Fire": "Timessi",
|
||||
"Moon": "Ayyur",
|
||||
"Mushroom": "Agursel",
|
||||
"Tree": "Aseklu",
|
||||
"Fish": "Aselm",
|
||||
"Turtle": "Ifker",
|
||||
"Rooster": "Ayaẓiḍ",
|
||||
"Rabbit": "Agnin",
|
||||
"Elephant": "Ilu",
|
||||
"Pig": "Ilef",
|
||||
"Horse": "Ayyis",
|
||||
"Lion": "Izem",
|
||||
"Cat": "Amuc",
|
||||
"Dog": "Aydi"
|
||||
}
|
|
@ -27,6 +27,7 @@
|
|||
"sr": "пас",
|
||||
"sv": "Hund",
|
||||
"szl": null,
|
||||
"tzm": "Aydi",
|
||||
"uk": "Пес",
|
||||
"zh_Hans": "狗"
|
||||
}
|
||||
|
@ -59,6 +60,7 @@
|
|||
"sr": "мачка",
|
||||
"sv": "Katt",
|
||||
"szl": null,
|
||||
"tzm": "Amuc",
|
||||
"uk": "Кіт",
|
||||
"zh_Hans": "猫"
|
||||
}
|
||||
|
@ -91,6 +93,7 @@
|
|||
"sr": "лав",
|
||||
"sv": "Lejon",
|
||||
"szl": null,
|
||||
"tzm": "Izem",
|
||||
"uk": "Лев",
|
||||
"zh_Hans": "狮子"
|
||||
}
|
||||
|
@ -123,6 +126,7 @@
|
|||
"sr": "коњ",
|
||||
"sv": "Häst",
|
||||
"szl": null,
|
||||
"tzm": "Ayyis",
|
||||
"uk": "Кінь",
|
||||
"zh_Hans": "马"
|
||||
}
|
||||
|
@ -155,6 +159,7 @@
|
|||
"sr": "једнорог",
|
||||
"sv": "Enhörning",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Єдиноріг",
|
||||
"zh_Hans": "独角兽"
|
||||
}
|
||||
|
@ -187,6 +192,7 @@
|
|||
"sr": "прасе",
|
||||
"sv": "Gris",
|
||||
"szl": null,
|
||||
"tzm": "Ilef",
|
||||
"uk": "Свиня",
|
||||
"zh_Hans": "猪"
|
||||
}
|
||||
|
@ -219,6 +225,7 @@
|
|||
"sr": "слон",
|
||||
"sv": "Elefant",
|
||||
"szl": null,
|
||||
"tzm": "Ilu",
|
||||
"uk": "Слон",
|
||||
"zh_Hans": "大象"
|
||||
}
|
||||
|
@ -251,6 +258,7 @@
|
|||
"sr": "зец",
|
||||
"sv": "Kanin",
|
||||
"szl": null,
|
||||
"tzm": "Agnin",
|
||||
"uk": "Кріль",
|
||||
"zh_Hans": "兔子"
|
||||
}
|
||||
|
@ -283,6 +291,7 @@
|
|||
"sr": "панда",
|
||||
"sv": "Panda",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Панда",
|
||||
"zh_Hans": "熊猫"
|
||||
}
|
||||
|
@ -315,6 +324,7 @@
|
|||
"sr": "петао",
|
||||
"sv": "Tupp",
|
||||
"szl": null,
|
||||
"tzm": "Ayaẓiḍ",
|
||||
"uk": "Когут",
|
||||
"zh_Hans": "公鸡"
|
||||
}
|
||||
|
@ -347,6 +357,7 @@
|
|||
"sr": "пингвин",
|
||||
"sv": "Pingvin",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Пінгвін",
|
||||
"zh_Hans": "企鹅"
|
||||
}
|
||||
|
@ -379,6 +390,7 @@
|
|||
"sr": "корњача",
|
||||
"sv": "Sköldpadda",
|
||||
"szl": null,
|
||||
"tzm": "Ifker",
|
||||
"uk": "Черепаха",
|
||||
"zh_Hans": "乌龟"
|
||||
}
|
||||
|
@ -411,6 +423,7 @@
|
|||
"sr": "риба",
|
||||
"sv": "Fisk",
|
||||
"szl": null,
|
||||
"tzm": "Aselm",
|
||||
"uk": "Риба",
|
||||
"zh_Hans": "鱼"
|
||||
}
|
||||
|
@ -443,6 +456,7 @@
|
|||
"sr": "октопод",
|
||||
"sv": "Bläckfisk",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Восьминіг",
|
||||
"zh_Hans": "章鱼"
|
||||
}
|
||||
|
@ -475,6 +489,7 @@
|
|||
"sr": "лептир",
|
||||
"sv": "Fjäril",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Метелик",
|
||||
"zh_Hans": "蝴蝶"
|
||||
}
|
||||
|
@ -507,6 +522,7 @@
|
|||
"sr": "цвет",
|
||||
"sv": "Blomma",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Квітка",
|
||||
"zh_Hans": "花"
|
||||
}
|
||||
|
@ -539,6 +555,7 @@
|
|||
"sr": "дрво",
|
||||
"sv": "Träd",
|
||||
"szl": null,
|
||||
"tzm": "Aseklu",
|
||||
"uk": "Дерево",
|
||||
"zh_Hans": "树"
|
||||
}
|
||||
|
@ -571,6 +588,7 @@
|
|||
"sr": "кактус",
|
||||
"sv": "Kaktus",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Кактус",
|
||||
"zh_Hans": "仙人掌"
|
||||
}
|
||||
|
@ -603,6 +621,7 @@
|
|||
"sr": "печурка",
|
||||
"sv": "Svamp",
|
||||
"szl": null,
|
||||
"tzm": "Agursel",
|
||||
"uk": "Гриб",
|
||||
"zh_Hans": "蘑菇"
|
||||
}
|
||||
|
@ -635,6 +654,7 @@
|
|||
"sr": "глобус",
|
||||
"sv": "Jordklot",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Глобус",
|
||||
"zh_Hans": "地球"
|
||||
}
|
||||
|
@ -667,6 +687,7 @@
|
|||
"sr": "месец",
|
||||
"sv": "Måne",
|
||||
"szl": null,
|
||||
"tzm": "Ayyur",
|
||||
"uk": "Місяць",
|
||||
"zh_Hans": "月亮"
|
||||
}
|
||||
|
@ -699,6 +720,7 @@
|
|||
"sr": "облак",
|
||||
"sv": "Moln",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Хмара",
|
||||
"zh_Hans": "云"
|
||||
}
|
||||
|
@ -731,6 +753,7 @@
|
|||
"sr": "ватра",
|
||||
"sv": "Eld",
|
||||
"szl": null,
|
||||
"tzm": "Timessi",
|
||||
"uk": "Вогонь",
|
||||
"zh_Hans": "火"
|
||||
}
|
||||
|
@ -763,6 +786,7 @@
|
|||
"sr": "банана",
|
||||
"sv": "Banan",
|
||||
"szl": null,
|
||||
"tzm": "Tabanant",
|
||||
"uk": "Банан",
|
||||
"zh_Hans": "香蕉"
|
||||
}
|
||||
|
@ -795,6 +819,7 @@
|
|||
"sr": "јабука",
|
||||
"sv": "Äpple",
|
||||
"szl": null,
|
||||
"tzm": "Tadeffuyt",
|
||||
"uk": "Яблуко",
|
||||
"zh_Hans": "苹果"
|
||||
}
|
||||
|
@ -827,6 +852,7 @@
|
|||
"sr": "јагода",
|
||||
"sv": "Jordgubbe",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Полуниця",
|
||||
"zh_Hans": "草莓"
|
||||
}
|
||||
|
@ -859,6 +885,7 @@
|
|||
"sr": "кукуруз",
|
||||
"sv": "Majs",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Кукурудза",
|
||||
"zh_Hans": "玉米"
|
||||
}
|
||||
|
@ -891,6 +918,7 @@
|
|||
"sr": "пица",
|
||||
"sv": "Pizza",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Піца",
|
||||
"zh_Hans": "披萨"
|
||||
}
|
||||
|
@ -923,6 +951,7 @@
|
|||
"sr": "торта",
|
||||
"sv": "Tårta",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Пиріг",
|
||||
"zh_Hans": "蛋糕"
|
||||
}
|
||||
|
@ -955,6 +984,7 @@
|
|||
"sr": "срце",
|
||||
"sv": "Hjärta",
|
||||
"szl": null,
|
||||
"tzm": "Ul",
|
||||
"uk": "Серце",
|
||||
"zh_Hans": "心"
|
||||
}
|
||||
|
@ -987,6 +1017,7 @@
|
|||
"sr": "смајли",
|
||||
"sv": "Smiley",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Посмішка",
|
||||
"zh_Hans": "笑脸"
|
||||
}
|
||||
|
@ -1019,6 +1050,7 @@
|
|||
"sr": "робот",
|
||||
"sv": "Robot",
|
||||
"szl": null,
|
||||
"tzm": "Aṛubu",
|
||||
"uk": "Робот",
|
||||
"zh_Hans": "机器人"
|
||||
}
|
||||
|
@ -1038,7 +1070,7 @@
|
|||
"es": "Sombrero",
|
||||
"et": "Kübar",
|
||||
"fi": "Hattu",
|
||||
"fr": "Châpeau",
|
||||
"fr": "Chapeau",
|
||||
"hr": "kapa",
|
||||
"it": "Cappello",
|
||||
"ja": "帽子",
|
||||
|
@ -1051,6 +1083,7 @@
|
|||
"sr": "шешир",
|
||||
"sv": "Hatt",
|
||||
"szl": null,
|
||||
"tzm": "Taraza",
|
||||
"uk": "Капелюх",
|
||||
"zh_Hans": "帽子"
|
||||
}
|
||||
|
@ -1083,6 +1116,7 @@
|
|||
"sr": "наочаре",
|
||||
"sv": "Glasögon",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Окуляри",
|
||||
"zh_Hans": "眼镜"
|
||||
}
|
||||
|
@ -1115,6 +1149,7 @@
|
|||
"sr": "кључ",
|
||||
"sv": "Skruvnyckel",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Гайковий ключ",
|
||||
"zh_Hans": "扳手"
|
||||
}
|
||||
|
@ -1147,6 +1182,7 @@
|
|||
"sr": "деда Мраз",
|
||||
"sv": "Tomte",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Санта Клаус",
|
||||
"zh_Hans": "圣诞老人"
|
||||
}
|
||||
|
@ -1166,7 +1202,7 @@
|
|||
"es": "Pulgar arriba",
|
||||
"et": "Pöidlad püsti",
|
||||
"fi": "Peukalo ylös",
|
||||
"fr": "Pouce en l'air",
|
||||
"fr": "Pouce en l’air",
|
||||
"hr": "palac gore",
|
||||
"it": "Pollice alzato",
|
||||
"ja": "いいね",
|
||||
|
@ -1179,6 +1215,7 @@
|
|||
"sr": "палчић горе",
|
||||
"sv": "Tummen upp",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Великий палець вгору",
|
||||
"zh_Hans": "赞"
|
||||
}
|
||||
|
@ -1211,6 +1248,7 @@
|
|||
"sr": "кишобран",
|
||||
"sv": "Paraply",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Парасолька",
|
||||
"zh_Hans": "伞"
|
||||
}
|
||||
|
@ -1243,6 +1281,7 @@
|
|||
"sr": "пешчаник",
|
||||
"sv": "Timglas",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Пісковий годинник",
|
||||
"zh_Hans": "沙漏"
|
||||
}
|
||||
|
@ -1275,6 +1314,7 @@
|
|||
"sr": "сат",
|
||||
"sv": "Klocka",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Годинник",
|
||||
"zh_Hans": "时钟"
|
||||
}
|
||||
|
@ -1307,6 +1347,7 @@
|
|||
"sr": "поклон",
|
||||
"sv": "Present",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Подарунок",
|
||||
"zh_Hans": "礼物"
|
||||
}
|
||||
|
@ -1339,6 +1380,7 @@
|
|||
"sr": "сијалица",
|
||||
"sv": "Lampa",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Лампочка",
|
||||
"zh_Hans": "灯泡"
|
||||
}
|
||||
|
@ -1371,6 +1413,7 @@
|
|||
"sr": "књига",
|
||||
"sv": "Bok",
|
||||
"szl": null,
|
||||
"tzm": "Adlis",
|
||||
"uk": "Книга",
|
||||
"zh_Hans": "书"
|
||||
}
|
||||
|
@ -1403,6 +1446,7 @@
|
|||
"sr": "оловка",
|
||||
"sv": "Penna",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Олівець",
|
||||
"zh_Hans": "铅笔"
|
||||
}
|
||||
|
@ -1435,6 +1479,7 @@
|
|||
"sr": "спајалица",
|
||||
"sv": "Gem",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Спиначка",
|
||||
"zh_Hans": "回形针"
|
||||
}
|
||||
|
@ -1467,6 +1512,7 @@
|
|||
"sr": "маказе",
|
||||
"sv": "Sax",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Ножиці",
|
||||
"zh_Hans": "剪刀"
|
||||
}
|
||||
|
@ -1499,6 +1545,7 @@
|
|||
"sr": "катанац",
|
||||
"sv": "Lås",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Замок",
|
||||
"zh_Hans": "锁"
|
||||
}
|
||||
|
@ -1531,6 +1578,7 @@
|
|||
"sr": "кључ",
|
||||
"sv": "Nyckel",
|
||||
"szl": null,
|
||||
"tzm": "Tasarut",
|
||||
"uk": "Ключ",
|
||||
"zh_Hans": "钥匙"
|
||||
}
|
||||
|
@ -1563,6 +1611,7 @@
|
|||
"sr": "чекић",
|
||||
"sv": "Hammare",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Молоток",
|
||||
"zh_Hans": "锤子"
|
||||
}
|
||||
|
@ -1595,6 +1644,7 @@
|
|||
"sr": "телефон",
|
||||
"sv": "Telefon",
|
||||
"szl": null,
|
||||
"tzm": "Atilifun",
|
||||
"uk": "Телефон",
|
||||
"zh_Hans": "电话"
|
||||
}
|
||||
|
@ -1627,6 +1677,7 @@
|
|||
"sr": "застава",
|
||||
"sv": "Flagga",
|
||||
"szl": null,
|
||||
"tzm": "Acenyal",
|
||||
"uk": "Прапор",
|
||||
"zh_Hans": "旗帜"
|
||||
}
|
||||
|
@ -1659,6 +1710,7 @@
|
|||
"sr": "воз",
|
||||
"sv": "Tåg",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Потяг",
|
||||
"zh_Hans": "火车"
|
||||
}
|
||||
|
@ -1691,6 +1743,7 @@
|
|||
"sr": "бицикл",
|
||||
"sv": "Cykel",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Велосипед",
|
||||
"zh_Hans": "自行车"
|
||||
}
|
||||
|
@ -1723,6 +1776,7 @@
|
|||
"sr": "авион",
|
||||
"sv": "Flygplan",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Літак",
|
||||
"zh_Hans": "飞机"
|
||||
}
|
||||
|
@ -1755,6 +1809,7 @@
|
|||
"sr": "ракета",
|
||||
"sv": "Raket",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Ракета",
|
||||
"zh_Hans": "火箭"
|
||||
}
|
||||
|
@ -1787,6 +1842,7 @@
|
|||
"sr": "пехар",
|
||||
"sv": "Trofé",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Приз",
|
||||
"zh_Hans": "奖杯"
|
||||
}
|
||||
|
@ -1819,6 +1875,7 @@
|
|||
"sr": "лопта",
|
||||
"sv": "Boll",
|
||||
"szl": null,
|
||||
"tzm": "Tcama",
|
||||
"uk": "М'яч",
|
||||
"zh_Hans": "球"
|
||||
}
|
||||
|
@ -1851,6 +1908,7 @@
|
|||
"sr": "гитара",
|
||||
"sv": "Gitarr",
|
||||
"szl": null,
|
||||
"tzm": "Agiṭaṛ",
|
||||
"uk": "Гітара",
|
||||
"zh_Hans": "吉他"
|
||||
}
|
||||
|
@ -1883,6 +1941,7 @@
|
|||
"sr": "труба",
|
||||
"sv": "Trumpet",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Труба",
|
||||
"zh_Hans": "喇叭"
|
||||
}
|
||||
|
@ -1915,6 +1974,7 @@
|
|||
"sr": "звоно",
|
||||
"sv": "Bjällra",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Дзвін",
|
||||
"zh_Hans": "铃铛"
|
||||
}
|
||||
|
@ -1947,6 +2007,7 @@
|
|||
"sr": "сидро",
|
||||
"sv": "Ankare",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Якір",
|
||||
"zh_Hans": "锚"
|
||||
}
|
||||
|
@ -1979,6 +2040,7 @@
|
|||
"sr": "слушалице",
|
||||
"sv": "Hörlurar",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Навушники",
|
||||
"zh_Hans": "耳机"
|
||||
}
|
||||
|
@ -2011,6 +2073,7 @@
|
|||
"sr": "фасцикла",
|
||||
"sv": "Mapp",
|
||||
"szl": null,
|
||||
"tzm": "Asdaw",
|
||||
"uk": "Тека",
|
||||
"zh_Hans": "文件夹"
|
||||
}
|
||||
|
@ -2043,6 +2106,7 @@
|
|||
"sr": "чиода",
|
||||
"sv": "Häftstift",
|
||||
"szl": null,
|
||||
"tzm": null,
|
||||
"uk": "Кнопка",
|
||||
"zh_Hans": "图钉"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2018 New Vector Ltd
|
||||
# Copyright 2018, 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.
|
||||
|
@ -69,6 +69,13 @@ properties:
|
|||
avatar_url:
|
||||
type: string
|
||||
description: The URL for the room's avatar, if one is set.
|
||||
join_rule:
|
||||
type: string
|
||||
description: |-
|
||||
The room's join rule. When not present, the room is assumed to
|
||||
be `public`. Note that rooms with `invite` join rules are not
|
||||
expected here, but rooms with `knock` rules are given their
|
||||
near-public nature.
|
||||
next_batch:
|
||||
type: string
|
||||
description: |-
|
||||
|
@ -90,13 +97,14 @@ example: {
|
|||
"chunk": [
|
||||
{
|
||||
"aliases": ["#murrays:cheese.bar"],
|
||||
"avatar_url": "mxc://bleeker.street/CHEDDARandBRIE",
|
||||
"avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
|
||||
"guest_can_join": false,
|
||||
"name": "CHEESE",
|
||||
"num_joined_members": 37,
|
||||
"room_id": "!ol19s:bleecker.street",
|
||||
"topic": "Tasty tasty cheese",
|
||||
"world_readable": true
|
||||
"world_readable": true,
|
||||
"join_rule": "public"
|
||||
}
|
||||
],
|
||||
"next_batch": "p190q",
|
||||
|
|
124
data/api/client-server/knocking.yaml
Normal file
124
data/api/client-server/knocking.yaml
Normal file
|
@ -0,0 +1,124 @@
|
|||
# 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.
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server Room Knocking API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
$ref: definitions/security.yaml
|
||||
paths:
|
||||
"/knock/{roomIdOrAlias}":
|
||||
post:
|
||||
summary: Knock on a room, requesting permission to join.
|
||||
description: |-
|
||||
*Note that this API takes either a room ID or alias, unlike other membership APIs.*
|
||||
|
||||
This API "knocks" on the room to ask for permission to join, if the user
|
||||
is allowed to knock on the room. Acceptance of the knock happens out of
|
||||
band from this API, meaning that the client will have to watch for updates
|
||||
regarding the acceptance/rejection of the knock.
|
||||
|
||||
If the room history settings allow, the user will still be able to see
|
||||
history of the room while being in the "knock" state. The user will have
|
||||
to accept the invitation to join the room (acceptance of knock) to see
|
||||
messages reliably. See the `/join` endpoints for more information about
|
||||
history visibility to the user.
|
||||
|
||||
The knock will appear as an entry in the response of the
|
||||
[`/sync`](/client-server-api/#get_matrixclientr0sync) API.
|
||||
operationId: knockRoom
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomIdOrAlias
|
||||
description: The room identifier or alias to knock upon.
|
||||
required: true
|
||||
x-example: "#monkeys:matrix.org"
|
||||
- in: query
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
name: server_name
|
||||
description: |-
|
||||
The servers to attempt to knock on the room through. One of the servers
|
||||
must be participating in the room.
|
||||
x-example: ["matrix.org", "elsewhere.ca"]
|
||||
- in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
reason:
|
||||
type: string
|
||||
description: |-
|
||||
Optional reason to be included as the `reason` on the subsequent
|
||||
membership event.
|
||||
example: "Looking for support"
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
The room has been knocked upon.
|
||||
|
||||
The knocked room ID must be returned in the `room_id` field.
|
||||
examples:
|
||||
application/json: {
|
||||
"room_id": "!d41d8cd:matrix.org"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
room_id:
|
||||
type: string
|
||||
description: The knocked room ID.
|
||||
required: ["room_id"]
|
||||
403:
|
||||
description: |-
|
||||
You do not have permission to knock on the room. A meaningful `errcode`
|
||||
and description error text will be returned. Example reasons for rejection are:
|
||||
|
||||
- The room is not set up for knocking.
|
||||
- The user has been banned from the room.
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_FORBIDDEN", "error": "You are not allowed to knock on this room."
|
||||
}
|
||||
schema:
|
||||
"$ref": "definitions/errors/error.yaml"
|
||||
404:
|
||||
description: |-
|
||||
The room could not be found or resolved to a room ID.
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_NOT_FOUND", "error": "That room does not appear to exist."
|
||||
}
|
||||
schema:
|
||||
"$ref": "definitions/errors/error.yaml"
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/errors/rate_limited.yaml"
|
||||
tags:
|
||||
- Room membership
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2016 OpenMarket Ltd
|
||||
# Copyright 2016, 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.
|
||||
|
@ -281,6 +281,28 @@ paths:
|
|||
items:
|
||||
$ref: "../../event-schemas/schema/stripped_state.yaml"
|
||||
type: array
|
||||
knock:
|
||||
title: Knocked rooms
|
||||
type: object
|
||||
description: |-
|
||||
The rooms that the user has knocked upon, mapped as room ID to room information.
|
||||
additionalProperties:
|
||||
title: Knocked Room
|
||||
type: object
|
||||
properties:
|
||||
knock_state:
|
||||
title: KnockState
|
||||
type: object
|
||||
description: |-
|
||||
The state of a room that the user has knocked upon. The state
|
||||
events contained here have the same restrictions as `InviteState`
|
||||
above.
|
||||
properties:
|
||||
events:
|
||||
description: The StrippedState events that form the knock state.
|
||||
items:
|
||||
$ref: "../../event-schemas/schema/stripped_state.yaml"
|
||||
type: array
|
||||
leave:
|
||||
title: Left rooms
|
||||
type: object
|
||||
|
@ -436,6 +458,26 @@ paths:
|
|||
}
|
||||
}
|
||||
},
|
||||
"knock": {
|
||||
"!223asd456:example.com": {
|
||||
"invite_state": {
|
||||
"events": [
|
||||
{
|
||||
"sender": "@alice:example.com",
|
||||
"type": "m.room.name",
|
||||
"state_key": "",
|
||||
"content": {"name": "My Room Name"}
|
||||
},
|
||||
{
|
||||
"sender": "@bob:example.com",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@bob:example.com",
|
||||
"content": {"membership": "knock"}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"leave": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ allOf:
|
|||
sender:
|
||||
type: string
|
||||
description: User ID of the sender.
|
||||
example: "john@example.com"
|
||||
example: "@john:example.com"
|
||||
type:
|
||||
type: string
|
||||
description: Event type for the message.
|
||||
|
@ -59,7 +59,7 @@ allOf:
|
|||
title: Device Message Contents
|
||||
properties: {}
|
||||
example: {
|
||||
"alice@example.org": {
|
||||
"@alice:example.org": {
|
||||
"IWHQUZUIAH": {
|
||||
"algorithm": "m.megolm.v1.aes-sha2",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
|
|
322
data/api/server-server/knocks.yaml
Normal file
322
data/api/server-server/knocks.yaml
Normal file
|
@ -0,0 +1,322 @@
|
|||
# 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.
|
||||
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Federation Knock Room API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8448
|
||||
schemes:
|
||||
- https
|
||||
basePath: /_matrix/federation/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
$ref: definitions/security.yaml
|
||||
paths:
|
||||
"/make_knock/{roomId}/{userId}":
|
||||
get:
|
||||
summary: Get information required to make a knock event for a room.
|
||||
description: |-
|
||||
Asks the receiving server to return information that the sending
|
||||
server will need to prepare a knock event for the room.
|
||||
operationId: makeKnock
|
||||
security:
|
||||
- signedRequest: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: roomId
|
||||
type: string
|
||||
description: The room ID that is about to be knocked.
|
||||
required: true
|
||||
x-example: "!abc123:example.org"
|
||||
- in: path
|
||||
name: userId
|
||||
type: string
|
||||
description: The user ID the knock event will be for.
|
||||
required: true
|
||||
x-example: "@someone:example.org"
|
||||
- in: query
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
name: ver
|
||||
description: |-
|
||||
The room versions the sending server has support for.
|
||||
required: true # knocking was supported in v7
|
||||
x-example: ["1", "7"]
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
A template to be used for the rest of the [Knocking Rooms](/server-server-api/#knocking-rooms)
|
||||
handshake. Note that events have a different format depending on room version - check the
|
||||
[room version specification](/#room-versions) for precise event formats. **The response body
|
||||
here describes the common event fields in more detail and may be missing other
|
||||
required fields for a PDU.**
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
room_version:
|
||||
type: string
|
||||
description: |-
|
||||
The version of the room where the server is trying to knock.
|
||||
example: "7"
|
||||
event:
|
||||
description: |-
|
||||
An unsigned template event. Note that events have a different format
|
||||
depending on the room version - check the [room version specification](/#room-versions)
|
||||
for precise event formats.
|
||||
type: object
|
||||
title: Event Template
|
||||
properties:
|
||||
sender:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
origin:
|
||||
type: string
|
||||
description: The name of the resident homeserver.
|
||||
example: "matrix.org"
|
||||
origin_server_ts:
|
||||
type: integer
|
||||
format: int64
|
||||
description: A timestamp added by the resident homeserver.
|
||||
example: 1234567890
|
||||
type:
|
||||
type: string
|
||||
description: The value `m.room.member`.
|
||||
example: "m.room.member"
|
||||
state_key:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
content:
|
||||
type: object
|
||||
title: Membership Event Content
|
||||
description: The content of the event.
|
||||
example: {"membership": "knock"}
|
||||
properties:
|
||||
membership:
|
||||
type: string
|
||||
description: The value `knock`.
|
||||
example: "knock"
|
||||
required: ['membership']
|
||||
required:
|
||||
- state_key
|
||||
- origin
|
||||
- origin_server_ts
|
||||
- type
|
||||
- content
|
||||
- sender
|
||||
required:
|
||||
- room_version # knocking was added in v7
|
||||
- event
|
||||
examples:
|
||||
application/json: {
|
||||
"room_version": "7",
|
||||
"event": {
|
||||
"$ref": "examples/minimal_pdu.json",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@someone:example.org",
|
||||
"origin": "example.org",
|
||||
"origin_server_ts": 1549041175876,
|
||||
"sender": "@someone:example.org",
|
||||
"content": {
|
||||
"membership": "knock"
|
||||
}
|
||||
}
|
||||
}
|
||||
400:
|
||||
description: |-
|
||||
The request is invalid or the room the server is attempting
|
||||
to knock upon has a version that is not listed in the `ver`
|
||||
parameters.
|
||||
|
||||
The error should be passed through to clients so that they
|
||||
may give better feedback to users.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
- type: object
|
||||
properties:
|
||||
room_version:
|
||||
type: string
|
||||
description: |-
|
||||
The version of the room. Required if the `errcode`
|
||||
is `M_INCOMPATIBLE_ROOM_VERSION`.
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
|
||||
"error": "Your homeserver does not support the features required to knock on this room",
|
||||
"room_version": "7"
|
||||
}
|
||||
404:
|
||||
description: |-
|
||||
The room that the knocking server is attempting to knock upon is unknown
|
||||
to the receiving server.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Unknown room",
|
||||
}
|
||||
403:
|
||||
description: |-
|
||||
The knocking server or user is not permitted to knock on the room, such as when the
|
||||
server/user is banned or the room is not set up for receiving knocks.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_FORBIDDEN",
|
||||
"error": "You are not permitted to knock on this room",
|
||||
}
|
||||
|
||||
"/send_knock/{roomId}/{eventId}":
|
||||
put:
|
||||
summary: Submit a signed knock event to a resident server.
|
||||
description: |-
|
||||
Submits a signed knock event to the resident server for it to
|
||||
accept into the room's graph. Note that events have
|
||||
a different format depending on the room version - check
|
||||
the [room version specification](/#room-versions) for precise event formats.
|
||||
**The request and response body here describe the common
|
||||
event fields in more detail and may be missing other required
|
||||
fields for a PDU.**
|
||||
operationId: sendKnock
|
||||
security:
|
||||
- signedRequest: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: roomId
|
||||
type: string
|
||||
description: The room ID that is about to be knocked upon.
|
||||
required: true
|
||||
x-example: "!abc123:example.org"
|
||||
- in: path
|
||||
name: eventId
|
||||
type: string
|
||||
description: The event ID for the knock event.
|
||||
required: true
|
||||
x-example: "$abc123:example.org"
|
||||
- in: body
|
||||
name: body
|
||||
type: object
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
sender:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
origin:
|
||||
type: string
|
||||
description: The name of the knocking homeserver.
|
||||
example: "matrix.org"
|
||||
origin_server_ts:
|
||||
type: integer
|
||||
format: int64
|
||||
description: A timestamp added by the knocking homeserver.
|
||||
example: 1234567890
|
||||
type:
|
||||
type: string
|
||||
description: The value `m.room.member`.
|
||||
example: "m.room.member"
|
||||
state_key:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
content:
|
||||
type: object
|
||||
title: Membership Event Content
|
||||
description: The content of the event.
|
||||
example: {"membership": "knock"}
|
||||
properties:
|
||||
membership:
|
||||
type: string
|
||||
description: The value `knock`.
|
||||
example: "knock"
|
||||
required: ['membership']
|
||||
required:
|
||||
- state_key
|
||||
- sender
|
||||
- origin
|
||||
- origin_server_ts
|
||||
- type
|
||||
- content
|
||||
example: {
|
||||
"$ref": "examples/minimal_pdu.json",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@someone:example.org",
|
||||
"origin": "example.org",
|
||||
"origin_server_ts": 1549041175876,
|
||||
"sender": "@someone:example.org",
|
||||
"content": {
|
||||
"membership": "knock"
|
||||
}
|
||||
}
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
Information about the room to pass along to clients regarding the
|
||||
knock.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
knock_room_state:
|
||||
type: array
|
||||
items:
|
||||
$ref: "../../event-schemas/schema/stripped_state.yaml"
|
||||
description: |-
|
||||
A list of simplified events to help the initiator of the knock identify
|
||||
the room. The recommended events to include are the join rules, canonical
|
||||
alias, avatar, name, and encryption state of the room.
|
||||
example:
|
||||
$ref: "../../event-schemas/examples/knock_room_state.json"
|
||||
required: ['knock_room_state']
|
||||
examples:
|
||||
application/json: {
|
||||
"knock_room_state": {"$ref": "../../event-schemas/examples/knock_room_state.json"}
|
||||
}
|
||||
404:
|
||||
description: |-
|
||||
The room that the knocking server is attempting to knock upon is unknown
|
||||
to the receiving server.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Unknown room",
|
||||
}
|
||||
403:
|
||||
description: |-
|
||||
The knocking server or user is not permitted to knock on the room, such as when the
|
||||
server/user is banned or the room is not set up for receiving knocks.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_FORBIDDEN",
|
||||
"error": "You are not permitted to knock on this room",
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2018 New Vector Ltd
|
||||
# Copyright 2018, 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.
|
||||
|
@ -193,6 +193,13 @@ paths:
|
|||
avatar_url:
|
||||
type: string
|
||||
description: The URL for the room's avatar, if one is set.
|
||||
join_rule:
|
||||
type: string
|
||||
description: |-
|
||||
The room's join rule. When not present, the room is assumed to
|
||||
be `public`. Note that rooms with `invite` join rules are not
|
||||
expected here, but rooms with `knock` rules are given their
|
||||
near-public nature.
|
||||
next_batch:
|
||||
type: string
|
||||
description: |-
|
||||
|
@ -215,13 +222,14 @@ paths:
|
|||
"chunk": [
|
||||
{
|
||||
"aliases": ["#murrays:cheese.bar"],
|
||||
"avatar_url": "mxc://bleeker.street/CHEDDARandBRIE",
|
||||
"avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
|
||||
"guest_can_join": false,
|
||||
"name": "CHEESE",
|
||||
"num_joined_members": 37,
|
||||
"room_id": "!ol19s:bleecker.street",
|
||||
"topic": "Tasty tasty cheese",
|
||||
"world_readable": true
|
||||
"world_readable": true,
|
||||
"join_rule": "public"
|
||||
}
|
||||
],
|
||||
"next_batch": "p190q",
|
||||
|
|
18
data/event-schemas/examples/knock_room_state.json
Normal file
18
data/event-schemas/examples/knock_room_state.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
[
|
||||
{
|
||||
"type": "m.room.name",
|
||||
"sender": "@bob:example.org",
|
||||
"state_key": "",
|
||||
"content": {
|
||||
"name": "Example Room"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.join_rules",
|
||||
"sender": "@bob:example.org",
|
||||
"state_key": "",
|
||||
"content": {
|
||||
"join_rule": "knock"
|
||||
}
|
||||
}
|
||||
]
|
6
data/event-schemas/examples/m.key.verification.done.yaml
Normal file
6
data/event-schemas/examples/m.key.verification.done.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type": "m.key.verification.done",
|
||||
"content": {
|
||||
"transaction_id": "S0meUniqueAndOpaqueString"
|
||||
}
|
||||
}
|
10
data/event-schemas/examples/m.key.verification.ready.yaml
Normal file
10
data/event-schemas/examples/m.key.verification.ready.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"type": "m.key.verification.ready",
|
||||
"content": {
|
||||
"from_device": "BobDevice1",
|
||||
"transaction_id": "S0meUniqueAndOpaqueString",
|
||||
"methods": [
|
||||
"m.sas.v1"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"$ref": "m.room.member.yaml",
|
||||
"content": {
|
||||
"membership": "knock",
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234,
|
||||
"knock_room_state": {
|
||||
"$ref": "knock_room_state.json"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,16 +3,16 @@ allOf:
|
|||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Accepts a previously sent `m.key.verification.start` message. Typically sent as a
|
||||
[to-device](/client-server-api/#send-to-device-messaging) event.
|
||||
Accepts a previously sent `m.key.verification.start` message.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
An opaque identifier for the verification process. Must be the same as
|
||||
the one used for the `m.key.verification.start` message.
|
||||
Required when sent as a to-device message. An opaque identifier for
|
||||
the verification process. Must be the same as the one used for the
|
||||
`m.key.verification.start` message.
|
||||
key_agreement_protocol:
|
||||
type: string
|
||||
description: |-
|
||||
|
@ -43,8 +43,10 @@ properties:
|
|||
The hash (encoded as unpadded base64) of the concatenation of the device's
|
||||
ephemeral public key (encoded as unpadded base64) and the canonical JSON
|
||||
representation of the `m.key.verification.start` message.
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- transaction_id
|
||||
- method
|
||||
- key_agreement_protocol
|
||||
- hash
|
||||
|
|
|
@ -3,14 +3,15 @@ allOf:
|
|||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Cancels a key verification process/request. Typically sent as a [to-device](/client-server-api/#send-to-device-messaging) event.
|
||||
Cancels a key verification process/request.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
The opaque identifier for the verification process/request.
|
||||
Required when sent as a to-device message. The opaque identifier for
|
||||
the verification process/request.
|
||||
reason:
|
||||
type: string
|
||||
description: |-
|
||||
|
@ -56,8 +57,10 @@ properties:
|
|||
gets an unexpected response with `m.unexpected_message`, the client should not
|
||||
respond again with `m.unexpected_message` to avoid the other device potentially
|
||||
sending another error response.
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- transaction_id
|
||||
- code
|
||||
- reason
|
||||
type: object
|
||||
|
|
23
data/event-schemas/schema/m.key.verification.done.yaml
Normal file
23
data/event-schemas/schema/m.key.verification.done.yaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
allOf:
|
||||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Indicates that a verification process/request has completed successfully.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
Required when sent as a to-device message. The opaque identifier for
|
||||
the verification process/request.
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
type: object
|
||||
type:
|
||||
enum:
|
||||
- m.key.verification.done
|
||||
type: string
|
||||
type: object
|
|
@ -3,22 +3,24 @@ allOf:
|
|||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Sends the ephemeral public key for a device to the partner device. Typically sent as a
|
||||
[to-device](/client-server-api/#send-to-device-messaging) event.
|
||||
Sends the ephemeral public key for a device to the partner device.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
An opaque identifier for the verification process. Must be the same as
|
||||
the one used for the `m.key.verification.start` message.
|
||||
Required when sent as a to-device message. An opaque identifier for
|
||||
the verification process. Must be the same as the one used for the
|
||||
`m.key.verification.start` message.
|
||||
key:
|
||||
type: string
|
||||
description: |-
|
||||
The device's ephemeral public key, encoded as unpadded base64.
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- transaction_id
|
||||
- key
|
||||
type: object
|
||||
type:
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
description: |-
|
||||
Required when sent as an in-room message. Indicates the
|
||||
`m.key.verification.request` that this message is related to. Note that for
|
||||
encrypted messages, this property should be in the unencrypted portion of the
|
||||
event.
|
||||
properties:
|
||||
rel_type:
|
||||
type: string
|
||||
enum:
|
||||
- m.reference
|
||||
description: |-
|
||||
The relationship type.
|
||||
event_id:
|
||||
type: string
|
||||
description: |-
|
||||
The event ID of the `m.key.verification.request` that this message is
|
||||
related to.
|
||||
type: object
|
||||
type: object
|
||||
title: VerificationRelatesTo
|
|
@ -3,16 +3,16 @@ allOf:
|
|||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Sends the MAC of a device's key to the partner device. Typically sent as a
|
||||
[to-device](/client-server-api/#send-to-device-messaging) event.
|
||||
Sends the MAC of a device's key to the partner device.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
An opaque identifier for the verification process. Must be the same as
|
||||
the one used for the `m.key.verification.start` message.
|
||||
Required when sent as a to-device message. An opaque identifier for
|
||||
the verification process. Must be the same as the one used for the
|
||||
`m.key.verification.start` message.
|
||||
mac:
|
||||
type: object
|
||||
description: |-
|
||||
|
@ -26,8 +26,10 @@ properties:
|
|||
description: |-
|
||||
The MAC of the comma-separated, sorted, list of key IDs given in the `mac`
|
||||
property, encoded as unpadded base64.
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- transaction_id
|
||||
- mac
|
||||
- keys
|
||||
type: object
|
||||
|
|
40
data/event-schemas/schema/m.key.verification.ready.yaml
Normal file
40
data/event-schemas/schema/m.key.verification.ready.yaml
Normal file
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
allOf:
|
||||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Accepts a key verification request. Sent in response to an
|
||||
`m.key.verification.request` event.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
from_device:
|
||||
type: string
|
||||
description: |-
|
||||
The device ID which is accepting the request.
|
||||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
Required when sent as a to-device message. The transaction ID of the
|
||||
verification request, as given in the `m.key.verification.request`
|
||||
message.
|
||||
methods:
|
||||
type: array
|
||||
description: |-
|
||||
The verification methods supported by the sender, corresponding to
|
||||
the verification methods indicated in the
|
||||
`m.key.verification.request` message.
|
||||
items:
|
||||
type: string
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- from_device
|
||||
- methods
|
||||
type: object
|
||||
type:
|
||||
enum:
|
||||
- m.key.verification.ready
|
||||
type: string
|
||||
type: object
|
|
@ -3,8 +3,7 @@ allOf:
|
|||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Requests a key verification with another user's devices. Typically sent as a
|
||||
[to-device](/client-server-api/#send-to-device-messaging) event.
|
||||
Requests a key verification with another user's devices.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
|
@ -15,8 +14,9 @@ properties:
|
|||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
An opaque identifier for the verification request. Must be unique
|
||||
with respect to the devices involved.
|
||||
Required when sent as a to-device message. An opaque identifier for
|
||||
the verification request. Must be unique with respect to the devices
|
||||
involved.
|
||||
methods:
|
||||
type: array
|
||||
description: |-
|
||||
|
@ -27,14 +27,13 @@ properties:
|
|||
type: integer
|
||||
format: int64
|
||||
description: |-
|
||||
The POSIX timestamp in milliseconds for when the request was made. If
|
||||
the request is in the future by more than 5 minutes or more than 10
|
||||
minutes in the past, the message should be ignored by the receiver.
|
||||
Required when sent as a to-device message. The POSIX timestamp in
|
||||
milliseconds for when the request was made. If the request is in the
|
||||
future by more than 5 minutes or more than 10 minutes in the past,
|
||||
the message should be ignored by the receiver.
|
||||
required:
|
||||
- from_device
|
||||
- transaction_id
|
||||
- methods
|
||||
- timestamp
|
||||
type: object
|
||||
type:
|
||||
enum:
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
allOf:
|
||||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Begins a key verification process using the `m.reciprocate.v1` method, after
|
||||
scanning a QR code.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
from_device:
|
||||
type: string
|
||||
description: |-
|
||||
The device ID which is initiating the process.
|
||||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
Required when sent as a to-device message. An opaque identifier for
|
||||
the verification process. Must be unique with respect to the devices
|
||||
involved. Must be the same as the `transaction_id` given in the
|
||||
`m.key.verification.request` if this process is originating from a
|
||||
request.
|
||||
method:
|
||||
type: string
|
||||
enum: ["m.reciprocate.v1"]
|
||||
description: |-
|
||||
The verification method to use.
|
||||
secret:
|
||||
type: string
|
||||
description: |-
|
||||
The shared secret from the QR code, encoded using unpadded base64.
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- from_device
|
||||
- method
|
||||
- secret
|
||||
type: object
|
||||
type:
|
||||
enum:
|
||||
- m.key.verification.start
|
||||
type: string
|
||||
type: object
|
|
@ -3,7 +3,7 @@ allOf:
|
|||
- $ref: core-event-schema/event.yaml
|
||||
|
||||
description: |-
|
||||
Begins a SAS key verification process using the `m.sas.v1` method. Typically sent as a [to-device](/client-server-api/#send-to-device-messaging) event.
|
||||
Begins a SAS key verification process using the `m.sas.v1` method.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
|
@ -14,10 +14,11 @@ properties:
|
|||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
An opaque identifier for the verification process. Must be unique
|
||||
with respect to the devices involved. Must be the same as the
|
||||
`transaction_id` given in the `m.key.verification.request`
|
||||
if this process is originating from a request.
|
||||
Required when sent as a to-device message. An opaque identifier for
|
||||
the verification process. Must be unique with respect to the devices
|
||||
involved. Must be the same as the `transaction_id` given in the
|
||||
`m.key.verification.request` if this process is originating from a
|
||||
request.
|
||||
method:
|
||||
type: string
|
||||
enum: ["m.sas.v1"]
|
||||
|
@ -53,9 +54,11 @@ properties:
|
|||
items:
|
||||
type: string
|
||||
enum: ["decimal", "emoji"]
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- from_device
|
||||
- transaction_id
|
||||
- method
|
||||
- key_agreement_protocols
|
||||
- hashes
|
||||
|
|
|
@ -16,10 +16,11 @@ properties:
|
|||
transaction_id:
|
||||
type: string
|
||||
description: |-
|
||||
An opaque identifier for the verification process. Must be unique
|
||||
with respect to the devices involved. Must be the same as the
|
||||
`transaction_id` given in the `m.key.verification.request`
|
||||
if this process is originating from a request.
|
||||
Required when sent as a to-device message. An opaque identifier for
|
||||
the verification process. Must be unique with respect to the devices
|
||||
involved. Must be the same as the `transaction_id` given in the
|
||||
`m.key.verification.request` if this process is originating from a
|
||||
request.
|
||||
method:
|
||||
type: string
|
||||
description: |-
|
||||
|
@ -30,9 +31,11 @@ properties:
|
|||
Optional method to use to verify the other user's key with. Applicable
|
||||
when the `method` chosen only verifies one user's key. This field will
|
||||
never be present if the `method` verifies keys both ways.
|
||||
m.relates_to:
|
||||
allOf:
|
||||
- $ref: m.key.verification.m.relates_to.yaml
|
||||
required:
|
||||
- from_device
|
||||
- transaction_id
|
||||
- method
|
||||
type: object
|
||||
type:
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
---
|
||||
allOf:
|
||||
- $ref: core-event-schema/state_event.yaml
|
||||
description: 'A room may be `public` meaning anyone can join the room without any prior action. Alternatively, it can be `invite` meaning that a user who wishes to join the room must first receive an invite to the room from someone already inside of the room. Currently, `knock` and `private` are reserved keywords which are not implemented.'
|
||||
description: |
|
||||
A room may be `public` meaning anyone can join the room without any prior action.
|
||||
Alternatively, it can be `invite` meaning that a user who wishes to join the room
|
||||
must first receive an invite to the room from someone already inside of the room.
|
||||
`knock` means that users are able to ask for permission to join the room, where
|
||||
they are either allowed (invited) or denied (kicked/banned) access. Join rules
|
||||
of `knock` are otherwise the same as `invite`: the user needs an explicit invite
|
||||
to join the room.
|
||||
|
||||
Currently, `private` is a reserved keyword which is not implemented.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
|
|
|
@ -14,7 +14,7 @@ description: |-
|
|||
|
||||
- `ban` - The user has been banned from the room, and is no longer allowed to join it until they are un-banned from the room (by having their membership state set to a value other than `ban`).
|
||||
|
||||
- `knock` - This is a reserved word, which currently has no meaning.
|
||||
- `knock` - The user has knocked on the room, requesting permission to participate. They may not participate in the room until they join.
|
||||
|
||||
The `third_party_invite` property will be set if this invite is an `invite` event and is the successor of an `m.room.third_party_invite` event, and absent otherwise.
|
||||
|
||||
|
@ -31,13 +31,13 @@ description: |-
|
|||
from the `prev_content` object on an event. If not present, the user's previous membership must be assumed
|
||||
as `leave`.
|
||||
|
||||
| | to `invite` | to `join` | to `leave` | to `ban` | to `knock` |
|
||||
|-------------------|---------------------|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|------------------|
|
||||
| **from `invite`** | No change. | User joined the room. | If the `state_key` is the same as the `sender`, the user rejected the invite. Otherwise, the `state_key` user had their invite revoked. | User was banned. | Not implemented. |
|
||||
| **from `join`** |Must never happen. | `displayname` or `avatar_url` changed. | If the `state_key` is the same as the `sender`, the user left. Otherwise, the `state_key` user was kicked. | User was kicked and banned. | Not implemented. |
|
||||
| **from `leave`** |New invitation sent. | User joined. | No change. | User was banned. | Not implemented. |
|
||||
| **from `ban`** |Must never happen. | Must never happen. | User was unbanned. | No change. | Not implemented. |
|
||||
| **from `knock`** |Not implemented. | Not implemented. | Not implemented. | Not implemented. | Not implemented. |
|
||||
| | to `invite` | to `join` | to `leave` | to `ban` | to `knock` |
|
||||
|-------------------|----------------------|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|--------------------|
|
||||
| **from `invite`** | No change. | User joined the room. | If the `state_key` is the same as the `sender`, the user rejected the invite. Otherwise, the `state_key` user had their invite revoked. | User was banned. | Must never happen. |
|
||||
| **from `join`** | Must never happen. | `displayname` or `avatar_url` changed. | If the `state_key` is the same as the `sender`, the user left. Otherwise, the `state_key` user was kicked. | User was kicked and banned. | Must never happen. |
|
||||
| **from `leave`** | New invitation sent. | User joined. | No change. | User was banned. | User is knocking. |
|
||||
| **from `ban`** | Must never happen. | Must never happen. | User was unbanned. | No change. | Must never happen. |
|
||||
| **from `knock`** | Knock accepted. | Must never happen. | If the `state_key` is the same as the `sender`, the user retracted the knock. Otherwise, the `state_key` user had their knock denied. | User was banned. | No change. |
|
||||
|
||||
properties:
|
||||
content:
|
||||
|
@ -124,7 +124,24 @@ properties:
|
|||
- type: object
|
||||
properties:
|
||||
invite_room_state:
|
||||
description: 'A subset of the state of the room at the time of the invite, if `membership` is `invite`. Note that this state is informational, and SHOULD NOT be trusted; once the client has joined the room, it SHOULD fetch the live state from the server and discard the invite_room_state. Also, clients must not rely on any particular state being present here; they SHOULD behave properly (with possibly a degraded but not a broken experience) in the absence of any particular events here. If they are set on the room, at least the state for `m.room.avatar`, `m.room.canonical_alias`, `m.room.join_rules`, and `m.room.name` SHOULD be included.'
|
||||
description: |-
|
||||
A subset of the state of the room at the time of the invite, if `membership` is `invite`.
|
||||
Note that this state is informational, and SHOULD NOT be trusted; once the client has
|
||||
joined the room, it SHOULD fetch the live state from the server and discard the
|
||||
invite_room_state. Also, clients must not rely on any particular state being present here;
|
||||
they SHOULD behave properly (with possibly a degraded but not a broken experience) in
|
||||
the absence of any particular events here. If they are set on the room, at least the
|
||||
state for `m.room.avatar`, `m.room.canonical_alias`, `m.room.join_rules`, and `m.room.name`
|
||||
SHOULD be included.
|
||||
items:
|
||||
$ref: "stripped_state.yaml"
|
||||
type: array
|
||||
knock_room_state:
|
||||
description: |-
|
||||
A subset of the state of the room at the time of the knock, if `membership` is `knock`.
|
||||
This has the same restrictions as `invite_room_state`. If they are set on the room, at least
|
||||
the state for `m.room.avatar`, `m.room.canonical_alias`, `m.room.join_rules`, `m.room.name`,
|
||||
and `m.room.encryption` SHOULD be included.
|
||||
items:
|
||||
$ref: "stripped_state.yaml"
|
||||
type: array
|
||||
|
|
|
@ -8,7 +8,7 @@ in.
|
|||
Format
|
||||
------
|
||||
|
||||
Documentation is written either in github-flavored markdown.
|
||||
Documentation is written in github-flavored markdown.
|
||||
|
||||
Sections
|
||||
--------
|
||||
|
|
314
proposals/2241-e2e-verification-in-dms.md
Normal file
314
proposals/2241-e2e-verification-in-dms.md
Normal file
|
@ -0,0 +1,314 @@
|
|||
# Key verification in DMs
|
||||
|
||||
Currently, key verification is done using `to_device` messages. However, since
|
||||
`to_device` messages are not part of a timeline, there is no user-visible
|
||||
record of the key verification.
|
||||
|
||||
As well, the current key verification framework does not provide any feedback
|
||||
when interacting with clients that do not support it; if a client does not
|
||||
support the key verification framework, there is no way for users to discover
|
||||
this other than waiting for a while and noticing that nothing is happening.
|
||||
|
||||
This proposal will solve both problems.
|
||||
|
||||
## Proposal
|
||||
|
||||
The current [key verification
|
||||
framework](https://matrix.org/docs/spec/client_server/r0.5.0#key-verification-framework)
|
||||
will be replaced by a new framework that uses room messages rather than
|
||||
`to_device` messages. Key verification messages will be sent in a [Direct
|
||||
Messaging](https://matrix.org/docs/spec/client_server/r0.5.0#id185) room. If
|
||||
there is no Direct Messaging room between the two users involved, the client
|
||||
that initiates the key verification will create one.
|
||||
|
||||
In this proposal, we use "Alice" to denote the user who initiates the key
|
||||
verification, and "Bob" to denote the other user involved in the key
|
||||
verification.
|
||||
|
||||
### General framework
|
||||
|
||||
#### Requesting a key verification
|
||||
|
||||
To request a key verification, Alice will send an `m.room.message` event with the
|
||||
following properties in its contents:
|
||||
|
||||
- `body`: a fallback message to alert users that their client does not support
|
||||
the key verification framework, and that they should use a different method
|
||||
to verify keys. For example, "Alice is requesting to verify keys with you.
|
||||
However, your client does not support this method, so you will need to use
|
||||
the legacy method of key verification."
|
||||
|
||||
Clients that do support the key verification framework should hide the body
|
||||
and instead present the user with an interface to accept or reject the key
|
||||
verification.
|
||||
|
||||
The event may also contain `format` and `formatted_body` properties as
|
||||
described in the [m.room.message
|
||||
msgtypes](https://matrix.org/docs/spec/client_server/r0.5.0#m-room-message-msgtypes)
|
||||
section of the spec. Clients that support the key verification should
|
||||
similarly hide these from the user.
|
||||
- `msgtype`: `m.key.verification.request`
|
||||
- `methods`: the verification methods supported by Alice's client
|
||||
- `to`: Bob's Matrix ID. Users should only respond to verification requests if
|
||||
they are named in this field. Users who are not named in this field and who
|
||||
did not send this event should ignore all other events that have a
|
||||
`m.reference` relationship with this event.
|
||||
- `from_device`: Alice's device ID. This is required since some verification
|
||||
methods may use the device IDs as part of the verification process.
|
||||
|
||||
Key verifications will be identified by the event ID of the key verification
|
||||
request event.
|
||||
|
||||
Clients should ignore verification requests that have been accepted or
|
||||
cancelled, or if they do not belong to the sending or target users.
|
||||
|
||||
The way that clients display this event can depend on which user and device the
|
||||
client belongs to, and what state the verification is in. For example:
|
||||
|
||||
- If the verification has been completed (there is an `m.key.verification.done`
|
||||
or `m.key.verification.cancel` event), the client can indicate that the
|
||||
verification was successful or had an error.
|
||||
- If the verification has been accepted (there is an `m.key.verification.start`
|
||||
event) but has not been completed, the two devices involved can indicate that
|
||||
the verification is in progress and can use this event as a place in the
|
||||
room's timeline to display progress of the key verification and to interact
|
||||
with the user as necessary. Other devices can indicate that the verification
|
||||
is in progress on other devices.
|
||||
- If the verification has not been accepted, clients for the target user can
|
||||
indicate that a verification has been requested and allow the user to accept
|
||||
the verification on that device. The sending client can indicate that it is
|
||||
waiting for the request to be accepted, and the sending user's other clients
|
||||
can indicate the that a request was initiated on a different device.
|
||||
|
||||
Clients may choose to display or not to display events of any other type that
|
||||
reference the original request event; but it must not have any effect on the
|
||||
verification itself.
|
||||
|
||||
#### Accepting a key verification
|
||||
|
||||
To accept a key verification, Bob will send an `m.key.verification.ready` event
|
||||
with the following properties in its contents:
|
||||
|
||||
- `m.relates_to`: an object with the properties:
|
||||
- `rel_type`: `m.reference`
|
||||
- `event_id`: the event ID of the key verification request that is being
|
||||
accepted
|
||||
- `methods`: an array of verification methods that the device supports
|
||||
- `from_device`: Bob's device ID. This is required since some verification
|
||||
methods may use the device IDs as part of the verification process.
|
||||
|
||||
(Note: the form of the `m.relates_to` property is based on the current state of
|
||||
[MSC2674](https://github.com/matrix-org/matrix-doc/pull/2674), but is
|
||||
independent from it since this MSC does not rely on any aggregations features.)
|
||||
|
||||
Clients should ignore `m.key.verification.ready` events that correspond to
|
||||
verification requests that they did not send.
|
||||
|
||||
After this, either Alice or Bob may start the verification by sending an
|
||||
`m.key.verification.start` event with the following properties in its contents:
|
||||
|
||||
- `m.relates_to`: an object with the properties:
|
||||
- `rel_type`: `m.reference`
|
||||
- `event_id`: the event ID of the key verification request that is being
|
||||
started
|
||||
- `method`: the key verification method that is being used. This should be a
|
||||
method that both Alice's and Bob's devices support.
|
||||
- `from_device`: The user's device ID.
|
||||
|
||||
If both Alice and Bob send an `m.key.verification.start` message, and they both
|
||||
specify the same verification method, then the event sent by the user whose
|
||||
user ID is the smallest is used, and the other event is ignored. If they both
|
||||
send an `m.key.verification.start` message and the method is different, then
|
||||
the verification should be cancelled with a `code` of `m.unexpected_message`.
|
||||
|
||||
After the `m.key.verification.start` event is sent, the devices may exchange
|
||||
messages (if any) according to the verification method in use.
|
||||
|
||||
#### Rejecting a key verification
|
||||
|
||||
To reject a key verification, Alice or Bob will send an
|
||||
`m.key.verification.cancel` event with the following properties in its
|
||||
contents:
|
||||
|
||||
- `m.relates_to`: an object with the properties:
|
||||
- `rel_type`: `m.reference`
|
||||
- `event_id`: the event ID of the key verification that is being cancelled
|
||||
- `reason`: A human readable description of the `code`. The client should only
|
||||
rely on this string if it does not understand the `code`.
|
||||
- `code`: The error code for why the process/request was cancelled by the
|
||||
user. The contents are the same as the `code` property of the currently
|
||||
defined [`m.key.verification.cancel` to-device
|
||||
event](https://matrix.org/docs/spec/client_server/r0.5.0#m-key-verification-cancel),
|
||||
or as defined for specific key verification methods.
|
||||
|
||||
This message may be sent at any point in the key verification process. Any
|
||||
subsequent key verification messages relating to the same request are ignored.
|
||||
However, this does not undo any verifications that have already been done.
|
||||
|
||||
#### Concluding a key verification
|
||||
|
||||
When the other user's key is verified and no more messages are expected, each
|
||||
party will send an `m.key.verification.done` event with the following
|
||||
properties in its contents:
|
||||
|
||||
- `m.relates_to`: an object with the properties:
|
||||
- `rel_type`: `m.reference`
|
||||
- `event_id`: the event ID of the key verification that is being concluded
|
||||
|
||||
This provides a record within the room of the result of the verification.
|
||||
|
||||
Any subsequent key verification messages relating to the same request are
|
||||
ignored.
|
||||
|
||||
Although a client may have successfully completed its side of the verification,
|
||||
it may wait until receiving an `m.key.verification.done` (or
|
||||
`m.key.verification.cancel`) event from the other device before informing the
|
||||
user that the verification was successful or unsuccessful.
|
||||
|
||||
#### Other events
|
||||
|
||||
Key verification methods may define their own event types, or extensions to the
|
||||
above event types. All events sent as part of a key verification process
|
||||
should have an `m.relates_to` property as defined for
|
||||
`m.key.verification.accept` or `m.key.verification.cancel` events.
|
||||
|
||||
Clients should ignore events with an `m.relates_to` that have a `rel_type` of
|
||||
`m.reference` that refer to a verification where it is neither the requester
|
||||
nor the accepter.
|
||||
|
||||
Clients should not redact or edit verification messages. A client may ignore
|
||||
redactions or edits of key verification messages, or may cancel the
|
||||
verification with a `code` of `m.unexpected_message` when it receives a
|
||||
redaction or edit.
|
||||
|
||||
### SAS verification
|
||||
|
||||
The messages used in SAS verification are the same as those currently defined,
|
||||
except that instead of the `transaction_id` property, an `m.relates_to`
|
||||
property, as defined above, is used instead.
|
||||
|
||||
If the key verification messages are encrypted, the hash commitment sent in the
|
||||
`m.key.verification.accept` message MUST be based on the decrypted
|
||||
`m.key.verification.start` message contents, and include the `m.relates_to`
|
||||
field, even if the decrypted message contents do not include that field. For
|
||||
example, if Alice sends a message to start the SAS verification:
|
||||
|
||||
```json
|
||||
{
|
||||
"content": {
|
||||
"algorithm": "m.megolm.v1.aes-sha2",
|
||||
"ciphertext": "ABCDEFG...",
|
||||
"device_id": "Dynabook",
|
||||
"sender_key": "alice+sender+key",
|
||||
"session_id": "session+id",
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.reference",
|
||||
"event_id": "$verification_request_event"
|
||||
}
|
||||
},
|
||||
"event_id": "$event_id",
|
||||
"origin_server_ts": 1234567890,
|
||||
"sender": "@alice:example.org",
|
||||
"type": "m.room.encrypted",
|
||||
"room_id": "!room_id:example.org"
|
||||
}
|
||||
```
|
||||
|
||||
which, when decrypted, yields:
|
||||
|
||||
```json
|
||||
{
|
||||
"room_id": "!room_id:example.org",
|
||||
"type": "m.key.verification.start",
|
||||
"content": {
|
||||
"from_device": "Dynabook",
|
||||
"hashes": [
|
||||
"sha256"
|
||||
],
|
||||
"key_agreement_protocols": [
|
||||
"curve25519"
|
||||
],
|
||||
"message_authentication_codes": [
|
||||
"hkdf-hmac-sha256"
|
||||
],
|
||||
"method": "m.sas.v1",
|
||||
"short_authentication_string": [
|
||||
"decimal",
|
||||
"emoji"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
then the hash commitment will be based on the message contents:
|
||||
|
||||
```json
|
||||
{
|
||||
"from_device": "Dynabook",
|
||||
"hashes": [
|
||||
"sha256"
|
||||
],
|
||||
"key_agreement_protocols": [
|
||||
"curve25519"
|
||||
],
|
||||
"message_authentication_codes": [
|
||||
"hkdf-hmac-sha256"
|
||||
],
|
||||
"method": "m.sas.v1",
|
||||
"short_authentication_string": [
|
||||
"decimal",
|
||||
"emoji"
|
||||
],
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.reference",
|
||||
"event_id": "$verification_request_event"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Alternatives
|
||||
|
||||
Messages sent by the verification methods, after the initial key verification
|
||||
request message, could be sent as to-device messages. The
|
||||
`m.key.verification.ready`, `m.key.verification.cancel`, and
|
||||
`m.key.verification.done` messages must be still be sent in the room, as the
|
||||
`m.key.verification.ready` notifies the sender's other devices that the request
|
||||
has been acknowledged, and the `m.key.verification.cancel` and
|
||||
`m.key.verification.done` provide a record of the status of the key
|
||||
verification.
|
||||
|
||||
However, it seems more natural to have all messages sent via the same
|
||||
mechanism.
|
||||
|
||||
## Potential issues
|
||||
|
||||
If a user wants to verify their own device, this will require the creation of a
|
||||
Direct Messaging room with themselves. Instead, clients may use the current
|
||||
`to_device` messages for verifying the user's other devices.
|
||||
|
||||
Direct Messaging rooms could have end-to-end encryption enabled, and some
|
||||
clients can be configured to only send decryption keys to verified devices.
|
||||
Key verification messages should be granted an exception to this (so that
|
||||
decryption keys are sent to all of the target user's devices), or should be
|
||||
sent unencrypted, so that unverified devices will be able to be verified.
|
||||
|
||||
Users might have multiple Direct Messaging rooms with other users. In this
|
||||
case, clients could need to prompt the user to select the room in which they
|
||||
want to perform the verification, or could select a room.
|
||||
|
||||
## Security considerations
|
||||
|
||||
Key verification is subject to the room's visibility settings, and may be
|
||||
visible to other users in the room. However, key verification does not rely on
|
||||
secrecy, so this will no affect the security of the key verification. This may
|
||||
reveal to others in the room that Alice and Bob know each other, but this is
|
||||
already revealed by the fact that they share a Direct Messaging room.
|
||||
|
||||
This framework allows users to see what key verifications they have performed
|
||||
in the past. However, since key verification messages are not secured, this
|
||||
should not be considered as authoritative.
|
||||
|
||||
## Conclusion
|
||||
|
||||
By using room messages to perform key verification rather than `to_device`
|
||||
messages, the user experience of key verification can be improved.
|
|
@ -199,7 +199,7 @@ contain this information:
|
|||
"aliases": [
|
||||
"#murrays:cheese.bar"
|
||||
],
|
||||
"avatar_url": "mxc://bleeker.street/CHEDDARandBRIE",
|
||||
"avatar_url": "mxc://bleecker.street/CHEDDARandBRIE",
|
||||
"guest_can_join": false,
|
||||
"name": "CHEESE",
|
||||
"num_joined_members": 37,
|
||||
|
|
26
proposals/2713-remove-deprecated-identity-endpoints.md
Normal file
26
proposals/2713-remove-deprecated-identity-endpoints.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# MSC2713: Remove deprecated Identity Service endpoints
|
||||
|
||||
Implementations will have had plenty of time to adopt the new v2 API for Identity Servers, so
|
||||
we should clean out the old endpoints.
|
||||
|
||||
All deprecated endpoints in the r0.3.0 Identity Service API specification are to be removed.
|
||||
|
||||
For completeness, this includes:
|
||||
|
||||
* `GET /_matrix/identity/api/v1`
|
||||
* `GET /_matrix/identity/api/v1/pubkey/{keyId}`
|
||||
* `GET /_matrix/identity/api/v1/pubkey/isvalid`
|
||||
* `GET /_matrix/identity/api/v1/pubkey/ephemeral/isvalid`
|
||||
* `GET /_matrix/identity/api/v1/lookup`
|
||||
* `POST /_matrix/identity/api/v1/bulk_lookup`
|
||||
* `POST /_matrix/identity/api/v1/validate/email/requestToken`
|
||||
* `POST /_matrix/identity/api/v1/validate/email/submitToken`
|
||||
* `GET /_matrix/identity/api/v1/validate/email/submitToken`
|
||||
* `POST /_matrix/identity/api/v1/validate/msisdn/requestToken`
|
||||
* `POST /_matrix/identity/api/v1/validate/msisdn/submitToken`
|
||||
* `GET /_matrix/identity/api/v1/validate/msisdn/submitToken`
|
||||
* `GET /_matrix/identity/api/v1/3pid/getValidated3pid`
|
||||
* `POST /_matrix/identity/api/v1/3pid/bind`
|
||||
* `POST /_matrix/identity/api/v1/3pid/unbind`
|
||||
* `POST /_matrix/identity/api/v1/store-invite`
|
||||
* `POST /_matrix/identity/api/v1/sign-ed25519`
|
|
@ -1,9 +1,5 @@
|
|||
[ tool.gilesbot ]
|
||||
|
||||
[ tool.gilesbot.circleci_artifacts.legacydocs ]
|
||||
url = "gen/index.html"
|
||||
message = "Click details to preview the legacy HTML documentation."
|
||||
|
||||
[ tool.gilesbot.circleci_artifacts.docs ]
|
||||
url = "public/index.html"
|
||||
message = "Click details to preview the HTML documentation."
|
||||
|
|
|
@ -337,7 +337,7 @@ func (s *server) serveSpec(w http.ResponseWriter, req *http.Request) {
|
|||
log.Printf("Serving pr %s (%s)\n", branchName, sha)
|
||||
} else if strings.ToLower(branchName) == "head" ||
|
||||
branchName == "master" ||
|
||||
strings.HasPrefix(branchName, "drafts/") {
|
||||
strings.HasPrefix(branchName, "attic/drafts/") {
|
||||
branchSHA, err := s.lookupBranch(branchName)
|
||||
if err != nil {
|
||||
writeError(w, 400, err)
|
||||
|
|
17
static/diagrams/README.md
Normal file
17
static/diagrams/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# 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 PNG.
|
||||
* 100% size.
|
||||
* `20` for a border width.
|
||||
* No transparent background, shadow, or grid.
|
||||
* Include a copy of the diagram.
|
||||
|
||||
To reference a diagram, use the absolute path when compiled. For example,
|
||||
``
|
1
static/diagrams/membership.drawio
Normal file
1
static/diagrams/membership.drawio
Normal file
|
@ -0,0 +1 @@
|
|||
<mxfile host="app.diagrams.net" modified="2021-04-28T19:35:50.494Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36" etag="-IOh23FjJiPnGlGWLseU" version="14.6.6" type="device"><diagram id="4a_pTli-mcEMNPq0ciXK" name="Page-1">3Vvdb6M4EP9rIt09NMIGA3ncZtvbh73TSn243X1ZOYmT0BIcOaZJ9q8/E0z4cggEgulVqoqHsbHHv/nwjDsyp5vDXwxv13/TBfFH0FgcRubnEYTAAa74E1GOMWViopiwYt5CMqWEF+83kURDUkNvQXY5Rk6pz71tnjinQUDmPEfDjNF9nm1J/fxXt3hFSoSXOfbL1H+9BV/HVBc6Kf0L8Vbr5MvAnsRvNjhhlivZrfGC7jMk82lkThmlPH7aHKbEj4SXyCXu93zh7XlijAS8TodvBvr96euj/xL83G+Dn2gaTvgDkMO8Yz+UKxYjvFIvkJPmx0QSZCEEI5uU8TVd0QD7Tyn1kdEwWJDoc4ZopTxfKd0KIhDEV8L5Ue4yDjkVpDXf+PJt/M3oQxfXKEk7GrI5qVqYxApmK8Ir+OB5JwSECd0Qzo6iHyM+5t57fh5YYml15pNdPzGGjxmGrZAf32VG/hYRBINUCziRmJBKYdqFrWvGLx7iGSStzFJS0gkOTaBhqaDhEyykogEb5ODx75nnH9FQY4hk8/NBDn1qHJNGIGTwPcMZtX9kX6b9Tq2kY4dIhKgmFC01FCUGjDEwHDMeqhk6S3Cy3DycoDUZ25kfx8qPGM9cDpLCrinqLVD4rG1Wot4CqIo/j/q0dzIdulzuCC/06EYzSorhBe8eL6tFHvT7teB52eITWvbCXaqM3zthnByqQXcRI9ApSBjJ9j51XcCQtHXGbSV8KgzlpNdYVHaFETEoG0VO6/nNm799cH8DO1JysRduOyW/P/4hVG3qDGuJGRK/YIxRxjOA29wC6NEt1ASMWQmYB2NsI6ulW+gBMSXAKEPMRvZySQP+jDeeH23OVIjbI5E5+Yfs72NMLUu7MXVVehcGujUvF5HVDMhAVu1kr34Uz2xnqWsrWautNks7rdrkQekLQoVDiqldX5R+KrY8hreMVhfOfG8+grYv5vI4E/KwV9FTxPOLhT7Z6XVp8Caf5ug66lg1VavtobsdKsq6Jca5EMH3sduXNq7HYKT2xqFr0QgaeigCnEEGr05W0426mq4tqVEXMBei1340vZzAUmevBuVGzWKuT7sbVUadGu1lO53RFnfCSWdG1rVdq52dPddnCgkjB+WHiFdVSv31arCNjx3F5RPW1ccjLXBraaLrIcuy6yGrs4yDsrCly82nHrtRhHdLbksHhK7mqFxogrsYLGTVg1XTWgUo1CqQWV2hs41Kfn21CqhMwA/Ae9+SNGp6tM3Fal+I/064N8cdK0lHZycRzN+iIU2BbVr5ohqYoIEAVXkuG0qlqGWV4eOitrpeJUy7A4ZffjAHFQzcEBf+L6B0NUpwEGoZJfRgpspJhbeAKozSoJMK5zBKW1IhuRXT8SWKvouC+m9YJEF6RpBPB06YMEknl1k8FP8R0JOmiN8N2cwI+7NC4qChxMsmpl1JqXCGVKXCoELY9r2EbSpzEdIAxMmIU+MBz4QwB5qQKJSVaiebu/E8r+Fmmyw0oAFJSPGsHV1JkKtxjuvCm3JuTQP0wt08YFUfPM+3EtT8Gi/Jle2S/pjrVug3O3ZmIY7ZvGNI171OejXcmvRy4ISTwoETXcmkwEr+ewBaNNNr+DF7+s8M5tN/</diagram></mxfile>
|
BIN
static/diagrams/membership.png
Normal file
BIN
static/diagrams/membership.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Loading…
Add table
Add a link
Reference in a new issue