Merge branch 'master' into markjh/end_to_end_encryption
This commit is contained in:
commit
5f4458b2ff
155 changed files with 11892 additions and 2364 deletions
56
specification/modules/_template.rst
Normal file
56
specification/modules/_template.rst
Normal file
|
@ -0,0 +1,56 @@
|
|||
Module Heading
|
||||
==============
|
||||
|
||||
.. _module:short-name:
|
||||
|
||||
A short summary of the module. What features does this module provide? An anchor
|
||||
should be specified at the top of the module using the format ``module:name``.
|
||||
|
||||
Complicated modules may wish to have architecture diagrams or event flows
|
||||
(e.g. VoIP call flows) here. Custom subsections can be included but they should
|
||||
be used *sparingly* to reduce the risk of putting client or server behaviour
|
||||
information in these custom sections.
|
||||
|
||||
Events
|
||||
------
|
||||
List the new event types introduced by this module, if any. If there are no
|
||||
new events, this section can be omitted. Event types should be done as
|
||||
subsections. This section is intended to document the "common shared event
|
||||
structure" between client and server. Deviations from this shared structure
|
||||
should be documented in the relevant behaviour section.
|
||||
|
||||
``m.example.event.type``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
There should be JSON Schema docs for this event. Once there is JSON schema,
|
||||
there will be a template variable with dots in the event type replaced with
|
||||
underscores and the suffix ``_event``. You can insert a template like so:
|
||||
|
||||
{{m_example_event_type_event}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
List any new HTTP endpoints. These endpoints should be documented using Swagger.
|
||||
Once there is Swagger, there will be a template variable based on the name of
|
||||
the YAML file with the suffix ``_http_api``. You can insert a template for
|
||||
swagger docs like so:
|
||||
|
||||
{{name-of-yaml-file-without-file-ext_http_api}}
|
||||
|
||||
List the steps the client needs to take to
|
||||
correctly process this module. List what data structures the client should be
|
||||
storing in order to aid implementation.
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
Does the server need to handle any of the new events in a special way (e.g.
|
||||
typing timeouts, presence). Advice on how to persist events and/or requests are
|
||||
recommended to aid implementation. Federation-specific logic should be included
|
||||
here.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
This includes privacy leaks: for example leaking presence info. How do
|
||||
misbehaving clients or servers impact this module? This section should always be
|
||||
included, if only to say "we've thought about it but there isn't anything to do
|
||||
here".
|
||||
|
50
specification/modules/anonymous_access.rst
Normal file
50
specification/modules/anonymous_access.rst
Normal file
|
@ -0,0 +1,50 @@
|
|||
Guest access
|
||||
================
|
||||
|
||||
.. _module:guest-access:
|
||||
|
||||
It may be desirable to allow users without a fully registered user account to
|
||||
ephemerally access Matrix rooms. This module specifies limited ways of doing so.
|
||||
|
||||
Note that this is not currently a complete anonymous access solution; in
|
||||
particular, it only allows servers to provided anonymous access to rooms in
|
||||
which they are already participating, and relies on individual homeservers to
|
||||
adhere to the conventions which this module sets, rather than allowing all
|
||||
participating homeservers to enforce them.
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
{{m_room_guest_accessibility}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
A client can register for guest access using the FOO endpoint. From that point
|
||||
on, they can interact with a limited subset of the existing client-server API,
|
||||
as if they were a fully registered user, using the access token granted to them
|
||||
by the server.
|
||||
|
||||
These users are only allowed to make calls in relation to rooms which have the
|
||||
``m.room.history_visibility`` event set to ``world_readable``.
|
||||
|
||||
The APIs they are allowed to hit are:
|
||||
|
||||
/rooms/{roomId}/messages
|
||||
/rooms/{roomId}/state
|
||||
/rooms/{roomId}/state/{eventType}/{stateKey}
|
||||
/events
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
Does the server need to handle any of the new events in a special way (e.g.
|
||||
typing timeouts, presence). Advice on how to persist events and/or requests are
|
||||
recommended to aid implementation. Federation-specific logic should be included
|
||||
here.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
This includes privacy leaks: for example leaking presence info. How do
|
||||
misbehaving clients or servers impact this module? This section should always be
|
||||
included, if only to say "we've thought about it but there isn't anything to do
|
||||
here".
|
||||
|
87
specification/modules/content_repo.rst
Normal file
87
specification/modules/content_repo.rst
Normal file
|
@ -0,0 +1,87 @@
|
|||
Content repository
|
||||
==================
|
||||
|
||||
.. _module:content:
|
||||
|
||||
This module allows users to upload content to their homeserver which is
|
||||
retrievable from other homeservers. Its' purpose is to allow users to share
|
||||
attachments in a room. Content locations are represented as Matrix Content (MXC)
|
||||
URIs. They look like::
|
||||
|
||||
mxc://<server-name>/<media-id>
|
||||
|
||||
<server-name> : The name of the homeserver where this content originated, e.g. matrix.org
|
||||
<media-id> : An opaque ID which identifies the content.
|
||||
|
||||
Uploads are POSTed to a resource on the user's local homeserver which returns a
|
||||
token which is used to GET the download. Content is downloaded from the
|
||||
recipient's local homeserver, which must first transfer the content from the
|
||||
origin homeserver using the same API (unless the origin and destination
|
||||
homeservers are the same).
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
Clients can upload and download content using the following HTTP APIs.
|
||||
|
||||
{{content_repo_http_api}}
|
||||
|
||||
Thumbnails
|
||||
~~~~~~~~~~
|
||||
The thumbnail methods are "crop" and "scale". "scale" tries to return an
|
||||
image where either the width or the height is smaller than the requested
|
||||
size. The client should then scale and letterbox the image if it needs to
|
||||
fit within a given rectangle. "crop" tries to return an image where the
|
||||
width and height are close to the requested size and the aspect matches
|
||||
the requested size. The client should scale the image if it needs to fit
|
||||
within a given rectangle.
|
||||
|
||||
In summary:
|
||||
* "scale" maintains the original aspect ratio of the image
|
||||
* "crop" provides an image in the aspect ratio of the sizes given in the request
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
Homeservers may generate thumbnails for content uploaded to remote
|
||||
homeservers themselves or may rely on the remote homeserver to thumbnail
|
||||
the content. Homeservers may return thumbnails of a different size to that
|
||||
requested. However homeservers should provide exact matches where reasonable.
|
||||
Homeservers must never upscale images.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
The HTTP GET endpoint does not require any authentication. Knowing the URL of
|
||||
the content is sufficient to retrieve the content, even if the entity isn't in
|
||||
the room.
|
||||
|
||||
MXC URIs are vulnerable to directory traversal attacks such as
|
||||
``mxc://127.0.0.1/../../../some_service/etc/passwd``. This would cause the target
|
||||
homeserver to try to access and return this file. As such, homeservers MUST
|
||||
sanitise MXC URIs by allowing only alphanumeric (``A-Za-z0-9``), ``_``
|
||||
and ``-`` characters in the ``server-name`` and ``media-id`` values. This set
|
||||
of whitelisted characters allows URL-safe base64 encodings specified in RFC 4648.
|
||||
Applying this character whitelist is preferable to blacklisting ``.`` and ``/``
|
||||
as there are techniques around blacklisted characters (percent-encoded characters,
|
||||
UTF-8 encoded traversals, etc).
|
||||
|
||||
Homeservers have additional content-specific concerns:
|
||||
|
||||
- Clients may try to upload very large files. Homeservers should not store files
|
||||
that are too large and should not serve them to clients.
|
||||
|
||||
- Clients may try to upload very large images. Homeservers should not attempt to
|
||||
generate thumbnails for images that are too large.
|
||||
|
||||
- Remote homeservers may host very large files or images. Homeservers should not
|
||||
proxy or thumbnail large files or images from remote homeservers.
|
||||
|
||||
- Clients may try to upload a large number of files. Homeservers should limit the
|
||||
number and total size of media that can be uploaded by clients.
|
||||
|
||||
- Clients may try to access a large number of remote files through a homeserver.
|
||||
Homeservers should restrict the number and size of remote files that it caches.
|
||||
|
||||
- Clients or remote homeservers may try to upload malicious files targeting
|
||||
vulnerabilities in either the homeserver thumbnailing or the client decoders.
|
343
specification/modules/end_to_end_encryption.rst
Normal file
343
specification/modules/end_to_end_encryption.rst
Normal file
|
@ -0,0 +1,343 @@
|
|||
End-to-End Encryption
|
||||
=====================
|
||||
|
||||
.. _module:e2e:
|
||||
|
||||
.. TODO-doc
|
||||
- Why is this needed.
|
||||
- Overview of process
|
||||
- Implementation
|
||||
|
||||
Matrix optionally supports end-to-end encryption, allowing rooms to be created
|
||||
whose conversation contents is not decryptable or interceptable on any of the
|
||||
participating homeservers.
|
||||
|
||||
End-to-end crypto is still being designed and prototyped - notes on the design
|
||||
may be found at https://lwn.net/Articles/634144/
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
.. code::
|
||||
|
||||
1) Bob publishes the public keys and supported algorithms for his device.
|
||||
|
||||
+----------+ +--------------+
|
||||
| Bob's HS | | Bob's Device |
|
||||
+----------+ +--------------+
|
||||
| |
|
||||
|<=============|
|
||||
/keys/upload
|
||||
|
||||
2) Alice requests Bob's public key and supported algorithms.
|
||||
|
||||
+----------------+ +------------+ +----------+
|
||||
| Alice's Device | | Alice's HS | | Bob's HS |
|
||||
+----------------+ +------------+ +----------+
|
||||
| | |
|
||||
|=================>|==============>|
|
||||
/keys/query <federation>
|
||||
|
||||
3) Alice selects an algorithm and claims any one-time keys needed.
|
||||
|
||||
+----------------+ +------------+ +----------+
|
||||
| Alice's Device | | Alice's HS | | Bob's HS |
|
||||
+----------------+ +------------+ +----------+
|
||||
| | |
|
||||
|=================>|==============>|
|
||||
/keys/claim <federation>
|
||||
|
||||
4) Alice sends an encrypted message to Bob.
|
||||
|
||||
+----------------+ +------------+ +----------+ +--------------+
|
||||
| Alice's Device | | Alice's HS | | Bob's HS | | Bob's Device |
|
||||
+----------------+ +------------+ +----------+ +--------------+
|
||||
| | | |
|
||||
|----------------->|-------------->|------------->|
|
||||
/send/ <federation> <events>
|
||||
|
||||
|
||||
Algorithms
|
||||
----------
|
||||
|
||||
There are two kinds of algorithms: messaging algorithms and key algorithms.
|
||||
Messaging algorithms are used to securely send messages between devices.
|
||||
Key algorithms are used for key agreement and digital signatures.
|
||||
|
||||
Messaging Algorithm Names
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Messaging algorithm names use the extensible naming scheme used throughout this
|
||||
specification. Algorithm names that start with `m.` are reserved for algorithms
|
||||
defined by this specification. Implementations wanting to experiment with new
|
||||
algorithms are encouraged to pick algorithm names that start with their
|
||||
domain to reduce the risk of collisions.
|
||||
|
||||
Algorithm names should be short and meaningful, and should list the primitives
|
||||
used by the algorithm so that it is easier to see if the algorithm is using a
|
||||
broken primitive.
|
||||
|
||||
The name `m.olm.v1.curve25519-aes-sha2` corresponds to version 1 of the Olm
|
||||
ratchet using Curve25519 for the initial key agreement, HKDF-SHA-256 for
|
||||
ratchet key derivation, Curve25519 for the DH ratchet, HMAC-SHA-256 for the
|
||||
hash ratchet, and HKDF-SHA-256, AES-256 in CBC mode, and 8 byte truncated
|
||||
HMAC-SHA-256 for authenticated encryption.
|
||||
|
||||
A name of `m.olm.v1` is too short: it gives no information about the primitives
|
||||
in use, and is difficult to extend for different primitives. However a name of
|
||||
`m.olm.v1.ecdh-curve25519-hdkfsha256.hmacsha256.hkdfsha256-aes256-cbc-hmac64sha256`
|
||||
is too long despite giving a more precise description of the algorithm: it adds
|
||||
to the data transfer overhead and sacrifices clarity for human readers without
|
||||
adding any useful extra information.
|
||||
|
||||
Key Algorithms
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The name `ed25519` corresponds to the Ed25519 signature algorithm. The key is
|
||||
a Base64 encoded 32-byte Ed25519 public key.
|
||||
|
||||
The name `curve25519` corresponds to the Curve25519 ECDH algorithm. The key is
|
||||
a Base64 encoded 32-byte Curve25519 public key.
|
||||
|
||||
Client Behaviour
|
||||
----------------
|
||||
|
||||
Uploading Keys
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Keys are uploaded as a signed JSON object. The JSON object must include an
|
||||
ed25519 key and must be signed by that key. A device may only have one ed25519
|
||||
signing key. This key is used as the fingerprint for a device by other clients.
|
||||
|
||||
The JSON object is signed using the process given by `Signing JSON`_.
|
||||
|
||||
|
||||
.. code:: http
|
||||
|
||||
POST /_matrix/client/v2_alpha/keys/upload/<device_id> HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_keys": {
|
||||
"user_id": "<user_id>",
|
||||
"device_id": "<device_id>",
|
||||
"valid_after_ts": 1234567890123,
|
||||
"valid_until_ts": 2345678901234,
|
||||
"algorithms": [
|
||||
"<chat_algorithm>",
|
||||
],
|
||||
"keys": {
|
||||
"<key_algorithm>:<device_id>": "<key_base64>",
|
||||
},
|
||||
"signatures": {
|
||||
"<user_id>": {
|
||||
"<key_algorithm>:<device_id>": "<signature_base64>"
|
||||
} } },
|
||||
"one_time_keys": {
|
||||
"<key_algorithm>:<key_id>": "<key_base64>"
|
||||
} }
|
||||
|
||||
.. code:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"one_time_key_counts": {
|
||||
"<key_algorithm>": 50
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Downloading Keys
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Keys are downloaded as a collection of signed JSON objects. There
|
||||
will be one JSON object per device per user. If one of the user's
|
||||
devices doesn't support end-to-end encryption then their
|
||||
homeserver must synthesise a JSON object without any device keys
|
||||
for that device.
|
||||
|
||||
The JSON must be signed by both the homeserver of
|
||||
the user querying the keys and by the homeserver of the device
|
||||
being queried. This provides an audit trail if either homeserver
|
||||
lies about the keys a user owns.
|
||||
|
||||
.. code:: http
|
||||
|
||||
POST /keys/query HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_keys": {
|
||||
"<user_id>": ["<device_id>"]
|
||||
} }
|
||||
|
||||
|
||||
.. code:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"device_keys": {
|
||||
"<user_id>": {
|
||||
"<device_id>": {
|
||||
"user_id": "<user_id>",
|
||||
"device_id": "<device_id>",
|
||||
"valid_after_ts": 1234567890123,
|
||||
"valid_until_ts": 2345678901234,
|
||||
"algorithms": [
|
||||
"<chat_algorithm>",
|
||||
],
|
||||
"keys": {
|
||||
"<algorithm>:<device_id>": "<key_base64>",
|
||||
},
|
||||
"signatures": {
|
||||
"<user_id>": {
|
||||
"<key_algorithm>:<device_id>": "<signature_base64>"
|
||||
},
|
||||
"<local_server_name>": {
|
||||
"<key_algorithm>:<key_id>": "<signature_base64>"
|
||||
},
|
||||
"<remote_server_name>": {
|
||||
"<key_algorithm>:<key_id>": "<signature_base64>"
|
||||
} } } } } }
|
||||
|
||||
|
||||
Clients use ``/_matrix/client/v2_alpha/keys/query`` on their own homeservers to
|
||||
query keys for any user they wish to contact. Homeservers will respond with the
|
||||
keys for their local users and forward requests for remote users to
|
||||
``/_matrix/federation/v1/user/keys/query`` over federation to the remote
|
||||
server.
|
||||
|
||||
|
||||
Claiming One Time Keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some algorithms require one-time keys to improve their secrecy and deniability.
|
||||
These keys are used once during session establishment, and are then thrown
|
||||
away. In order for these keys to be useful for improving deniability they
|
||||
must not be signed using the ed25519 key for a device.
|
||||
|
||||
A device must generate a number of these keys and publish them onto their
|
||||
homeserver. A device must periodically check how many one-time keys their
|
||||
homeserver still has. If the number has become too small then the device must
|
||||
generate new one-time keys and upload them to the homeserver.
|
||||
|
||||
Devices must store the private part of each one-time key they upload. They can
|
||||
discard the private part of the one-time key when they receive a message using
|
||||
that key. However it's possible that a one-time key given out by a homeserver
|
||||
will never be used, so the device that generates the key will never know that
|
||||
it can discard the key. Therefore a device could end up trying to store too
|
||||
many private keys. A device that is trying to store too many private keys may
|
||||
discard keys starting with the oldest.
|
||||
|
||||
A homeserver should rate-limit the number of one-time keys that a given user or
|
||||
remote server can claim. A homeserver should discard the public part of a one
|
||||
time key once it has given that key to another user.
|
||||
|
||||
|
||||
.. code:: http
|
||||
|
||||
POST /keys/claim HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"one_time_keys": {
|
||||
"<user_id>": {
|
||||
"<device_id>": "<key_algorithm>"
|
||||
} } }
|
||||
|
||||
.. code:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"one_time_keys": {
|
||||
"<user_id>": {
|
||||
"<device_id>": {
|
||||
"<key_algorithm>:<key_id>": "<key_base64>"
|
||||
} } } }
|
||||
|
||||
|
||||
Clients use ``/_matrix/client/v2_alpha/keys/claim`` on their own homeservers to
|
||||
claim keys for any user they wish to contact. Homeservers will respond with the
|
||||
keys for their local users and forward requests for remote users to
|
||||
``/_matrix/federation/v1/user/keys/claim`` over federation to the remote
|
||||
server.
|
||||
|
||||
Sending a Message
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Encrypted messages are sent in the form.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"type": "m.room.encrypted",
|
||||
"content": {
|
||||
"algorithm": "<chat_algorithm>",
|
||||
"<algorithm_specific_keys>": "<algorithm_specific_data>"
|
||||
} }
|
||||
|
||||
|
||||
Using Olm
|
||||
#########
|
||||
|
||||
Devices that support olm must include "m.olm.v1.curve25519-aes-sha2" in their
|
||||
list of supported chat algorithms, must list a Curve25519 device key, and
|
||||
must publish Curve25519 one-time keys.
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"type": "m.room.encrypted",
|
||||
"content": {
|
||||
"algorithm": "m.olm.v1.curve25519-aes-sha2",
|
||||
"sender_key": "<sender_curve25519_key>",
|
||||
"ciphertext": {
|
||||
"<device_curve25519_key>": {
|
||||
"type": 0,
|
||||
"body": "<base_64>"
|
||||
} } } }
|
||||
|
||||
The ciphertext is a mapping from device curve25519 key to an encrypted payload
|
||||
for that device. The ``body`` is a base64 encoded message body. The type is an
|
||||
integer indicating the type of the message body: 0 for the initial pre-key
|
||||
message, 1 for ordinary messages.
|
||||
|
||||
Olm sessions will generate messages with a type of 0 until they receive a
|
||||
message. Once a session has decrypted a message it will produce messages with
|
||||
a type of 1.
|
||||
|
||||
When a client receives a message with a type of 0 it must first check if it
|
||||
already has a matching session. If it does then it will use that session to
|
||||
try to decrypt the message. If there is no existing session then the client
|
||||
must create a new session and use the new session to decrypt the message. A
|
||||
client must not persist a session or remove one-time keys used by a session
|
||||
until it has successfully decrypted a message using that session.
|
||||
|
||||
The plaintext payload is of the form:
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"type": "<type of the plaintext event>",
|
||||
"content": "<content for the plaintext event>",
|
||||
"room_id": "<the room_id>",
|
||||
"fingerprint": "<sha256 hash of the currently participating keys>"
|
||||
}
|
||||
|
||||
The type and content of the plaintext message event are given in the payload.
|
||||
Encrypting state events is not supported.
|
||||
|
||||
We include the room ID in the payload, because otherwise the homeserver would
|
||||
be able to change the room a message was sent in. We include a hash of the
|
||||
participating keys so that clients can detect if another device is unexpectedly
|
||||
included in the conversation.
|
||||
|
||||
Clients must confirm that the ``sender_key`` belongs to the user that sent the
|
||||
message.
|
87
specification/modules/guest_access.rst
Normal file
87
specification/modules/guest_access.rst
Normal file
|
@ -0,0 +1,87 @@
|
|||
Guest access
|
||||
============
|
||||
|
||||
.. _module:guest-access:
|
||||
|
||||
There are times when it is desirable for clients to be able to interact with
|
||||
rooms without having to fully register for an account on a homeserver or join
|
||||
the room. This module specifies how these clients should interact with servers
|
||||
in order to participate in rooms as guests.
|
||||
|
||||
Guest users retrieve access tokens from a homeserver using the ordinary
|
||||
`register endpoint <#post-matrix-client-api-v2-alpha-register>`_, specifying
|
||||
the ``kind`` parameter as ``guest``. They may then interact with the
|
||||
client-server API as any other user would, but will only have access to a subset
|
||||
of the API as described the Client behaviour subsection below.
|
||||
Homeservers may choose not to allow this access at all to their local users, but
|
||||
have no information about whether users on other homeservers are guests or not.
|
||||
|
||||
This module does not fully factor in federation; it relies on individual
|
||||
homeservers properly adhering to the rules set out in this module, rather than
|
||||
allowing all homeservers to enforce the rules on each other.
|
||||
|
||||
Events
|
||||
------
|
||||
{{m_room_guest_access_event}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
The following API endpoints are allowed to be accessed by guest accounts for
|
||||
retrieving events:
|
||||
|
||||
* `GET /rooms/:room_id/state <#get-matrix-client-api-v1-rooms-roomid-state>`_
|
||||
* `GET /rooms/:room_id/state/:event_type/:state_key <#get-matrix-client-api-v1-rooms-roomid-state-eventtype-statekey>`_
|
||||
* `GET /rooms/:room_id/messages <#get-matrix-client-api-v1-rooms-roomid-messages>`_
|
||||
* `GET /rooms/:room_id/initialSync <#get-matrix-client-api-v1-rooms-roomid-initialsync>`_
|
||||
|
||||
There is also a special version of the
|
||||
`GET /events <#get-matrix-client-api-v1-events>`_ endpoint:
|
||||
|
||||
{{guest_events_http_api}}
|
||||
|
||||
They will only return events which happened while the room state had the
|
||||
``m.room.history_visibility`` state event present with ``history_visibility``
|
||||
value ``world_readable``. Guest clients do not need to join rooms in order to
|
||||
receive events for them.
|
||||
|
||||
The following API endpoints are allowed to be accessed by guest accounts for
|
||||
sending events:
|
||||
|
||||
* `POST /rooms/:room_id/join <#post-matrix-client-api-v1-rooms-roomid-join>`_
|
||||
* `PUT /rooms/:room_id/send/m.room.message/:txn_id <#put-matrix-client-api-v1-rooms-roomid-send-eventtype-txnid>`_
|
||||
|
||||
Guest clients *do* need to join rooms in order to send events to them.
|
||||
|
||||
The following API endpoints are allowed to be accessed by guest accounts for
|
||||
their own account maintenance:
|
||||
|
||||
* `PUT /profile/:user_id/displayname <#put-matrix-client-api-v1-profile-userid-displayname>`_
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
Servers are required to only return events to guest accounts for rooms where
|
||||
the room state at the event had the ``m.room.history_visibility`` state event
|
||||
present with ``history_visibility`` value ``world_readable``. These events may
|
||||
be returned even if the anonymous user is not joined to the room.
|
||||
|
||||
Servers MUST only allow guest users to join rooms if the ``m.room.guest_access``
|
||||
state event is present on the room, and has the ``guest_access`` value
|
||||
``can_join``. If the ``m.room.guest_access`` event is changed to stop this from
|
||||
being the case, the server MUST set those users' ``m.room.member`` state to
|
||||
``leave``.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
Each homeserver manages its own guest accounts itself, and whether an account
|
||||
is a guest account or not is not information passed from server to server.
|
||||
Accordingly, any server participating in a room is trusted to properly enforce
|
||||
the permissions outlined in this section.
|
||||
|
||||
Clients may wish to display to their users that rooms which are
|
||||
``world_readable`` *may* be showing messages to non-joined users. There is no
|
||||
way using this module to find out whether any non-joined guest users *do* see
|
||||
events in the room, or to list or count any guest users.
|
||||
|
||||
Homeservers may want to enable protections such as captchas for guest
|
||||
registration to prevent spam, denial of service, and similar attacks.
|
||||
|
72
specification/modules/history_visibility.rst
Normal file
72
specification/modules/history_visibility.rst
Normal file
|
@ -0,0 +1,72 @@
|
|||
Room History Visibility
|
||||
=======================
|
||||
|
||||
.. _module:history-visibility:
|
||||
|
||||
This module adds support for controlling the visibility of previous events in a
|
||||
room.
|
||||
|
||||
In all cases except ``world_readable``, a user needs to join a room to view events in that room. Once they
|
||||
have joined a room, they will gain access to a subset of events in the room. How
|
||||
this subset is chosen is controlled by the ``m.room.history_visibility`` event
|
||||
outlined below. After a user has left a room, they may seen any events which they
|
||||
were allowed to see before they left the room, but no events received after they
|
||||
left.
|
||||
|
||||
The four options for the ``m.room.history_visibility`` event are:
|
||||
|
||||
- ``shared`` - Previous events are always accessible to newly joined members. All
|
||||
events in the room are accessible, even those sent when the member was not a part
|
||||
of the room.
|
||||
- ``invited`` - Previous events are accessible to newly joined members from the point
|
||||
they were invited onwards. Events stop being accessible when the member's state
|
||||
changes to something other than ``invite`` or ``join``.
|
||||
- ``joined`` - Previous events are accessible to newly joined members from the point
|
||||
they joined the room onwards. Events stop being accessible when the member's state
|
||||
changes to something other than ``join``.
|
||||
- ``world_readable`` - All events while this is the ``m.room.history_visibility`` value
|
||||
may be shared by any participating homeserver with anyone, regardless of whether
|
||||
they have ever joined the room.
|
||||
|
||||
.. WARNING::
|
||||
These options are applied at the point an event is *sent*. Checks are
|
||||
performed with the state of the ``m.room.history_visibility`` event when the
|
||||
event in question is added to the DAG. This means clients cannot
|
||||
retrospectively choose to show or hide history to new users if the setting at
|
||||
that time was more restrictive.
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
{{m_room_history_visibility_event}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
Clients that implement this module MUST present to the user the possible options
|
||||
for setting history visibility when creating a room.
|
||||
|
||||
Clients may want to display a notice that their events may be read by non-joined
|
||||
people if the value is set to ``world_readable``.
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
By default if no ``history_visibility`` is set, or if the value is not understood, the visibility is assumed to be
|
||||
``shared``. The rules governing whether a user is allowed to see an event depend
|
||||
solely on the state of the room *at that event*:
|
||||
|
||||
1. If the user was joined, allow.
|
||||
2. If the user was invited and the ``history_visibility`` was set to
|
||||
``invited`` or ``shared``, allow.
|
||||
3. If the user was neither invited nor joined but the ``history_visibility``
|
||||
was set to ``shared``, allow.
|
||||
4. Otherwise, deny.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
The default value for ``history_visibility`` is ``shared`` for
|
||||
backwards-compatibility reasons. Clients need to be aware that by not setting
|
||||
this event they are exposing all of their room history to anyone in the room.
|
||||
|
283
specification/modules/instant_messaging.rst
Normal file
283
specification/modules/instant_messaging.rst
Normal file
|
@ -0,0 +1,283 @@
|
|||
Instant Messaging
|
||||
=================
|
||||
|
||||
.. _module:im:
|
||||
|
||||
This module adds support for sending human-readable messages to a room. It also
|
||||
adds support for associating human-readable information with the room itself
|
||||
such as a room name and topic.
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
{{m_room_message_event}}
|
||||
|
||||
{{m_room_message_feedback_event}}
|
||||
|
||||
Usage of this event is discouraged for several reasons:
|
||||
- The number of feedback events will grow very quickly with the number of users
|
||||
in the room. This event provides no way to "batch" feedback, unlike the
|
||||
`receipts module`_.
|
||||
- Pairing feedback to messages gets complicated when paginating as feedback
|
||||
arrives before the message it is acknowledging.
|
||||
- There are no guarantees that the client has seen the event ID being
|
||||
acknowledged.
|
||||
|
||||
|
||||
.. _`receipts module`: `module:receipts`_
|
||||
|
||||
{{m_room_name_event}}
|
||||
|
||||
{{m_room_topic_event}}
|
||||
|
||||
{{m_room_avatar_event}}
|
||||
|
||||
m.room.message msgtypes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Each `m.room.message`_ MUST have a ``msgtype`` key which identifies the type
|
||||
of message being sent. Each type has their own required and optional keys, as
|
||||
outlined below. If a client cannot display the given ``msgtype`` then it SHOULD
|
||||
display the fallback plain text ``body`` key instead.
|
||||
|
||||
{{msgtype_events}}
|
||||
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
Clients SHOULD verify the structure of incoming events to ensure that the
|
||||
expected keys exist and that they are of the right type. Clients can discard
|
||||
malformed events or display a placeholder message to the user. Redacted
|
||||
``m.room.message`` events MUST be removed from the client. This can either be
|
||||
replaced with placeholder text (e.g. "[REDACTED]") or the redacted message can
|
||||
be removed entirely from the messages view.
|
||||
|
||||
Events which have attachments (e.g. ``m.image``, ``m.file``) SHOULD be
|
||||
uploaded using the `content repository module`_ where available. The
|
||||
resulting ``mxc://`` URI can then be used in the ``url`` key.
|
||||
|
||||
.. _`content repository module`: `module:content`_
|
||||
|
||||
Recommendations when sending messages
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Clients can send messages using ``POST`` or ``PUT`` requests. Clients SHOULD use
|
||||
``PUT`` requests with `transaction IDs`_ to make requests idempotent. This
|
||||
ensures that messages are sent exactly once even under poor network conditions.
|
||||
Clients SHOULD retry requests using an exponential-backoff algorithm for a
|
||||
certain amount of time T. It is recommended that T is no longer than 5 minutes.
|
||||
After this time, the client should stop retrying and mark the message as "unsent".
|
||||
Users should be able to manually resend unsent messages.
|
||||
|
||||
Users may type several messages at once and send them all in quick succession.
|
||||
Clients SHOULD preserve the order in which they were sent by the user. This
|
||||
means that clients should wait for the response to the previous request before
|
||||
sending the next request. This can lead to head-of-line blocking. In order to
|
||||
reduce the impact of head-of-line blocking, clients should use a queue per room
|
||||
rather than a global queue, as ordering is only relevant within a single room
|
||||
rather than between rooms.
|
||||
|
||||
.. _`transaction IDs`: `sect:txn_ids`_
|
||||
|
||||
Local echo
|
||||
~~~~~~~~~~
|
||||
|
||||
Messages SHOULD appear immediately in the message view when a user presses the
|
||||
"send" button. This should occur even if the message is still sending. This is
|
||||
referred to as "local echo". Clients SHOULD implement "local echo" of messages.
|
||||
Clients MAY display messages in a different format to indicate that the server
|
||||
has not processed the message. This format should be removed when the server
|
||||
responds.
|
||||
|
||||
Clients need to be able to match the message they are sending with the same
|
||||
message which they receive from the event stream. The echo of the same message
|
||||
from the event stream is referred to as "remote echo". Both echoes need to be
|
||||
identified as the same message in order to prevent duplicate messages being
|
||||
displayed. Ideally this pairing would occur transparently to the user: the UI
|
||||
would not flicker as it transitions from local to remote. Flickering cannot be
|
||||
fully avoided in the current client-server API. Two scenarios need to be
|
||||
considered:
|
||||
|
||||
- The client sends a message and the remote echo arrives on the event stream
|
||||
*after* the request to send the message completes.
|
||||
- The client sends a message and the remote echo arrives on the event stream
|
||||
*before* the request to send the message completes.
|
||||
|
||||
In the first scenario, the client will receive an event ID when the request to
|
||||
send the message completes. This ID can be used to identify the duplicate event
|
||||
when it arrives on the event stream. However, in the second scenario, the event
|
||||
arrives before the client has obtained an event ID. This makes it impossible to
|
||||
identify it as a duplicate event. This results in the client displaying the
|
||||
message twice for a fraction of a second before the the original request to send
|
||||
the message completes. Once it completes, the client can take remedial actions
|
||||
to remove the duplicate event by looking for duplicate event IDs. A future version
|
||||
of the client-server API will resolve this by attaching the transaction ID of the
|
||||
sending request to the event itself.
|
||||
|
||||
|
||||
Calculating the display name for a user
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Clients may wish to show the human-readable display name of a room member as
|
||||
part of a membership list, or when they send a message. However, different
|
||||
members may have conflicting display names. Display names MUST be disambiguated
|
||||
before showing them to the user, in order to prevent spoofing of other users.
|
||||
|
||||
To ensure this is done consistently across clients, clients SHOULD use the
|
||||
following algorithm to calculate a disambiguated display name for a given user:
|
||||
|
||||
1. Inspect the ``m.room.member`` state event for the relevant user id.
|
||||
2. If the ``m.room.member`` state event has no ``displayname`` field, or if
|
||||
that field has a ``null`` value, use the raw user id as the display
|
||||
name. Otherwise:
|
||||
3. If the ``m.room.member`` event has a ``displayname`` which is unique among
|
||||
members of the room with ``membership: join`` or ``membership: invite``, use
|
||||
the given ``displayname`` as the user-visible display name. Otherwise:
|
||||
4. The ``m.room.member`` event has a non-unique ``displayname``. This should be
|
||||
disambiguated using the user id, for example "display name
|
||||
(@id:homeserver.org)".
|
||||
|
||||
.. TODO-spec
|
||||
what does it mean for a ``displayname`` to be 'unique'? Are we
|
||||
case-sensitive? Do we care about homograph attacks? See
|
||||
https://matrix.org/jira/browse/SPEC-221.
|
||||
|
||||
Developers should take note of the following when implementing the above
|
||||
algorithm:
|
||||
|
||||
* The user-visible display name of one member can be affected by changes in the
|
||||
state of another member. For example, if ``@user1:matrix.org`` is present in
|
||||
a room, with ``displayname: Alice``, then when ``@user2:example.com`` joins
|
||||
the room, also with ``displayname: Alice``, *both* users must be given
|
||||
disambiguated display names. Similarly, when one of the users then changes
|
||||
their display name, there is no longer a clash, and *both* users can be given
|
||||
their chosen display name. Clients should be alert to this possibility and
|
||||
ensure that all affected users are correctly renamed.
|
||||
|
||||
* The display name of a room may also be affected by changes in the membership
|
||||
list. This is due to the room name sometimes being based on user display
|
||||
names (see `Calculating the display name for a room`_).
|
||||
|
||||
* If the entire membership list is searched for clashing display names, this
|
||||
leads to an O(N^2) implementation for building the list of room members. This
|
||||
will be very inefficient for rooms with large numbers of members. It is
|
||||
recommended that client implementations maintain a hash table mapping from
|
||||
``displayname`` to a list of room members using that name. Such a table can
|
||||
then be used for efficient calculation of whether disambiguation is needed.
|
||||
|
||||
|
||||
Displaying membership information with messages
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Clients may wish to show the display name and avatar URL of the room member who
|
||||
sent a message. This can be achieved by inspecting the ``m.room.member`` state
|
||||
event for that user ID (see `Calculating the display name for a user`_).
|
||||
|
||||
When a user paginates the message history, clients may wish to show the
|
||||
**historical** display name and avatar URL for a room member. This is possible
|
||||
because older ``m.room.member`` events are returned when paginating. This can
|
||||
be implemented efficiently by keeping two sets of room state: old and current.
|
||||
As new events arrive and/or the user paginates back in time, these two sets of
|
||||
state diverge from each other. New events update the current state and paginated
|
||||
events update the old state. When paginated events are processed sequentially,
|
||||
the old state represents the state of the room *at the time the event was sent*.
|
||||
This can then be used to set the historical display name and avatar URL.
|
||||
|
||||
|
||||
Calculating the display name for a room
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Clients may wish to show a human-readable name for a room. There are a number
|
||||
of possibilities for choosing a useful name. To ensure that rooms are named
|
||||
consistently across clients, clients SHOULD use the following algorithm to
|
||||
choose a name:
|
||||
|
||||
1. If the room has an `m.room.name`_ state event, use the name given by that
|
||||
event.
|
||||
|
||||
#. If the room has an `m.room.canonical_alias`_ state event, use the alias
|
||||
given by that event.
|
||||
|
||||
#. If neither of the above events are present, a name should be composed based
|
||||
on the members of the room. Clients should consider `m.room.member`_ events
|
||||
for users other than the logged-in user, with ``membership: join`` or
|
||||
``membership: invite``.
|
||||
|
||||
.. _active_members:
|
||||
|
||||
i. If there is only one such event, the display name for the room should be
|
||||
the `disambiguated display name`_ of the corresponding user.
|
||||
|
||||
#. If there are two such events, they should be lexicographically sorted by
|
||||
their ``state_key`` (i.e. the corresponding user IDs), and the display
|
||||
name for the room should be the `disambiguated display name`_ of both
|
||||
users: "<user1> and <user2>", or a localised variant thereof.
|
||||
|
||||
#. If there are three or more such events, the display name for the room
|
||||
should be based on the disambiguated display name of the user
|
||||
corresponding to the first such event, under a lexicographical sorting
|
||||
according to their ``state_key``. The display name should be in the
|
||||
format "<user1> and <N> others" (or a localised variant thereof), where N
|
||||
is the number of `m.room.member`_ events with ``membership: join`` or
|
||||
``membership: invite``, excluding the logged-in user and "user1".
|
||||
|
||||
For example, if Alice joins a room, where Bob (whose user id is
|
||||
``@superuser:example.com``), Carol (user id ``@carol:example.com``) and
|
||||
Dan (user id ``@dan:matrix.org``) are in conversation, Alice's
|
||||
client should show the room name as "Carol and 2 others".
|
||||
|
||||
.. TODO-spec
|
||||
Sorting by user_id certainly isn't ideal, as IDs at the start of the
|
||||
alphabet will end up dominating room names: they will all be called
|
||||
"Arathorn and 15 others". Furthermore - user_ids are not necessarily
|
||||
ASCII, which means we need to either specify a collation order, or specify
|
||||
how to choose one.
|
||||
|
||||
Ideally we might sort by the time when the user was first invited to, or
|
||||
first joined the room. But we don't have this information.
|
||||
|
||||
See https://matrix.org/jira/browse/SPEC-267 for further discussion.
|
||||
|
||||
#. If the room has no ``m.room.name`` or ``m.room.canonical_alias`` events, and
|
||||
no active members other than the current user, clients should consider
|
||||
``m.room.member`` events with ``membership: leave``. If such events exist, a
|
||||
display name such as "Empty room (was <user1> and <N> others)" (or a
|
||||
localised variant thereof) should be used, following similar rules as for
|
||||
active members (see `above <active_members_>`_).
|
||||
|
||||
#. A complete absence of ``m.room.name``, ``m.room.canonical_alias``, and
|
||||
``m.room.member`` events is likely to indicate a problem with creating the
|
||||
room or synchronising the state table; however clients should still handle
|
||||
this situation. A display name such as "Empty room" (or a localised variant
|
||||
thereof) should be used in this situation.
|
||||
|
||||
.. _`disambiguated display name`: `Calculating the display name for a user`_
|
||||
|
||||
Clients SHOULD NOT use `m.room.aliases`_ events as a source for room names, as
|
||||
it is difficult for clients to agree on the best alias to use, and aliases can
|
||||
change unexpectedly.
|
||||
|
||||
.. TODO-spec
|
||||
How can we make this less painful for clients to implement, without forcing
|
||||
an English-language implementation on them all?
|
||||
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
Homeservers SHOULD reject ``m.room.message`` events which don't have a
|
||||
``msgtype`` key, or which don't have a textual ``body`` key, with an HTTP status
|
||||
code of 400.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
Messages sent using this module are not encrypted. Messages can be encrypted
|
||||
using the `E2E module`_.
|
||||
|
||||
Clients should sanitise **all displayed keys** for unsafe HTML to prevent Cross-Site
|
||||
Scripting (XSS) attacks. This includes room names and topics.
|
||||
|
||||
.. _`E2E module`: `module:e2e`_
|
||||
|
112
specification/modules/presence.rst
Normal file
112
specification/modules/presence.rst
Normal file
|
@ -0,0 +1,112 @@
|
|||
Presence
|
||||
========
|
||||
|
||||
.. _module:presence:
|
||||
|
||||
Each user has the concept of presence information. This encodes:
|
||||
|
||||
* Whether the user is currently online
|
||||
* How recently the user was last active (as seen by the server)
|
||||
* Whether a given client considers the user to be currently idle
|
||||
* Arbitrary information about the user's current status (e.g. "in a meeting").
|
||||
|
||||
This information is collated from both per-device (``online``, ``idle``,
|
||||
``last_active``) and per-user (status) data, aggregated by the user's homeserver
|
||||
and transmitted as an ``m.presence`` event. This is one of the few events which
|
||||
are sent *outside the context of a room*. Presence events are sent to all users
|
||||
who subscribe to this user's presence through a presence list or by sharing
|
||||
membership of a room.
|
||||
|
||||
A presence list is a list of user IDs whose presence the user wants to follow.
|
||||
To be added to this list, the user being added must be invited by the list owner
|
||||
who must accept the invitation.
|
||||
|
||||
User's presence state is represented by the ``presence`` key, which is an enum
|
||||
of one of the following:
|
||||
|
||||
- ``online`` : The default state when the user is connected to an event
|
||||
stream.
|
||||
- ``unavailable`` : The user is not reachable at this time e.g. they are
|
||||
idle.
|
||||
- ``offline`` : The user is not connected to an event stream or is
|
||||
explicitly suppressing their profile information from being sent.
|
||||
- ``free_for_chat`` : The user is generally willing to receive messages
|
||||
moreso than default.
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
{{presence_events}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
Clients can manually set/get their presence/presence list using the HTTP APIs
|
||||
listed below.
|
||||
|
||||
{{presence_http_api}}
|
||||
|
||||
Idle timeout
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Clients SHOULD implement an "idle timeout". This is a timer which fires after
|
||||
a period of inactivity on the client. The definition of inactivity varies
|
||||
depending on the client. For example, web implementations may determine
|
||||
inactivity to be not moving the mouse for a certain period of time. When this
|
||||
timer fires it should set the presence state to ``unavailable``. When the user
|
||||
becomes active again (e.g. by moving the mouse) the client should set the
|
||||
presence state to ``online``. A timeout value between 1 and 5 minutes is
|
||||
recommended.
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
Each user's home server stores a "presence list" per user. Once a user accepts
|
||||
a presence list, both user's HSes must track the subscription.
|
||||
|
||||
Propagating profile information
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Because the profile display name and avatar information are likely to be used in
|
||||
many places of a client's display, changes to these fields SHOULD cause an
|
||||
automatic propagation event to occur, informing likely-interested parties of the
|
||||
new values. One of these change mechanisms SHOULD be via ``m.presence`` events.
|
||||
These events should set ``displayname`` and ``avatar_url`` to the new values
|
||||
along with the presence-specific keys. This SHOULD be done automatically by the
|
||||
home server when a user successfully changes their display name or avatar URL.
|
||||
|
||||
.. admonition:: Rationale
|
||||
|
||||
The intention for sending this information in ``m.presence`` is so that any
|
||||
"user list" can display the *current* name/presence for a user ID outside the
|
||||
scope of a room e.g. for a user page. This is bundled into a single event for
|
||||
several reasons. The user's display name can change per room. This
|
||||
event provides the "canonical" name for the user. In addition, the name is
|
||||
bundled into a single event for the ease of client implementations. If this
|
||||
was not done, the client would need to search all rooms for their own
|
||||
membership event to pull out the display name.
|
||||
|
||||
|
||||
Last active ago
|
||||
~~~~~~~~~~~~~~~
|
||||
The server maintains a timestamp of the last time it saw a
|
||||
pro-active event from the user. A pro-active event may be sending a message to a
|
||||
room or changing presence state to a higher level of availability. Levels of
|
||||
availability are defined from low to high as follows:
|
||||
|
||||
- ``offline``
|
||||
- ``unavailable``
|
||||
- ``online``
|
||||
- ``free_for_chat``
|
||||
|
||||
Based on this list, changing state from ``unavailable`` to ``online`` counts as
|
||||
a pro-active event, whereas ``online`` to ``unavailable`` does not. This
|
||||
timestamp is presented via a key called ``last_active_ago`` which gives the
|
||||
relative number of milliseconds since the pro-active event.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
Presence information is shared with all users who share a room with the target
|
||||
user. In large public rooms this could be undesirable.
|
||||
|
440
specification/modules/push.rst
Normal file
440
specification/modules/push.rst
Normal file
|
@ -0,0 +1,440 @@
|
|||
Push Notifications
|
||||
==================
|
||||
|
||||
.. _module:push:
|
||||
|
||||
::
|
||||
|
||||
+--------------------+ +-------------------+
|
||||
Matrix HTTP | | | |
|
||||
Notification Protocol | App Developer | | Device Vendor |
|
||||
| | | |
|
||||
+-------------------+ | +----------------+ | | +---------------+ |
|
||||
| | | | | | | | | |
|
||||
| Matrix Home Server+-----> Push Gateway +------> Push Provider | |
|
||||
| | | | | | | | | |
|
||||
+-^-----------------+ | +----------------+ | | +----+----------+ |
|
||||
| | | | | |
|
||||
Matrix | | | | | |
|
||||
Client/Server API + | | | | |
|
||||
| | +--------------------+ +-------------------+
|
||||
| +--+-+ |
|
||||
| | <-------------------------------------------+
|
||||
+---+ |
|
||||
| | Provider Push Protocol
|
||||
+----+
|
||||
|
||||
Mobile Device or Client
|
||||
|
||||
|
||||
This module adds support for push notifications. Homeservers send notifications
|
||||
of events to user-configured HTTP endpoints. Users may also configure a
|
||||
number of rules that determine which events generate notifications. These are
|
||||
all stored and managed by the user's homeserver. This allows user-specific push
|
||||
settings to be reused between client applications.
|
||||
|
||||
The above diagram shows the flow of push notifications being sent to a handset
|
||||
where push notifications are submitted via the handset vendor, such as Apple's
|
||||
APNS or Google's GCM. This happens as follows:
|
||||
|
||||
1. The client app signs in to a homeserver.
|
||||
2. The client app registers with its vendor's Push Provider and
|
||||
obtains a routing token of some kind.
|
||||
3. The mobile app uses the Client/Server API to add a 'pusher', providing the
|
||||
URL of a specific Push Gateway which is configured for that
|
||||
application. It also provides the routing token it has acquired from the
|
||||
Push Provider.
|
||||
4. The homeserver starts sending HTTP requests to the Push Gateway using the
|
||||
supplied URL. The Push Gateway relays this notification to
|
||||
the Push Provider, passing the routing token along with any
|
||||
necessary private credentials the provider requires to send push
|
||||
notifications.
|
||||
5. The Push Provider sends the notification to the device.
|
||||
|
||||
Definitions for terms used in this section are below:
|
||||
|
||||
Push Provider
|
||||
A push provider is a service managed by the device vendor which can send
|
||||
notifications directly to the device. Google Cloud Messaging (GCM) and Apple
|
||||
Push Notification Service (APNS) are two examples of push providers.
|
||||
|
||||
Push Gateway
|
||||
A push gateway is a server that receives HTTP event notifications from
|
||||
homeservers and passes them on to a different protocol such as APNS for iOS
|
||||
devices or GCM for Android devices. Clients inform the homeserver which
|
||||
Push Gateway to send notifications to when it sets up a Pusher.
|
||||
|
||||
.. _def:pushers:
|
||||
|
||||
Pusher
|
||||
A pusher is a worker on the homeserver that manages the sending
|
||||
of HTTP notifications for a user. A user can have multiple pushers: one per
|
||||
device.
|
||||
|
||||
Push Rule
|
||||
A push rule is a single rule that states under what *conditions* an event should
|
||||
be passed onto a push gateway and *how* the notification should be presented.
|
||||
These rules are stored on the user's homeserver. They are manually configured
|
||||
by the user, who can create and view them via the Client/Server API.
|
||||
|
||||
Push Ruleset
|
||||
A push ruleset *scopes a set of rules according to some criteria*. For example,
|
||||
some rules may only be applied for messages from a particular sender,
|
||||
a particular room, or by default. The push ruleset contains the entire set
|
||||
of scopes and rules.
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
Clients MUST configure a Pusher before they will receive push notifications.
|
||||
There is a single API endpoint for this, as described below.
|
||||
|
||||
{{pusher_http_api}}
|
||||
|
||||
.. _pushers: `def:pushers`_
|
||||
|
||||
Push Rules
|
||||
~~~~~~~~~~
|
||||
A push rule is a single rule that states under what *conditions* an event should
|
||||
be passed onto a push gateway and *how* the notification should be presented.
|
||||
There are different "kinds" of push rules and each rule has an associated
|
||||
priority. Every push rule MUST have a ``kind`` and ``rule_id``. The ``rule_id``
|
||||
is a unique string within the kind of rule and its' scope: ``rule_ids`` do not
|
||||
need to be unique between rules of the same kind on different devices. Rules may
|
||||
have extra keys depending on the value of ``kind``.The different kinds of rule
|
||||
in descending order of priority are:
|
||||
|
||||
Override Rules ``override``
|
||||
The highest priority rules are user-configured overrides.
|
||||
Content-specific Rules ``content``
|
||||
These configure behaviour for (unencrypted) messages that match certain
|
||||
patterns. Content rules take one parameter: ``pattern``, that gives the glob
|
||||
pattern to match against. This is treated in the same way as ``pattern`` for
|
||||
``event_match``.
|
||||
Room-specific Rules ``room``
|
||||
These rules change the behaviour of all messages for a given room. The
|
||||
``rule_id`` of a room rule is always the ID of the room that it affects.
|
||||
Sender-specific rules ``sender``
|
||||
These rules configure notification behaviour for messages from a specific
|
||||
Matrix user ID. The ``rule_id`` of Sender rules is always the Matrix user
|
||||
ID of the user whose messages they'd apply to.
|
||||
Underride rules ``underride``
|
||||
These are identical to ``override`` rules, but have a lower priority than
|
||||
``content``, ``room`` and ``sender`` rules.
|
||||
|
||||
Push rules may be either global or device-specific. Device specific rules only
|
||||
affect delivery of notifications via pushers with a matching ``profile_tag``.
|
||||
All device-specific rules have a higher priority than global rules. This means
|
||||
that the full list of rule kinds, in descending priority order, is as follows:
|
||||
|
||||
* Device-specific Override
|
||||
* Device-specific Content
|
||||
* Device-specific Room
|
||||
* Device-specific Sender
|
||||
* Device-specific Underride
|
||||
* Global Override
|
||||
* Global Content
|
||||
* Global Room
|
||||
* Global Sender
|
||||
* Global Underride
|
||||
|
||||
Rules with the same ``kind`` can specify an ordering priority. This determines
|
||||
which rule is selected in the event of multiple matches. For example, a rule
|
||||
matching "tea" and a separate rule matching "time" would both match the sentence
|
||||
"It's time for tea". The ordering of the rules would then resolve the tiebreak
|
||||
to determine which rule is executed. Only ``actions`` for highest priority rule
|
||||
will be sent to the Push Gateway.
|
||||
|
||||
Each rule can be enabled or disabled. Disabled rules never match. If no rules
|
||||
match an event, the homeserver MUST NOT notify the Push Gateway for that event.
|
||||
Homeservers MUST NOT notify the Push Gateway for events that the user has sent
|
||||
themselves.
|
||||
|
||||
Actions
|
||||
+++++++
|
||||
All rules have an associated list of ``actions``. An action affects if and how a
|
||||
notification is delivered for a matching event. The following actions are defined:
|
||||
|
||||
``notify``
|
||||
This causes each matching event to generate a notification.
|
||||
``dont_notify``
|
||||
This prevents each matching event from generating a notification
|
||||
``coalesce``
|
||||
This enables notifications for matching events but activates homeserver
|
||||
specific behaviour to intelligently coalesce multiple events into a single
|
||||
notification. Not all homeservers may support this. Those that do not support
|
||||
it should treat it as the ``notify`` action.
|
||||
``set_tweak``
|
||||
Sets an entry in the ``tweaks`` dictionary key that is sent in the notification
|
||||
request to the Push Gateway. This takes the form of a dictionary with a
|
||||
``set_tweak`` key whose value is the name of the tweak to set. It may also
|
||||
have a ``value`` key which is the value to which it should be set.
|
||||
|
||||
Actions that have no parameters are represented as a string. Otherwise, they are
|
||||
represented as a dictionary with a key equal to their name and other keys as
|
||||
their parameters, e.g. ``{ "set_tweak": "sound", "value": "default" }``
|
||||
|
||||
Tweaks
|
||||
^^^^^^
|
||||
The ``set_tweak`` action is used to add an entry to the 'tweaks' dictionary
|
||||
that is sent in the notification request to the Push Gateway. The following
|
||||
tweaks are defined:
|
||||
|
||||
``sound``
|
||||
A string representing the sound to be played when this notification arrives.
|
||||
A value of ``default`` means to play a default sound.
|
||||
``highlight``
|
||||
A boolean representing whether or not this message should be highlighted in
|
||||
the UI. This will normally take the form of presenting the message in a
|
||||
different colour and/or style. The UI might also be adjusted to draw
|
||||
particular attention to the room in which the event occurred. The ``value``
|
||||
may be omitted from the highlight tweak, in which case it should default to
|
||||
``true``.
|
||||
|
||||
Tweaks are passed transparently through the homeserver so client applications
|
||||
and Push Gateways may agree on additional tweaks. For example, a tweak may be
|
||||
added to specify how to flash the notification light on a mobile device.
|
||||
|
||||
Predefined Rules
|
||||
++++++++++++++++
|
||||
Homeservers can specify "server-default rules" which operate at a lower priority
|
||||
than "user-defined rules". The ``rule_id`` for all server-default rules MUST
|
||||
start with a dot (".") to identify them as "server-default". The following
|
||||
server-default rules are specified:
|
||||
|
||||
``.m.rule.contains_user_name``
|
||||
Matches any message whose content is unencrypted and contains the local part
|
||||
of the user's Matrix ID, separated by word boundaries.
|
||||
|
||||
Definition (as a ``content`` rule)::
|
||||
|
||||
{
|
||||
"rule_id": ".m.rule.contains_user_name"
|
||||
"pattern": "[the local part of the user's Matrix ID]",
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
``.m.rule.contains_display_name``
|
||||
Matches any message whose content is unencrypted and contains the user's
|
||||
current display name in the room in which it was sent.
|
||||
|
||||
Definition (this rule can only be an ``override`` or ``underride`` rule)::
|
||||
|
||||
{
|
||||
"rule_id": ".m.rule.contains_display_name"
|
||||
"conditions": [
|
||||
{
|
||||
"kind": "contains_display_name"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
``.m.rule.room_one_to_one``
|
||||
Matches any message sent in a room with exactly two members.
|
||||
|
||||
Definition (this rule can only be an ``override`` or ``underride`` rule)::
|
||||
|
||||
{
|
||||
"rule_id": ".m.rule.room_two_members"
|
||||
"conditions": [
|
||||
{
|
||||
"is": "2",
|
||||
"kind": "room_member_count"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
``.m.rule.suppress_notices``
|
||||
Matches messages with a ``msgtype`` of ``notice``. This should be an
|
||||
``override`` rule so that it takes priority over ``content`` / ``sender`` /
|
||||
``room`` rules.
|
||||
|
||||
Definition::
|
||||
|
||||
{
|
||||
'rule_id': '.m.rule.suppress_notices',
|
||||
'conditions': [
|
||||
{
|
||||
'kind': 'event_match',
|
||||
'key': 'content.msgtype',
|
||||
'pattern': 'm.notice',
|
||||
}
|
||||
],
|
||||
'actions': [
|
||||
'dont-notify',
|
||||
]
|
||||
}
|
||||
|
||||
``.m.rule.fallback``
|
||||
Matches any message. Used to define the behaviour of messages that match no
|
||||
other rules. If homeservers define this it should be the lowest priority
|
||||
``underride`` rule.
|
||||
|
||||
Definition::
|
||||
|
||||
{
|
||||
"rule_id": ".m.rule.fallback"
|
||||
"conditions": [],
|
||||
"actions": [
|
||||
"notify"
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
||||
Conditions
|
||||
++++++++++
|
||||
|
||||
Override, Underride and Default Rules MAY have a list of 'conditions'.
|
||||
All conditions must hold true for an event in order to apply the ``action`` for
|
||||
the event. A rule with no conditions always matches. Room, Sender, User and
|
||||
Content rules do not have conditions in the same way, but instead have
|
||||
predefined conditions. These conditions can be configured using the parameters
|
||||
outlined below. In the cases of room and sender rules, the ``rule_id`` of the
|
||||
rule determines its behaviour. The following conditions are defined:
|
||||
|
||||
``event_match``
|
||||
This is a glob pattern match on a field of the event. Parameters:
|
||||
|
||||
* ``key``: The dot-separated field of the event to match, e.g. ``content.body``
|
||||
* ``pattern``: The glob-style pattern to match against. Patterns with no
|
||||
special glob characters should be treated as having asterisks
|
||||
prepended and appended when testing the condition.
|
||||
|
||||
``profile_tag``
|
||||
Matches the ``profile_tag`` of the device that the notification would be
|
||||
delivered to. Parameters:
|
||||
|
||||
* ``profile_tag``: The profile_tag to match with.
|
||||
|
||||
``contains_display_name``
|
||||
This matches unencrypted messages where ``content.body`` contains the owner's
|
||||
display name in that room. This is a separate rule because display names may
|
||||
change and as such it would be hard to maintain a rule that matched the user's
|
||||
display name. This condition has no parameters.
|
||||
|
||||
``room_member_count``
|
||||
This matches the current number of members in the room. Parameters:
|
||||
|
||||
* ``is``: A decimal integer optionally prefixed by one of, ``==``, ``<``,
|
||||
``>``, ``>=`` or ``<=``. A prefix of ``<`` matches rooms where the member
|
||||
count is strictly less than the given number and so forth. If no prefix is
|
||||
present, this parameter defaults to ``==``.
|
||||
|
||||
Push Rules: API
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Clients can retrieve, add, modify and remove push rules globally or per-device
|
||||
using the APIs below.
|
||||
|
||||
{{pushrules_http_api}}
|
||||
|
||||
Examples
|
||||
++++++++
|
||||
|
||||
To create a rule that suppresses notifications for the room with ID
|
||||
``!dj234r78wl45Gh4D:matrix.org``::
|
||||
|
||||
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/api/v1/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" -d \
|
||||
'{
|
||||
"actions" : ["dont_notify"]
|
||||
}'
|
||||
|
||||
To suppress notifications for the user ``@spambot:matrix.org``::
|
||||
|
||||
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" -d \
|
||||
'{
|
||||
"actions" : ["dont_notify"]
|
||||
}'
|
||||
|
||||
To always notify for messages that contain the work 'cake' and set a specific
|
||||
sound (with a rule_id of ``SSByZWFsbHkgbGlrZSBjYWtl``)::
|
||||
|
||||
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/api/v1/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" -d \
|
||||
'{
|
||||
"pattern": "cake",
|
||||
"actions" : ["notify", {"set_sound":"cakealarm.wav"}]
|
||||
}'
|
||||
|
||||
To add a rule suppressing notifications for messages starting with 'cake' but
|
||||
ending with 'lie', superseding the previous rule::
|
||||
|
||||
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/api/v1/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" -d \
|
||||
'{
|
||||
"pattern": "cake*lie",
|
||||
"actions" : ["notify"]
|
||||
}'
|
||||
|
||||
To add a custom sound for notifications messages containing the word 'beer' in
|
||||
any rooms with 10 members or fewer (with greater importance than the room,
|
||||
sender and content rules)::
|
||||
|
||||
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/api/v1/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456" -d \
|
||||
'{
|
||||
"conditions": [
|
||||
{"kind": "event_match", "key": "content.body", "pattern": "beer" },
|
||||
{"kind": "room_member_count", "is": "<=10"}
|
||||
],
|
||||
"actions" : [
|
||||
"notify",
|
||||
{"set_sound":"beeroclock.wav"}
|
||||
]
|
||||
}'
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
This describes the format used by "HTTP" pushers to send notifications of
|
||||
events to Push Gateways. If the endpoint returns an HTTP error code, the
|
||||
homeserver SHOULD retry for a reasonable amount of time using exponential-backoff.
|
||||
|
||||
{{push_notifier_http_api}}
|
||||
|
||||
Push Gateway behaviour
|
||||
----------------------
|
||||
|
||||
Recommendations for APNS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The exact format for sending APNS notifications is flexible and up to the
|
||||
client app and its' push gateway to agree on. As APNS requires that the sender
|
||||
has a private key owned by the app developer, each app must have its own push
|
||||
gateway. It is recommended that:
|
||||
|
||||
* The APNS token be base64 encoded and used as the pushkey.
|
||||
* A different app_id be used for apps on the production and sandbox
|
||||
APS environments.
|
||||
* APNS push gateways do not attempt to wait for errors from the APNS
|
||||
gateway before returning and instead to store failures and return
|
||||
'rejected' responses next time that pushkey is used.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
Clients specify the Push Gateway URL to use to send event notifications to. This
|
||||
URL should be over HTTPS and *never* over HTTP.
|
||||
|
||||
As push notifications will pass through a Push Provider, message content
|
||||
shouldn't be sent in the push itself where possible. Instead, Push Gateways
|
||||
should send a "sync" command to instruct the client to get new events from the
|
||||
homeserver directly.
|
||||
|
84
specification/modules/receipts.rst
Normal file
84
specification/modules/receipts.rst
Normal file
|
@ -0,0 +1,84 @@
|
|||
Receipts
|
||||
========
|
||||
|
||||
.. _module:receipts:
|
||||
|
||||
This module adds in support for receipts. These receipts are a form of
|
||||
acknowledgement of an event. This module defines a single acknowledgement:
|
||||
``m.read`` which indicates that the user has read up to a given event.
|
||||
|
||||
Sending a receipt for each event can result in sending large amounts of traffic
|
||||
to a homeserver. To prevent this from becoming a problem, receipts are implemented
|
||||
using "up to" markers. This marker indicates that the acknowledgement applies
|
||||
to all events "up to and including" the event specified. For example, marking
|
||||
an event as "read" would indicate that the user had read all events *up to* the
|
||||
referenced event.
|
||||
|
||||
Events
|
||||
------
|
||||
Each ``user_id``, ``receipt_type`` pair must be associated with only a
|
||||
single ``event_id``.
|
||||
|
||||
{{m_receipt_event}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
In v1 ``/initialSync``, receipts are listed in a separate top level ``receipts``
|
||||
key. In v2 ``/sync``, receipts are contained in the ``ephemeral`` block for a
|
||||
room. New receipts that come down the event streams are deltas which update
|
||||
existing mappings. Clients should replace older receipt acknowledgements based
|
||||
on ``user_id`` and ``receipt_type`` pairs. For example::
|
||||
|
||||
Client receives m.receipt:
|
||||
user = @alice:example.com
|
||||
receipt_type = m.read
|
||||
event_id = $aaa:example.com
|
||||
|
||||
Client receives another m.receipt:
|
||||
user = @alice:example.com
|
||||
receipt_type = m.read
|
||||
event_id = $bbb:example.com
|
||||
|
||||
The client should replace the older acknowledgement for $aaa:example.com with
|
||||
this one for $bbb:example.com
|
||||
|
||||
Clients should send read receipts when there is some certainty that the event in
|
||||
question has been **displayed** to the user. Simply receiving an event does not
|
||||
provide enough certainty that the user has seen the event. The user SHOULD need
|
||||
to *take some action* such as viewing the room that the event was sent to or
|
||||
dismissing a notification in order for the event to count as "read".
|
||||
|
||||
A client can update the markers for its user by interacting with the following
|
||||
HTTP APIs.
|
||||
|
||||
{{v2_receipts_http_api}}
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
For efficiency, receipts SHOULD be batched into one event per room before
|
||||
delivering them to clients.
|
||||
|
||||
Receipts are sent across federation as EDUs with type ``m.receipt``. The
|
||||
format of the EDUs are::
|
||||
|
||||
{
|
||||
<room_id>: {
|
||||
<receipt_type>: {
|
||||
<user_id>: { <content> }
|
||||
},
|
||||
...
|
||||
},
|
||||
...
|
||||
}
|
||||
|
||||
These are always sent as deltas to previously sent receipts. Currently only a
|
||||
single ``<receipt_type>`` should be used: ``m.read``.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
As receipts are sent outside the context of the event graph, there are no
|
||||
integrity checks performed on the contents of ``m.receipt`` events.
|
||||
|
46
specification/modules/search.rst
Normal file
46
specification/modules/search.rst
Normal file
|
@ -0,0 +1,46 @@
|
|||
Server Side Search
|
||||
==================
|
||||
|
||||
.. _module:search:
|
||||
|
||||
The search API allows clients to perform full text search across events in all
|
||||
rooms that the user has been in, including those that they have left. Only
|
||||
events that the user is allowed to see will be searched, e.g. it won't include
|
||||
events in rooms that happened after you left.
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
{{search_http_api}}
|
||||
|
||||
Search Categories
|
||||
-----------------
|
||||
|
||||
The search API allows clients to search in different categories of items.
|
||||
Currently the only specified category is ``room_events``.
|
||||
|
||||
``room_events``
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
This category covers all events that the user is allowed to see, including
|
||||
events in rooms that they have left. The search is performed on certain keys of
|
||||
certain event types.
|
||||
|
||||
The supported keys to search over are:
|
||||
|
||||
- ``content.body`` in ``m.room.message``
|
||||
- ``content.name`` in ``m.room.name``
|
||||
- ``content.topic`` in ``m.room.topic``
|
||||
|
||||
The search will *not* include rooms that are end to end encrypted.
|
||||
|
||||
The results include a ``rank`` key that can be used to sort the results by
|
||||
revelancy. The higher the ``rank`` the more relevant the result is.
|
||||
|
||||
The value of ``count`` may not match the number of results. For example due to
|
||||
the search query matching 1000s of results and the server truncating the
|
||||
response.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
The server must only return results that the user has permission to see.
|
||||
|
146
specification/modules/third_party_invites.rst
Normal file
146
specification/modules/third_party_invites.rst
Normal file
|
@ -0,0 +1,146 @@
|
|||
Third party invites
|
||||
===================
|
||||
|
||||
.. _module:third-party-invites:
|
||||
|
||||
This module adds in support for inviting new members to a room where their
|
||||
Matrix user ID is not known, instead addressing them by a third party identifier
|
||||
such as an email address.
|
||||
There are two flows here; one if a Matrix user ID is known for the third party
|
||||
identifier, and one if not. Either way, the client calls ``/invite`` with the
|
||||
details of the third party identifier.
|
||||
|
||||
The homeserver asks the identity server whether a Matrix user ID is known for
|
||||
that identifier:
|
||||
|
||||
- If it is, an invite is simply issued for that user.
|
||||
|
||||
- If it is not, the homeserver asks the identity server to record the details of
|
||||
the invitation, and to notify the invitee's homeserver of this pending invitation if it gets
|
||||
a binding for this identifier in the future. The identity server returns a token
|
||||
and public key to the inviting homeserver.
|
||||
|
||||
When the invitee's homeserver receives the notification of the binding, it
|
||||
should insert an ``m.room.member`` event into the room's graph for that user,
|
||||
with ``content.membership`` = ``invite``, as well as a
|
||||
``content.third_party_invite`` property which contains proof that the invitee
|
||||
does indeed own that third party identifier.
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
{{m_room_third_party_invite_event}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
A client asks a server to invite a user by their third party identifier.
|
||||
|
||||
{{third_party_membership_http_api}}
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
All homeservers MUST verify the signature in the event's
|
||||
``content.third_party_invite.signed`` object.
|
||||
|
||||
When a homeserver inserts an ``m.room.member`` ``invite`` event into the graph
|
||||
because of an ``m.room.third_party_invite`` event,
|
||||
that homesever MUST validate that the public
|
||||
key used for signing is still valid, by checking ``key_validity_url`` from the ``m.room.third_party_invite``. It does
|
||||
this by making an HTTP GET request to ``key_validity_url``:
|
||||
|
||||
.. TODO: Link to identity server spec when it exists
|
||||
|
||||
Schema::
|
||||
|
||||
=> GET $key_validity_url?public_key=$public_key
|
||||
<= HTTP/1.1 200 OK
|
||||
{
|
||||
"valid": true|false
|
||||
}
|
||||
|
||||
|
||||
Example::
|
||||
|
||||
key_validity_url = https://identity.server/is_valid
|
||||
public_key = ALJWLAFQfqffQHFqFfeqFUOEHf4AIHfefh4
|
||||
=> GET https://identity.server/is_valid?public_key=ALJWLAFQfqffQHFqFfeqFUOEHf4AIHfefh4
|
||||
<= HTTP/1.1 200 OK
|
||||
{
|
||||
"valid": true
|
||||
}
|
||||
|
||||
with the querystring
|
||||
?public_key=``public_key``. A JSON object will be returned.
|
||||
The invitation is valid if the object contains a key named ``valid`` which is
|
||||
``true``. Otherwise, the invitation MUST be rejected. This request is
|
||||
idempotent and may be retried by the homeserver.
|
||||
|
||||
If a homeserver is joining a room for the first time because of an
|
||||
``m.room.third_party_invite``, the server which is already participating in the
|
||||
room (which is chosen as per the standard server-server specification) MUST
|
||||
validate that the public key used for signing is still valid, by checking
|
||||
``key_validity_url`` in the above described way.
|
||||
|
||||
No other homeservers may reject the joining of the room on the basis of
|
||||
``key_validity_url``, this is so that all homeservers have a consistent view of
|
||||
the room. They may, however, indicate to their clients that a member's'
|
||||
membership is questionable.
|
||||
|
||||
For example:
|
||||
|
||||
#. Room R has two participating homeservers, H1, H2
|
||||
|
||||
#. User A on H1 invites a third party identifier to room R
|
||||
|
||||
#. H1 asks the identity server for a binding to a Matrix user ID, and has none,
|
||||
so issues an ``m.room.third_party_invite`` event to the room.
|
||||
|
||||
#. When the third party user validates their identity, their homeserver H3
|
||||
is notified and attempts to issue an ``m.room.member`` event to participate
|
||||
in the room.
|
||||
|
||||
#. H3 validates the signature given to it by the identity server.
|
||||
|
||||
#. H3 then asks H1 to join it to the room. H1 *must* validate the ``signed``
|
||||
property *and* check ``key_validity_url``.
|
||||
|
||||
#. Having validated these things, H1 writes the invite event to the room, and H3
|
||||
begins participating in the room. H2 *must* accept this event.
|
||||
|
||||
The reason that no other homeserver may reject the event based on checking
|
||||
``key_validity_url`` is that we must ensure event acceptance is deterministic.
|
||||
If some other participating server doesn't have a network path to the keyserver,
|
||||
or if the keyserver were to go offline, or revoke its keys, that other server
|
||||
would reject the event and cause the participating servers' graphs to diverge.
|
||||
This relies on participating servers trusting each other, but that trust is
|
||||
already implied by the server-server protocol. Also, the public key signature
|
||||
verification must still be performed, so the attack surface here is minimized.
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
There are a number of privary and trust implications to this module.
|
||||
|
||||
It is important for user privacy that leaking the mapping between a matrix user
|
||||
ID and a third party identifier is hard. In particular, being able to look up
|
||||
all third party identifiers from a matrix user ID (and accordingly, being able
|
||||
to link each third party identifier) should be avoided wherever possible.
|
||||
To this end, the third party identifier is not put in any event, rather an
|
||||
opaque display name provided by the identity server is put into the events.
|
||||
Clients should not remember or display third party identifiers from invites,
|
||||
other than for the use of the inviter themself.
|
||||
|
||||
Homeservers are not required to trust any particular identity server(s). It is
|
||||
generally a client's responsibility to decide which identity servers it trusts,
|
||||
not a homeserver's. Accordingly, this API takes identity servers as input from
|
||||
end users, and doesn't have any specific trusted set. It is possible some
|
||||
homeservers may want to supply defaults, or reject some identity servers for
|
||||
*its* users, but no homeserver is allowed to dictate which identity servers
|
||||
*other* homeservers' users trust.
|
||||
|
||||
There is some risk of denial of service attacks by flooding homeservers or
|
||||
identity servers with many requests, or much state to store. Defending against
|
||||
these is left to the implementer's discretion.
|
||||
|
66
specification/modules/typing_notifications.rst
Normal file
66
specification/modules/typing_notifications.rst
Normal file
|
@ -0,0 +1,66 @@
|
|||
Typing Notifications
|
||||
====================
|
||||
|
||||
.. _module:typing:
|
||||
|
||||
Users may wish to be informed when another user is typing in a room. This can be
|
||||
achieved using typing notifications. These are ephemeral events scoped to a
|
||||
``room_id``. This means they do not form part of the `Event Graph`_ but still
|
||||
have a ``room_id`` key.
|
||||
|
||||
.. _Event Graph: `sect:event-graph`_
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
{{m_typing_event}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
When a client receives an ``m.typing`` event, it MUST use the user ID list to
|
||||
**REPLACE** its knowledge of every user who is currently typing. The reason for
|
||||
this is that the server *does not remember* users who are not currently typing
|
||||
as that list gets big quickly. The client should mark as not typing any user ID
|
||||
who is not in that list.
|
||||
|
||||
It is recommended that clients store a ``boolean`` indicating whether the user
|
||||
is typing or not. Whilst this value is ``true`` a timer should fire periodically
|
||||
every N seconds to send a typing HTTP request. The value of N is recommended to
|
||||
be no more than 20-30 seconds. This request should be re-sent by the client to
|
||||
continue informing the server the user is still typing. As subsequent
|
||||
requests will replace older requests, a safety margin of 5 seconds before the
|
||||
expected timeout runs out is recommended. When the user stops typing, the
|
||||
state change of the ``boolean`` to ``false`` should trigger another HTTP request
|
||||
to inform the server that the user has stopped typing.
|
||||
|
||||
{{typing_http_api}}
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
Servers MUST emit typing EDUs in a different form to ``m.typing`` events which
|
||||
are shown to clients. This form looks like::
|
||||
|
||||
{
|
||||
"type": "m.typing",
|
||||
"content": {
|
||||
"room_id": "!room-id-here:matrix.org",
|
||||
"user_id": "@user-id-here:matrix.org",
|
||||
"typing": true/false
|
||||
}
|
||||
}
|
||||
|
||||
This does not contain timing information so it is up to originating homeservers
|
||||
to ensure they eventually send "stop" notifications.
|
||||
|
||||
.. TODO
|
||||
((This will eventually need addressing, as part of the wider typing/presence
|
||||
timer addition work))
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
Clients may not wish to inform everyone in a room that they are typing and
|
||||
instead only specific users in the room.
|
||||
|
101
specification/modules/voip_events.rst
Normal file
101
specification/modules/voip_events.rst
Normal file
|
@ -0,0 +1,101 @@
|
|||
Voice over IP
|
||||
=============
|
||||
|
||||
.. _module:voip:
|
||||
|
||||
This module outlines how two users in a room can set up a Voice over IP (VoIP)
|
||||
call to each other. Voice and video calls are built upon the WebRTC 1.0 standard.
|
||||
Call signalling is achieved by sending `message events`_ to the room. As a result,
|
||||
this means that clients MUST only send call events to rooms with exactly two
|
||||
participants as currently the WebRTC standard is based around two-party
|
||||
communication.
|
||||
|
||||
.. _message events: `sect:events`_
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
{{voip_events}}
|
||||
|
||||
Client behaviour
|
||||
----------------
|
||||
|
||||
A call is set up with message events exchanged as follows:
|
||||
|
||||
::
|
||||
|
||||
Caller Callee
|
||||
[Place Call]
|
||||
m.call.invite ----------->
|
||||
m.call.candidate -------->
|
||||
[..candidates..] -------->
|
||||
[Answers call]
|
||||
<--------------- m.call.answer
|
||||
[Call is active and ongoing]
|
||||
<--------------- m.call.hangup
|
||||
|
||||
Or a rejected call:
|
||||
|
||||
::
|
||||
|
||||
Caller Callee
|
||||
m.call.invite ------------>
|
||||
m.call.candidate --------->
|
||||
[..candidates..] --------->
|
||||
[Rejects call]
|
||||
<-------------- m.call.hangup
|
||||
|
||||
Calls are negotiated according to the WebRTC specification.
|
||||
|
||||
Glare
|
||||
~~~~~
|
||||
|
||||
"Glare" is a problem which occurs when two users call each other at roughly the
|
||||
same time. This results in the call failing to set up as there already is an
|
||||
incoming/outgoing call. A glare resolution algorithm can be used to determine
|
||||
which call to hangup and which call to answer. If both clients implement the
|
||||
same algorithm then they will both select the same call and the call will be
|
||||
successfully connected.
|
||||
|
||||
|
||||
As calls are "placed" to rooms rather than users, the glare resolution algorithm
|
||||
outlined below is only considered for calls which are to the same room. The
|
||||
algorithm is as follows:
|
||||
|
||||
- If an ``m.call.invite`` to a room is received whilst the client is
|
||||
**preparing to send** an ``m.call.invite`` to the same room:
|
||||
|
||||
* the client should cancel its outgoing call and instead
|
||||
automatically accept the incoming call on behalf of the user.
|
||||
|
||||
- If an ``m.call.invite`` to a room is received **after the client has sent**
|
||||
an ``m.call.invite`` to the same room and is waiting for a response:
|
||||
|
||||
* the client should perform a lexicographical comparison of the call IDs of
|
||||
the two calls and use the *lesser* of the two calls, aborting the
|
||||
greater. If the incoming call is the lesser, the client should accept
|
||||
this call on behalf of the user.
|
||||
|
||||
|
||||
The call setup should appear seamless to the user as if they had simply placed
|
||||
a call and the other party had accepted. This means any media stream that had been
|
||||
setup for use on a call should be transferred and used for the call that
|
||||
replaces it.
|
||||
|
||||
Server behaviour
|
||||
----------------
|
||||
|
||||
The homeserver MAY provide a TURN server which clients can use to contact the
|
||||
remote party. The following HTTP API endpoints will be used by clients in order
|
||||
to get information about the TURN server.
|
||||
|
||||
{{voip_http_api}}
|
||||
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
|
||||
Calls should only be placed to rooms with one other user in them. If they are
|
||||
placed to group chat rooms it is possible that another user will intercept and
|
||||
answer the call.
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue