Add support for modules
parent
74adbfc1ec
commit
228c737f56
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 190
|
||||||
|
---
|
||||||
|
|
||||||
|
### Client Config
|
||||||
|
|
||||||
|
Clients can store custom config data for their account on their
|
||||||
|
homeserver. This account data will be synced between different devices
|
||||||
|
and can persist across installations on a particular device. Users may
|
||||||
|
only view the account data for their own account
|
||||||
|
|
||||||
|
The account\_data may be either global or scoped to a particular rooms.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
The client receives the account data as events in the `account_data`
|
||||||
|
sections of a `/sync`.
|
||||||
|
|
||||||
|
These events can also be received in a `/events` response or in the
|
||||||
|
`account_data` section of a room in `/sync`. `m.tag` events appearing in
|
||||||
|
`/events` will have a `room_id` with the room the tags are for.
|
||||||
|
|
||||||
|
#### Client Behaviour
|
||||||
|
|
||||||
|
{{account\_data\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Server Behaviour
|
||||||
|
|
||||||
|
Servers MUST reject clients from setting account data for event types
|
||||||
|
that the server manages. Currently, this only includes
|
||||||
|
[m.fully\_read]().
|
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 200
|
||||||
|
---
|
||||||
|
|
||||||
|
### Server Administration
|
||||||
|
|
||||||
|
This module adds capabilities for server administrators to inspect
|
||||||
|
server state and data.
|
||||||
|
|
||||||
|
#### Client Behaviour
|
||||||
|
|
||||||
|
{{admin\_cs\_http\_api}}
|
@ -0,0 +1,118 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 70
|
||||||
|
---
|
||||||
|
|
||||||
|
### Content repository
|
||||||
|
|
||||||
|
The content repository (or "media repository") allows users to upload
|
||||||
|
files to their homeserver for later use. For example, files which the
|
||||||
|
user wants to send to a room would be uploaded here, as would an avatar
|
||||||
|
the user wants to use.
|
||||||
|
|
||||||
|
Uploads are POSTed to a resource on the user's local homeserver which
|
||||||
|
returns a MXC URI which can later be used to GET the download. Content
|
||||||
|
is downloaded from the recipient's local homeserver, which must first
|
||||||
|
transfer the content from the origin homeserver using the same API
|
||||||
|
(unless the origin and destination homeservers are the same).
|
||||||
|
|
||||||
|
When serving content, the server SHOULD provide a
|
||||||
|
`Content-Security-Policy` header. The recommended policy is
|
||||||
|
`sandbox; default-src 'none'; script-src 'none'; plugin-types application/pdf; style-src 'unsafe-inline'; object-src 'self';`.
|
||||||
|
|
||||||
|
#### Matrix Content (MXC) URIs
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients can upload and download content using the following HTTP APIs.
|
||||||
|
|
||||||
|
{{content\_repo\_cs\_http\_api}}
|
||||||
|
|
||||||
|
##### Thumbnails
|
||||||
|
|
||||||
|
The homeserver SHOULD be able to supply thumbnails for uploaded images
|
||||||
|
and videos. The exact file types which can be thumbnailed are not
|
||||||
|
currently specified - see [Issue
|
||||||
|
\#1938](https://github.com/matrix-org/matrix-doc/issues/1938) for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
The thumbnail methods are "crop" and "scale". "scale" tries to return an
|
||||||
|
image where either the width or the height is smaller than the requested
|
||||||
|
size. The client should then scale and letterbox the image if it needs
|
||||||
|
to fit within a given rectangle. "crop" tries to return an image where
|
||||||
|
the width and height are close to the requested size and the aspect
|
||||||
|
matches the requested size. The client should scale the image if it
|
||||||
|
needs to fit within a given rectangle.
|
||||||
|
|
||||||
|
The dimensions given to the thumbnail API are the minimum size the
|
||||||
|
client would prefer. Servers must never return thumbnails smaller than
|
||||||
|
the client's requested dimensions, unless the content being thumbnailed
|
||||||
|
is smaller than the dimensions. When the content is smaller than the
|
||||||
|
requested dimensions, servers should return the original content rather
|
||||||
|
than thumbnail it.
|
||||||
|
|
||||||
|
Servers SHOULD produce thumbnails with the following dimensions and
|
||||||
|
methods:
|
||||||
|
|
||||||
|
- 32x32, crop
|
||||||
|
- 96x96, crop
|
||||||
|
- 320x240, scale
|
||||||
|
- 640x480, scale
|
||||||
|
- 800x600, scale
|
||||||
|
|
||||||
|
In summary:
|
||||||
|
- "scale" maintains the original aspect ratio of the image
|
||||||
|
- "crop" provides an image in the aspect ratio of the sizes given in
|
||||||
|
the request
|
||||||
|
- The server will return an image larger than or equal to the
|
||||||
|
dimensions requested where possible.
|
||||||
|
|
||||||
|
Servers MUST NOT upscale thumbnails under any circumstance. Servers MUST
|
||||||
|
NOT return a smaller thumbnail than requested, unless the original
|
||||||
|
content makes that impossible.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
The HTTP GET endpoint does not require any authentication. Knowing the
|
||||||
|
URL of the content is sufficient to retrieve the content, even if the
|
||||||
|
entity isn't in the room.
|
||||||
|
|
||||||
|
MXC URIs are vulnerable to directory traversal attacks such as
|
||||||
|
`mxc://127.0.0.1/../../../some_service/etc/passwd`. This would cause the
|
||||||
|
target homeserver to try to access and return this file. As such,
|
||||||
|
homeservers MUST sanitise MXC URIs by allowing only alphanumeric
|
||||||
|
(`A-Za-z0-9`), `_` and `-` characters in the `server-name` and
|
||||||
|
`media-id` values. This set of whitelisted characters allows URL-safe
|
||||||
|
base64 encodings specified in RFC 4648. Applying this character
|
||||||
|
whitelist is preferable to blacklisting `.` and `/` as there are
|
||||||
|
techniques around blacklisted characters (percent-encoded characters,
|
||||||
|
UTF-8 encoded traversals, etc).
|
||||||
|
|
||||||
|
Homeservers have additional content-specific concerns:
|
||||||
|
|
||||||
|
- Clients may try to upload very large files. Homeservers should not
|
||||||
|
store files that are too large and should not serve them to clients,
|
||||||
|
returning a HTTP 413 error with the `M_TOO_LARGE` code.
|
||||||
|
- Clients may try to upload very large images. Homeservers should not
|
||||||
|
attempt to generate thumbnails for images that are too large,
|
||||||
|
returning a HTTP 413 error with the `M_TOO_LARGE` code.
|
||||||
|
- Remote homeservers may host very large files or images. Homeservers
|
||||||
|
should not proxy or thumbnail large files or images from remote
|
||||||
|
homeservers, returning a HTTP 502 error with the `M_TOO_LARGE` code.
|
||||||
|
- Clients may try to upload a large number of files. Homeservers
|
||||||
|
should limit the number and total size of media that can be uploaded
|
||||||
|
by clients, returning a HTTP 403 error with the `M_FORBIDDEN` code.
|
||||||
|
- Clients may try to access a large number of remote files through a
|
||||||
|
homeserver. Homeservers should restrict the number and size of
|
||||||
|
remote files that it caches.
|
||||||
|
- Clients or remote homeservers may try to upload malicious files
|
||||||
|
targeting vulnerabilities in either the homeserver thumbnailing or
|
||||||
|
the client decoders.
|
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 90
|
||||||
|
---
|
||||||
|
|
||||||
|
### Device Management
|
||||||
|
|
||||||
|
This module provides a means for a user to manage their [devices]().
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients that implement this module should offer the user a list of
|
||||||
|
registered devices, as well as the means to update their display names.
|
||||||
|
Clients should also allow users to delete disused devices.
|
||||||
|
|
||||||
|
{{device\_management\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Deleting devices has security implications: it invalidates the
|
||||||
|
access\_token assigned to the device, so an attacker could use it to log
|
||||||
|
out the real user (and do it repeatedly every time the real user tries
|
||||||
|
to log in to block the attacker). Servers should require additional
|
||||||
|
authentication beyond the access token when deleting devices (for
|
||||||
|
example, requiring that the user resubmit their password).
|
||||||
|
|
||||||
|
The display names of devices are publicly visible. Clients should
|
||||||
|
consider advising the user of this.
|
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 230
|
||||||
|
---
|
||||||
|
|
||||||
|
### Direct Messaging
|
||||||
|
|
||||||
|
All communication over Matrix happens within a room. It is sometimes
|
||||||
|
desirable to offer users the concept of speaking directly to one
|
||||||
|
particular person. This module defines a way of marking certain rooms as
|
||||||
|
'direct chats' with a given person. This does not restrict the chat to
|
||||||
|
being between exactly two people since this would preclude the presence
|
||||||
|
of automated 'bot' users or even a 'personal assistant' who is able to
|
||||||
|
answer direct messages on behalf of the user in their absence.
|
||||||
|
|
||||||
|
A room may not necessarily be considered 'direct' by all members of the
|
||||||
|
room, but a signalling mechanism exists to propagate the information of
|
||||||
|
whether a chat is 'direct' to an invitee.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{m\_direct\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
To start a direct chat with another user, the inviting user's client
|
||||||
|
should set the `is_direct` flag to \_. The client should do this
|
||||||
|
whenever the flow the user has followed is one where their intention is
|
||||||
|
to speak directly with another person, as opposed to bringing that
|
||||||
|
person in to a shared room. For example, clicking on 'Start Chat' beside
|
||||||
|
a person's profile picture would imply the `is_direct` flag should be
|
||||||
|
set.
|
||||||
|
|
||||||
|
The invitee's client may use the `is_direct` flag in the
|
||||||
|
[m.room.member]() event to automatically mark the room as a direct chat
|
||||||
|
but this is not required: it may for example, prompt the user, or ignore
|
||||||
|
the flag altogether.
|
||||||
|
|
||||||
|
Both the inviting client and the invitee's client should record the fact
|
||||||
|
that the room is a direct chat by storing an `m.direct` event in the
|
||||||
|
account data using \_.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
When the `is_direct` flag is given to \_, the home server must set the
|
||||||
|
`is_direct` flag in the invite member event for any users invited in the
|
||||||
|
\_ call.
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 210
|
||||||
|
---
|
||||||
|
|
||||||
|
### Event Context
|
||||||
|
|
||||||
|
This API returns a number of events that happened just before and after
|
||||||
|
the specified event. This allows clients to get the context surrounding
|
||||||
|
an event.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
There is a single HTTP API for retrieving event context, documented
|
||||||
|
below.
|
||||||
|
|
||||||
|
{{event\_context\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
The server must only return results that the user has permission to see.
|
@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 160
|
||||||
|
---
|
||||||
|
|
||||||
|
### Guest Access
|
||||||
|
|
||||||
|
There are times when it is desirable for clients to be able to interact
|
||||||
|
with rooms without having to fully register for an account on a
|
||||||
|
homeserver or join the room. This module specifies how these clients
|
||||||
|
should interact with servers in order to participate in rooms as guests.
|
||||||
|
|
||||||
|
Guest users retrieve access tokens from a homeserver using the ordinary
|
||||||
|
[register
|
||||||
|
endpoint](#post-matrix-client-%CLIENT_MAJOR_VERSION%-register),
|
||||||
|
specifying the `kind` parameter as `guest`. They may then interact with
|
||||||
|
the client-server API as any other user would, but will only have access
|
||||||
|
to a subset of the API as described the Client behaviour subsection
|
||||||
|
below. Homeservers may choose not to allow this access at all to their
|
||||||
|
local users, but have no information about whether users on other
|
||||||
|
homeservers are guests or not.
|
||||||
|
|
||||||
|
Guest users can also upgrade their account by going through the ordinary
|
||||||
|
`register` flow, but specifying the additional POST parameter
|
||||||
|
`guest_access_token` containing the guest's access token. They are also
|
||||||
|
required to specify the `username` parameter to the value of the local
|
||||||
|
part of their username, which is otherwise optional.
|
||||||
|
|
||||||
|
This module does not fully factor in federation; it relies on individual
|
||||||
|
homeservers properly adhering to the rules set out in this module,
|
||||||
|
rather than allowing all homeservers to enforce the rules on each other.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{m\_room\_guest\_access\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
The following API endpoints are allowed to be accessed by guest accounts
|
||||||
|
for retrieving events:
|
||||||
|
|
||||||
|
- [GET
|
||||||
|
/rooms/:room\_id/state](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state)
|
||||||
|
- [GET
|
||||||
|
/rooms/:room\_id/context/:event\_id](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-context-eventid)
|
||||||
|
- [GET
|
||||||
|
/rooms/:room\_id/event/:event\_id](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-event-eventid)
|
||||||
|
- [GET
|
||||||
|
/rooms/:room\_id/state/:event\_type/:state\_key](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-state-eventtype-statekey)
|
||||||
|
- [GET
|
||||||
|
/rooms/:room\_id/messages](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages)
|
||||||
|
- [GET
|
||||||
|
/rooms/:room\_id/members](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-members)
|
||||||
|
- [GET
|
||||||
|
/rooms/:room\_id/initialSync](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync)
|
||||||
|
- [GET /sync](#get-matrix-client-%CLIENT_MAJOR_VERSION%-sync)
|
||||||
|
- [GET /events]() as used for room previews.
|
||||||
|
|
||||||
|
The following API endpoints are allowed to be accessed by guest accounts
|
||||||
|
for sending events:
|
||||||
|
|
||||||
|
- [POST
|
||||||
|
/rooms/:room\_id/join](#post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-join)
|
||||||
|
- [POST
|
||||||
|
/rooms/:room\_id/leave](#post-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-leave)
|
||||||
|
- [PUT
|
||||||
|
/rooms/:room\_id/send/m.room.message/:txn\_id](#put-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-send-eventtype-txnid)
|
||||||
|
- [PUT
|
||||||
|
/sendToDevice/{eventType}/{txnId}](#put-matrix-client-%CLIENT_MAJOR_VERSION%-sendtodevice-eventtype-txnid)
|
||||||
|
|
||||||
|
The following API endpoints are allowed to be accessed by guest accounts
|
||||||
|
for their own account maintenance:
|
||||||
|
|
||||||
|
- [PUT
|
||||||
|
/profile/:user\_id/displayname](#put-matrix-client-%CLIENT_MAJOR_VERSION%-profile-userid-displayname)
|
||||||
|
- [GET /devices](#get-matrix-client-%CLIENT_MAJOR_VERSION%-devices)
|
||||||
|
- [GET
|
||||||
|
/devices/{deviceId}](#get-matrix-client-%CLIENT_MAJOR_VERSION%-devices-deviceid)
|
||||||
|
- [PUT
|
||||||
|
/devices/{deviceId}](#put-matrix-client-%CLIENT_MAJOR_VERSION%-devices-deviceid)
|
||||||
|
|
||||||
|
The following API endpoints are allowed to be accessed by guest accounts
|
||||||
|
for end-to-end encryption:
|
||||||
|
|
||||||
|
- [POST
|
||||||
|
/keys/upload](#post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-upload)
|
||||||
|
- [POST
|
||||||
|
/keys/query](#post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-query)
|
||||||
|
- [POST
|
||||||
|
/keys/claim](#post-matrix-client-%CLIENT_MAJOR_VERSION%-keys-claim)
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Servers MUST only allow guest users to join rooms if the
|
||||||
|
`m.room.guest_access` state event is present on the room, and has the
|
||||||
|
`guest_access` value `can_join`. If the `m.room.guest_access` event is
|
||||||
|
changed to stop this from being the case, the server MUST set those
|
||||||
|
users' `m.room.member` state to `leave`.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Each homeserver manages its own guest accounts itself, and whether an
|
||||||
|
account is a guest account or not is not information passed from server
|
||||||
|
to server. Accordingly, any server participating in a room is trusted to
|
||||||
|
properly enforce the permissions outlined in this section.
|
||||||
|
|
||||||
|
Homeservers may want to enable protections such as captchas for guest
|
||||||
|
registration to prevent spam, denial of service, and similar attacks.
|
@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 120
|
||||||
|
---
|
||||||
|
|
||||||
|
### Room History Visibility
|
||||||
|
|
||||||
|
This module adds support for controlling the visibility of previous
|
||||||
|
events in a room.
|
||||||
|
|
||||||
|
In all cases except `world_readable`, a user needs to join a room to
|
||||||
|
view events in that room. Once they have joined a room, they will gain
|
||||||
|
access to a subset of events in the room. How this subset is chosen is
|
||||||
|
controlled by the `m.room.history_visibility` event outlined below.
|
||||||
|
After a user has left a room, they may see any events which they were
|
||||||
|
allowed to see before they left the room, but no events received after
|
||||||
|
they left.
|
||||||
|
|
||||||
|
The four options for the `m.room.history_visibility` event are:
|
||||||
|
|
||||||
|
- `world_readable` - All events while this is the
|
||||||
|
`m.room.history_visibility` value may be shared by any participating
|
||||||
|
homeserver with anyone, regardless of whether they have ever joined
|
||||||
|
the room.
|
||||||
|
- `shared` - Previous events are always accessible to newly joined
|
||||||
|
members. All events in the room are accessible, even those sent when
|
||||||
|
the member was not a part of the room.
|
||||||
|
- `invited` - Events are accessible to newly joined members from the
|
||||||
|
point they were invited onwards. Events stop being accessible when
|
||||||
|
the member's state changes to something other than `invite` or
|
||||||
|
`join`.
|
||||||
|
- `joined` - Events are accessible to newly joined members from the
|
||||||
|
point they joined the room onwards. Events stop being accessible
|
||||||
|
when the member's state changes to something other than `join`.
|
||||||
|
|
||||||
|
Warning
|
||||||
|
|
||||||
|
These options are applied at the point an event is *sent*. Checks are
|
||||||
|
performed with the state of the `m.room.history_visibility` event when
|
||||||
|
the event in question is added to the DAG. This means clients cannot
|
||||||
|
retrospectively choose to show or hide history to new users if the
|
||||||
|
setting at that time was more restrictive.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{m\_room\_history\_visibility\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients that implement this module MUST present to the user the possible
|
||||||
|
options for setting history visibility when creating a room.
|
||||||
|
|
||||||
|
Clients may want to display a notice that their events may be read by
|
||||||
|
non-joined people if the value is set to `world_readable`.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
By default if no `history_visibility` is set, or if the value is not
|
||||||
|
understood, the visibility is assumed to be `shared`. The rules
|
||||||
|
governing whether a user is allowed to see an event depend on the state
|
||||||
|
of the room *at that event*.
|
||||||
|
|
||||||
|
1. If the `history_visibility` was set to `world_readable`, allow.
|
||||||
|
2. If the user's `membership` was `join`, allow.
|
||||||
|
3. If `history_visibility` was set to `shared`, and the user joined the
|
||||||
|
room at any point after the event was sent, allow.
|
||||||
|
4. If the user's `membership` was `invite`, and the
|
||||||
|
`history_visibility` was set to `invited`, allow.
|
||||||
|
5. Otherwise, deny.
|
||||||
|
|
||||||
|
For `m.room.history_visibility` events themselves, the user should be
|
||||||
|
allowed to see the event if the `history_visibility` before *or* after
|
||||||
|
the event would allow them to see it. (For example, a user should be
|
||||||
|
able to see `m.room.history_visibility` events which change the
|
||||||
|
`history_visibility` from `world_readable` to `joined` *or* from
|
||||||
|
`joined` to `world_readable`, even if that user was not a member of the
|
||||||
|
room.)
|
||||||
|
|
||||||
|
Likewise, for the user's own `m.room.member` events, the user should be
|
||||||
|
allowed to see the event if their `membership` before *or* after the
|
||||||
|
event would allow them to see it. (For example, a user can always see
|
||||||
|
`m.room.member` events which set their membership to `join`, or which
|
||||||
|
change their membership from `join` to any other value, even if
|
||||||
|
`history_visibility` is `joined`.)
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
The default value for `history_visibility` is `shared` for
|
||||||
|
backwards-compatibility reasons. Clients need to be aware that by not
|
||||||
|
setting this event they are exposing all of their room history to anyone
|
||||||
|
in the room.
|
@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 240
|
||||||
|
---
|
||||||
|
|
||||||
|
### Ignoring Users
|
||||||
|
|
||||||
|
With all the communication through Matrix it may be desirable to ignore
|
||||||
|
a particular user for whatever reason. This module defines how clients
|
||||||
|
and servers can implement the ignoring of users.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{m\_ignored\_user\_list\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
To ignore a user, effectively blocking them, the client should add the
|
||||||
|
target user to the `m.ignored_user_list` event in their account data
|
||||||
|
using \_. Once ignored, the client will no longer receive events sent by
|
||||||
|
that user, with the exception of state events. The client should either
|
||||||
|
hide previous content sent by the newly ignored user or perform a new
|
||||||
|
`/sync` with no previous token.
|
||||||
|
|
||||||
|
Invites to new rooms by ignored users will not be sent to the client.
|
||||||
|
The server may optionally reject the invite on behalf of the client.
|
||||||
|
|
||||||
|
State events will still be sent to the client, even if the user is
|
||||||
|
ignored. This is to ensure parts, such as the room name, do not appear
|
||||||
|
different to the user just because they ignored the sender.
|
||||||
|
|
||||||
|
To remove a user from the ignored users list, remove them from the
|
||||||
|
account data event. The server will resume sending events from the
|
||||||
|
previously ignored user, however it should not send events that were
|
||||||
|
missed while the user was ignored. To receive the events that were sent
|
||||||
|
while the user was ignored the client should perform a fresh sync. The
|
||||||
|
client may also un-hide any events it previously hid due to the user
|
||||||
|
becoming ignored.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Following an update of the `m.ignored_user_list`, the sync API for all
|
||||||
|
clients should immediately start ignoring (or un-ignoring) the user.
|
||||||
|
Clients are responsible for determining if they should hide previously
|
||||||
|
sent events or to start a new sync stream.
|
||||||
|
|
||||||
|
Servers must still send state events sent by ignored users to clients.
|
||||||
|
|
||||||
|
Servers must not send room invites from ignored users to clients.
|
||||||
|
Servers may optionally decide to reject the invite, however.
|
@ -0,0 +1,482 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 10
|
||||||
|
---
|
||||||
|
|
||||||
|
### Instant Messaging
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
{{m\_room\_name\_event}}
|
||||||
|
|
||||||
|
{{m\_room\_topic\_event}}
|
||||||
|
|
||||||
|
{{m\_room\_avatar\_event}}
|
||||||
|
|
||||||
|
{{m\_room\_pinned\_events\_event}}
|
||||||
|
|
||||||
|
##### m.room.message msgtypes
|
||||||
|
|
||||||
|
Each [m.room.message]() MUST have a `msgtype` key which identifies the
|
||||||
|
type of message being sent. Each type has their own required and
|
||||||
|
optional keys, as outlined below. If a client cannot display the given
|
||||||
|
`msgtype` then it SHOULD display the fallback plain text `body` key
|
||||||
|
instead.
|
||||||
|
|
||||||
|
Some message types support HTML in the event content that clients should
|
||||||
|
prefer to display if available. Currently `m.text`, `m.emote`, and
|
||||||
|
`m.notice` support an additional `format` parameter of
|
||||||
|
`org.matrix.custom.html`. When this field is present, a `formatted_body`
|
||||||
|
with the HTML must be provided. The plain text version of the HTML
|
||||||
|
should be provided in the `body`.
|
||||||
|
|
||||||
|
Clients should limit the HTML they render to avoid Cross-Site Scripting,
|
||||||
|
HTML injection, and similar attacks. The strongly suggested set of HTML
|
||||||
|
tags to permit, denying the use and rendering of anything else, is:
|
||||||
|
`font`, `del`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `blockquote`, `p`,
|
||||||
|
`a`, `ul`, `ol`, `sup`, `sub`, `li`, `b`, `i`, `u`, `strong`, `em`,
|
||||||
|
`strike`, `code`, `hr`, `br`, `div`, `table`, `thead`, `tbody`, `tr`,
|
||||||
|
`th`, `td`, `caption`, `pre`, `span`, `img`.
|
||||||
|
|
||||||
|
Not all attributes on those tags should be permitted as they may be
|
||||||
|
avenues for other disruption attempts, such as adding `onclick` handlers
|
||||||
|
or excessively large text. Clients should only permit the attributes
|
||||||
|
listed for the tags below. Where `data-mx-bg-color` and `data-mx-color`
|
||||||
|
are listed, clients should translate the value (a 6-character hex color
|
||||||
|
code) to the appropriate CSS/attributes for the tag.
|
||||||
|
|
||||||
|
`font`
|
||||||
|
`data-mx-bg-color`, `data-mx-color`
|
||||||
|
|
||||||
|
`span`
|
||||||
|
`data-mx-bg-color`, `data-mx-color`
|
||||||
|
|
||||||
|
`a`
|
||||||
|
`name`, `target`, `href` (provided the value is not relative and has a
|
||||||
|
scheme matching one of: `https`, `http`, `ftp`, `mailto`, `magnet`)
|
||||||
|
|
||||||
|
`img`
|
||||||
|
`width`, `height`, `alt`, `title`, `src` (provided it is a [Matrix
|
||||||
|
Content (MXC) URI]())
|
||||||
|
|
||||||
|
`ol`
|
||||||
|
`start`
|
||||||
|
|
||||||
|
`code`
|
||||||
|
`class` (only classes which start with `language-` for syntax
|
||||||
|
highlighting)
|
||||||
|
|
||||||
|
Additionally, web clients should ensure that *all* `a` tags get a
|
||||||
|
`rel="noopener"` to prevent the target page from referencing the
|
||||||
|
client's tab/window.
|
||||||
|
|
||||||
|
Tags must not be nested more than 100 levels deep. Clients should only
|
||||||
|
support the subset of tags they can render, falling back to other
|
||||||
|
representations of the tags where possible. For example, a client may
|
||||||
|
not be able to render tables correctly and instead could fall back to
|
||||||
|
rendering tab-delimited text.
|
||||||
|
|
||||||
|
In addition to not rendering unsafe HTML, clients should not emit unsafe
|
||||||
|
HTML in events. Likewise, clients should not generate HTML that is not
|
||||||
|
needed, such as extra paragraph tags surrounding text due to Rich Text
|
||||||
|
Editors. HTML included in events should otherwise be valid, such as
|
||||||
|
having appropriate closing tags, appropriate attributes (considering the
|
||||||
|
custom ones defined in this specification), and generally valid
|
||||||
|
structure.
|
||||||
|
|
||||||
|
A special tag, `mx-reply`, may appear on rich replies (described below)
|
||||||
|
and should be allowed if, and only if, the tag appears as the very first
|
||||||
|
tag in the `formatted_body`. The tag cannot be nested and cannot be
|
||||||
|
located after another tag in the tree. Because the tag contains HTML, an
|
||||||
|
`mx-reply` is expected to have a partner closing tag and should be
|
||||||
|
treated similar to a `div`. Clients that support rich replies will end
|
||||||
|
up stripping the tag and its contents and therefore may wish to exclude
|
||||||
|
the tag entirely.
|
||||||
|
|
||||||
|
Note
|
||||||
|
|
||||||
|
A future iteration of the specification will support more powerful and
|
||||||
|
extensible message formatting options, such as the proposal
|
||||||
|
[MSC1767](https://github.com/matrix-org/matrix-doc/pull/1767).
|
||||||
|
|
||||||
|
{{msgtype\_events}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients SHOULD verify the structure of incoming events to ensure that
|
||||||
|
the expected keys exist and that they are of the right type. Clients can
|
||||||
|
discard malformed events or display a placeholder message to the user.
|
||||||
|
Redacted `m.room.message` events MUST be removed from the client. This
|
||||||
|
can either be replaced with placeholder text (e.g. "\[REDACTED\]") or
|
||||||
|
the redacted message can be removed entirely from the messages view.
|
||||||
|
|
||||||
|
Events which have attachments (e.g. `m.image`, `m.file`) SHOULD be
|
||||||
|
uploaded using the [content repository module]() where available. The
|
||||||
|
resulting `mxc://` URI can then be used in the `url` key.
|
||||||
|
|
||||||
|
Clients MAY include a client generated thumbnail image for an attachment
|
||||||
|
under a `info.thumbnail_url` key. The thumbnail SHOULD also be a
|
||||||
|
`mxc://` URI. Clients displaying events with attachments can either use
|
||||||
|
the client generated thumbnail or ask its homeserver to generate a
|
||||||
|
thumbnail from the original attachment using the [content repository
|
||||||
|
module]().
|
||||||
|
|
||||||
|
##### Recommendations when sending messages
|
||||||
|
|
||||||
|
In the event of send failure, clients SHOULD retry requests using an
|
||||||
|
exponential-backoff algorithm for a certain amount of time T. It is
|
||||||
|
recommended that T is no longer than 5 minutes. After this time, the
|
||||||
|
client should stop retrying and mark the message as "unsent". Users
|
||||||
|
should be able to manually resend unsent messages.
|
||||||
|
|
||||||
|
Users may type several messages at once and send them all in quick
|
||||||
|
succession. Clients SHOULD preserve the order in which they were sent by
|
||||||
|
the user. This means that clients should wait for the response to the
|
||||||
|
previous request before sending the next request. This can lead to
|
||||||
|
head-of-line blocking. In order to reduce the impact of head-of-line
|
||||||
|
blocking, clients should use a queue per room rather than a global
|
||||||
|
queue, as ordering is only relevant within a single room rather than
|
||||||
|
between rooms.
|
||||||
|
|
||||||
|
##### Local echo
|
||||||
|
|
||||||
|
Messages SHOULD appear immediately in the message view when a user
|
||||||
|
presses the "send" button. This should occur even if the message is
|
||||||
|
still sending. This is referred to as "local echo". Clients SHOULD
|
||||||
|
implement "local echo" of messages. Clients MAY display messages in a
|
||||||
|
different format to indicate that the server has not processed the
|
||||||
|
message. This format should be removed when the server responds.
|
||||||
|
|
||||||
|
Clients need to be able to match the message they are sending with the
|
||||||
|
same message which they receive from the event stream. The echo of the
|
||||||
|
same message from the event stream is referred to as "remote echo". Both
|
||||||
|
echoes need to be identified as the same message in order to prevent
|
||||||
|
duplicate messages being displayed. Ideally this pairing would occur
|
||||||
|
transparently to the user: the UI would not flicker as it transitions
|
||||||
|
from local to remote. Flickering can be reduced through clients making
|
||||||
|
use of the transaction ID they used to send a particular event. The
|
||||||
|
transaction ID used will be included in the event's `unsigned` data as
|
||||||
|
`transaction_id` when it arrives through the event stream.
|
||||||
|
|
||||||
|
Clients unable to make use of the transaction ID are likely to
|
||||||
|
experience flickering when the remote echo arrives on the event stream
|
||||||
|
*before* the request to send the message completes. In that case the
|
||||||
|
event arrives before the client has obtained an event ID, making it
|
||||||
|
impossible to identify it as a remote echo. This results in the client
|
||||||
|
displaying the message twice for some time (depending on the server
|
||||||
|
responsiveness) before the original request to send the message
|
||||||
|
completes. Once it completes, the client can take remedial actions to
|
||||||
|
remove the duplicate event by looking for duplicate event IDs.
|
||||||
|
|
||||||
|
##### Calculating the display name for a user
|
||||||
|
|
||||||
|
Clients may wish to show the human-readable display name of a room
|
||||||
|
member as part of a membership list, or when they send a message.
|
||||||
|
However, different members may have conflicting display names. Display
|
||||||
|
names MUST be disambiguated before showing them to the user, in order to
|
||||||
|
prevent spoofing of other users.
|
||||||
|
|
||||||
|
To ensure this is done consistently across clients, clients SHOULD use
|
||||||
|
the following algorithm to calculate a disambiguated display name for a
|
||||||
|
given user:
|
||||||
|
|
||||||
|
1. Inspect the `m.room.member` state event for the relevant user id.
|
||||||
|
2. If the `m.room.member` state event has no `displayname` field, or if
|
||||||
|
that field has a `null` value, use the raw user id as the display
|
||||||
|
name. Otherwise:
|
||||||
|
3. If the `m.room.member` event has a `displayname` which is unique
|
||||||
|
among members of the room with `membership: join` or
|
||||||
|
`membership: invite`, use the given `displayname` as the
|
||||||
|
user-visible display name. Otherwise:
|
||||||
|
4. The `m.room.member` event has a non-unique `displayname`. This
|
||||||
|
should be disambiguated using the user id, for example "display name
|
||||||
|
(@id:homeserver.org)".
|
||||||
|
|
||||||
|
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](#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](#calculating-the-display-name-for-a-user)).
|
||||||
|
|
||||||
|
When a user paginates the message history, clients may wish to show the
|
||||||
|
**historical** display name and avatar URL for a room member. This is
|
||||||
|
possible because older `m.room.member` events are returned when
|
||||||
|
paginating. This can be implemented efficiently by keeping two sets of
|
||||||
|
room state: old and current. As new events arrive and/or the user
|
||||||
|
paginates back in time, these two sets of state diverge from each other.
|
||||||
|
New events update the current state and paginated events update the old
|
||||||
|
state. When paginated events are processed sequentially, the old state
|
||||||
|
represents the state of the room *at the time the event was sent*. This
|
||||||
|
can then be used to set the historical display name and avatar URL.
|
||||||
|
|
||||||
|
##### Calculating the display name for a room
|
||||||
|
|
||||||
|
Clients may wish to show a human-readable name for a room. There are a
|
||||||
|
number of possibilities for choosing a useful name. To ensure that rooms
|
||||||
|
are named consistently across clients, clients SHOULD use the following
|
||||||
|
algorithm to choose a name:
|
||||||
|
|
||||||
|
1. If the room has an [m.room.name]() state event with a non-empty
|
||||||
|
`name` field, use the name given by that field.
|
||||||
|
2. If the room has an [m.room.canonical\_alias]() state event with a
|
||||||
|
valid `alias` field, use the alias given by that field as the name.
|
||||||
|
Note that clients should avoid using `alt_aliases` when calculating
|
||||||
|
the room name.
|
||||||
|
3. If none of the above conditions are met, a name should be composed
|
||||||
|
based on the members of the room. Clients should consider
|
||||||
|
[m.room.member]() events for users other than the logged-in user, as
|
||||||
|
defined below.
|
||||||
|
1. If the number of `m.heroes` for the room are greater or equal to
|
||||||
|
`m.joined_member_count + m.invited_member_count - 1`, then use
|
||||||
|
the membership events for the heroes to calculate display names
|
||||||
|
for the users ([disambiguating them if
|
||||||
|
required](#calculating-the-display-name-for-a-user)) and
|
||||||
|
concatenating them. For example, the client may choose to show
|
||||||
|
"Alice, Bob, and Charlie (@charlie:example.org)" as the room
|
||||||
|
name. The client may optionally limit the number of users it
|
||||||
|
uses to generate a room name.
|
||||||
|
2. If there are fewer heroes than
|
||||||
|
`m.joined_member_count + m.invited_member_count - 1`, and
|
||||||
|
`m.joined_member_count + m.invited_member_count` is greater than
|
||||||
|
1, the client should use the heroes to calculate display names
|
||||||
|
for the users ([disambiguating them if
|
||||||
|
required](#calculating-the-display-name-for-a-user)) and
|
||||||
|
concatenating them alongside a count of the remaining users. For
|
||||||
|
example, "Alice, Bob, and 1234 others".
|
||||||
|
3. If `m.joined_member_count + m.invited_member_count` is less than
|
||||||
|
or equal to 1 (indicating the member is alone), the client
|
||||||
|
should use the rules above to indicate that the room was empty.
|
||||||
|
For example, "Empty Room (was Alice)", "Empty Room (was Alice
|
||||||
|
and 1234 others)", or "Empty Room" if there are no heroes.
|
||||||
|
|
||||||
|
Clients SHOULD internationalise the room name to the user's language
|
||||||
|
when using the `m.heroes` to calculate the name. Clients SHOULD use
|
||||||
|
minimum 5 heroes to calculate room names where possible, but may use
|
||||||
|
more or less to fit better with their user experience.
|
||||||
|
|
||||||
|
##### Forming relationships between events
|
||||||
|
|
||||||
|
In some cases, events may wish to reference other events. This could be
|
||||||
|
to form a thread of messages for the user to follow along with, or to
|
||||||
|
provide more context as to what a particular event is describing.
|
||||||
|
Currently, the only kind of relation defined is a "rich reply" where a
|
||||||
|
user may reference another message to create a thread-like conversation.
|
||||||
|
|
||||||
|
Relationships are defined under an `m.relates_to` key in the event's
|
||||||
|
`content`. If the event is of the type `m.room.encrypted`, the
|
||||||
|
`m.relates_to` key MUST NOT be covered by the encryption and instead be
|
||||||
|
put alongside the encryption information held in the `content`.
|
||||||
|
|
||||||
|
###### Rich replies
|
||||||
|
|
||||||
|
Users may wish to reference another message when forming their own
|
||||||
|
message, and clients may wish to better embed the referenced message for
|
||||||
|
the user to have a better context for the conversation being had. This
|
||||||
|
sort of embedding another message in a message is known as a "rich
|
||||||
|
reply", or occasionally just a "reply".
|
||||||
|
|
||||||
|
A rich reply is formed through use of an `m.relates_to` relation for
|
||||||
|
`m.in_reply_to` where a single key, `event_id`, is used to reference the
|
||||||
|
event being replied to. The referenced event ID SHOULD belong to the
|
||||||
|
same room where the reply is being sent. Clients should be cautious of
|
||||||
|
the event ID belonging to another room, or being invalid entirely. Rich
|
||||||
|
replies can only be constructed in the form of `m.room.message` events
|
||||||
|
with a `msgtype` of `m.text` or `m.notice`. Due to the fallback
|
||||||
|
requirements, rich replies cannot be constructed for types of `m.emote`,
|
||||||
|
`m.file`, etc. Rich replies may reference any other `m.room.message`
|
||||||
|
event, however. Rich replies may reference another event which also has
|
||||||
|
a rich reply, infinitely.
|
||||||
|
|
||||||
|
An `m.in_reply_to` relationship looks like the following:
|
||||||
|
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"type": "m.room.message",
|
||||||
|
"content": {
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": "<body including fallback>",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "<HTML including fallback>",
|
||||||
|
"m.relates_to": {
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": "$another:event.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
####### Fallbacks and event representation
|
||||||
|
|
||||||
|
Some clients may not have support for rich replies and therefore need a
|
||||||
|
fallback to use instead. Clients that do not support rich replies should
|
||||||
|
render the event as if rich replies were not special.
|
||||||
|
|
||||||
|
Clients that do support rich replies MUST provide the fallback format on
|
||||||
|
replies, and MUST strip the fallback before rendering the reply. Rich
|
||||||
|
replies MUST have a `format` of `org.matrix.custom.html` and therefore a
|
||||||
|
`formatted_body` alongside the `body` and appropriate `msgtype`. The
|
||||||
|
specific fallback text is different for each `msgtype`, however the
|
||||||
|
general format for the `body` is:
|
||||||
|
|
||||||
|
> <@alice:example.org> This is the original body
|
||||||
|
|
||||||
|
This is where the reply goes
|
||||||
|
|
||||||
|
The `formatted_body` should use the following template:
|
||||||
|
|
||||||
|
<mx-reply>
|
||||||
|
<blockquote>
|
||||||
|
<a href="https://matrix.to/#/!somewhere:example.org/$event:example.org">In reply to</a>
|
||||||
|
<a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
||||||
|
<br />
|
||||||
|
<!-- This is where the related event's HTML would be. -->
|
||||||
|
</blockquote>
|
||||||
|
</mx-reply>
|
||||||
|
This is where the reply goes.
|
||||||
|
|
||||||
|
If the related event does not have a `formatted_body`, the event's
|
||||||
|
`body` should be considered after encoding any HTML special characters.
|
||||||
|
Note that the `href` in both of the anchors use a [matrix.to
|
||||||
|
URI](../appendices.html#matrix-to-navigation).
|
||||||
|
|
||||||
|
######## Stripping the fallback
|
||||||
|
|
||||||
|
Clients which support rich replies MUST strip the fallback from the
|
||||||
|
event before rendering the event. This is because the text provided in
|
||||||
|
the fallback cannot be trusted to be an accurate representation of the
|
||||||
|
event. After removing the fallback, clients are recommended to represent
|
||||||
|
the event referenced by `m.in_reply_to` similar to the fallback's
|
||||||
|
representation, although clients do have creative freedom for their user
|
||||||
|
interface. Clients should prefer the `formatted_body` over the `body`,
|
||||||
|
just like with other `m.room.message` events.
|
||||||
|
|
||||||
|
To strip the fallback on the `body`, the client should iterate over each
|
||||||
|
line of the string, removing any lines that start with the fallback
|
||||||
|
prefix ("> ", including the space, without quotes) and stopping when
|
||||||
|
a line is encountered without the prefix. This prefix is known as the
|
||||||
|
"fallback prefix sequence".
|
||||||
|
|
||||||
|
To strip the fallback on the `formatted_body`, the client should remove
|
||||||
|
the entirety of the `mx-reply` tag.
|
||||||
|
|
||||||
|
######## Fallback for `m.text`, `m.notice`, and unrecognised message types
|
||||||
|
|
||||||
|
Using the prefix sequence, the first line of the related event's `body`
|
||||||
|
should be prefixed with the user's ID, followed by each line being
|
||||||
|
prefixed with the fallback prefix sequence. For example:
|
||||||
|
|
||||||
|
> <@alice:example.org> This is the first line
|
||||||
|
> This is the second line
|
||||||
|
|
||||||
|
This is the reply
|
||||||
|
|
||||||
|
The `formatted_body` uses the template defined earlier in this section.
|
||||||
|
|
||||||
|
######## Fallback for `m.emote`
|
||||||
|
|
||||||
|
Similar to the fallback for `m.text`, each line gets prefixed with the
|
||||||
|
fallback prefix sequence. However an asterisk should be inserted before
|
||||||
|
the user's ID, like so:
|
||||||
|
|
||||||
|
> * <@alice:example.org> feels like today is going to be a great day
|
||||||
|
|
||||||
|
This is the reply
|
||||||
|
|
||||||
|
The `formatted_body` has a subtle difference for the template where the
|
||||||
|
asterisk is also inserted ahead of the user's ID:
|
||||||
|
|
||||||
|
<mx-reply>
|
||||||
|
<blockquote>
|
||||||
|
<a href="https://matrix.to/#/!somewhere:example.org/$event:example.org">In reply to</a>
|
||||||
|
* <a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
||||||
|
<br />
|
||||||
|
<!-- This is where the related event's HTML would be. -->
|
||||||
|
</blockquote>
|
||||||
|
</mx-reply>
|
||||||
|
This is where the reply goes.
|
||||||
|
|
||||||
|
######## Fallback for `m.image`, `m.video`, `m.audio`, and `m.file`
|
||||||
|
|
||||||
|
The related event's `body` would be a file name, which may not be very
|
||||||
|
descriptive. The related event should additionally not have a `format`
|
||||||
|
or `formatted_body` in the `content` - if the event does have a `format`
|
||||||
|
and/or `formatted_body`, those fields should be ignored. Because the
|
||||||
|
filename alone may not be descriptive, the related event's `body` should
|
||||||
|
be considered to be `"sent a file."` such that the output looks similar
|
||||||
|
to the following:
|
||||||
|
|
||||||
|
> <@alice:example.org> sent a file.
|
||||||
|
|
||||||
|
This is the reply
|
||||||
|
|
||||||
|
<mx-reply>
|
||||||
|
<blockquote>
|
||||||
|
<a href="https://matrix.to/#/!somewhere:example.org/$event:example.org">In reply to</a>
|
||||||
|
<a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
||||||
|
<br />
|
||||||
|
sent a file.
|
||||||
|
</blockquote>
|
||||||
|
</mx-reply>
|
||||||
|
This is where the reply goes.
|
||||||
|
|
||||||
|
For `m.image`, the text should be `"sent an image."`. For `m.video`, the
|
||||||
|
text should be `"sent a video."`. For `m.audio`, the text should be
|
||||||
|
`"sent an audio file"`.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Homeservers SHOULD reject `m.room.message` events which don't have a
|
||||||
|
`msgtype` key, or which don't have a textual `body` key, with an HTTP
|
||||||
|
status code of 400.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Messages sent using this module are not encrypted, although end to end
|
||||||
|
encryption is in development (see [E2E module]()).
|
||||||
|
|
||||||
|
Clients should sanitise **all displayed keys** for unsafe HTML to
|
||||||
|
prevent Cross-Site Scripting (XSS) attacks. This includes room names and
|
||||||
|
topics.
|
@ -0,0 +1,58 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 300
|
||||||
|
---
|
||||||
|
|
||||||
|
### User, room, and group mentions
|
||||||
|
|
||||||
|
This module allows users to mention other users, rooms, and groups
|
||||||
|
within a room message. This is achieved by including a [matrix.to
|
||||||
|
URI](../appendices.html#matrix-to-navigation) in the HTML body of an
|
||||||
|
[m.room.message]() event. This module does not have any server-specific
|
||||||
|
behaviour to it.
|
||||||
|
|
||||||
|
Mentions apply only to [m.room.message]() events where the `msgtype` is
|
||||||
|
`m.text`, `m.emote`, or `m.notice`. The `format` for the event must be
|
||||||
|
`org.matrix.custom.html` and therefore requires a `formatted_body`.
|
||||||
|
|
||||||
|
To make a mention, reference the entity being mentioned in the
|
||||||
|
`formatted_body` using an anchor, like so:
|
||||||
|
|
||||||
|
{
|
||||||
|
"body": "Hello Alice!",
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "Hello <a href='https://matrix.to/#/@alice:example.org'>Alice</a>!"
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
In addition to using the appropriate `matrix.to URI` for the mention,
|
||||||
|
clients should use the following guidelines when making mentions in
|
||||||
|
events to be sent:
|
||||||
|
|
||||||
|
- When mentioning users, use the user's potentially ambiguous display
|
||||||
|
name for the anchor's text. If the user does not have a display
|
||||||
|
name, use the user's ID.
|
||||||
|
- When mentioning rooms, use the canonical alias for the room. If the
|
||||||
|
room does not have a canonical alias, prefer one of the aliases
|
||||||
|
listed on the room. If no alias can be found, fall back to the room
|
||||||
|
ID. In all cases, use the alias/room ID being linked to as the
|
||||||
|
anchor's text.
|
||||||
|
- When referencing groups, use the group ID as the anchor's text.
|
||||||
|
|
||||||
|
The text component of the anchor should be used in the event's `body`
|
||||||
|
where the mention would normally be represented, as shown in the example
|
||||||
|
above.
|
||||||
|
|
||||||
|
Clients should display mentions differently from other elements. For
|
||||||
|
example, this may be done by changing the background color of the
|
||||||
|
mention to indicate that it is different from a normal link.
|
||||||
|
|
||||||
|
If the current user is mentioned in a message (either by a mention as
|
||||||
|
defined in this module or by a push rule), the client should show that
|
||||||
|
mention differently from other mentions, such as by using a red
|
||||||
|
background color to signify to the user that they were mentioned.
|
||||||
|
|
||||||
|
When clicked, the mention should navigate the user to the appropriate
|
||||||
|
room, group, or user information.
|
@ -0,0 +1,126 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 330
|
||||||
|
---
|
||||||
|
|
||||||
|
### Moderation policy lists
|
||||||
|
|
||||||
|
With Matrix being an open network where anyone can participate, a very
|
||||||
|
wide range of content exists and it is important that users are
|
||||||
|
empowered to select which content they wish to see, and which content
|
||||||
|
they wish to block. By extension, room moderators and server admins
|
||||||
|
should also be able to select which content they do not wish to host in
|
||||||
|
their rooms and servers.
|
||||||
|
|
||||||
|
The protocol's position on this is one of neutrality: it should not be
|
||||||
|
deciding what content is undesirable for any particular entity and
|
||||||
|
should instead be empowering those entities to make their own decisions.
|
||||||
|
As such, a generic framework for communicating "moderation policy lists"
|
||||||
|
or "moderation policy rooms" is described. Note that this module only
|
||||||
|
describes the data structures and not how they should be interpreting:
|
||||||
|
the entity making the decisions on filtering is best positioned to
|
||||||
|
interpret the rules how it sees fit.
|
||||||
|
|
||||||
|
Moderation policy lists are stored as room state events. There are no
|
||||||
|
restrictions on how the rooms can be configured (they could be public,
|
||||||
|
private, encrypted, etc).
|
||||||
|
|
||||||
|
There are currently 3 kinds of entities which can be affected by rules:
|
||||||
|
`user`, `server`, and `room`. All 3 are described with
|
||||||
|
`m.policy.rule.<kind>` state events. The `state_key` for a policy rule
|
||||||
|
is an arbitrary string decided by the sender of the rule.
|
||||||
|
|
||||||
|
Rules contain recommendations and reasons for the rule existing. The
|
||||||
|
`reason` is a human-readable string which describes the
|
||||||
|
`recommendation`. Currently only one recommendation, `m.ban`, is
|
||||||
|
specified.
|
||||||
|
|
||||||
|
#### `m.ban` recommendation
|
||||||
|
|
||||||
|
When this recommendation is used, the entities affected by the rule
|
||||||
|
should be banned from participation where possible. The enforcement of
|
||||||
|
this is deliberately left as an implementation detail to avoid the
|
||||||
|
protocol imposing its opinion on how the policy list is to be
|
||||||
|
interpreted. However, a suggestion for a simple implementation is as
|
||||||
|
follows:
|
||||||
|
|
||||||
|
- Is a `user` rule...
|
||||||
|
- Applied to a user: The user should be added to the subscriber's
|
||||||
|
ignore list.
|
||||||
|
- Applied to a room: The user should be banned from the room
|
||||||
|
(either on sight or immediately).
|
||||||
|
- Applied to a server: The user should not be allowed to send
|
||||||
|
invites to users on the server.
|
||||||
|
- Is a `room` rule...
|
||||||
|
- Applied to a user: The user should leave the room and not join
|
||||||
|
it
|
||||||
|
([MSC2270](https://github.com/matrix-org/matrix-doc/pull/2270)-style
|
||||||
|
ignore).
|
||||||
|
- Applied to a room: No-op because a room cannot ban itself.
|
||||||
|
- Applied to a server: The server should prevent users from
|
||||||
|
joining the room and from receiving invites to it.
|
||||||
|
- Is a `server` rule...
|
||||||
|
- Applied to a user: The user should not receive events or invites
|
||||||
|
from the server.
|
||||||
|
- Applied to a room: The server is added as a denied server in the
|
||||||
|
ACLs.
|
||||||
|
- Applied to a server: The subscriber should avoid federating with
|
||||||
|
the server as much as possible by blocking invites from the
|
||||||
|
server and not sending traffic unless strictly required (no
|
||||||
|
outbound invites).
|
||||||
|
|
||||||
|
#### Subscribing to policy lists
|
||||||
|
|
||||||
|
This is deliberately left as an implementation detail. For
|
||||||
|
implementations using the Client-Server API, this could be as easy as
|
||||||
|
joining or peeking the room. Joining or peeking is not required,
|
||||||
|
however: an implementation could poll for updates or use a different
|
||||||
|
technique for receiving updates to the policy's rules.
|
||||||
|
|
||||||
|
#### Sharing
|
||||||
|
|
||||||
|
In addition to sharing a direct reference to the room which contains the
|
||||||
|
policy's rules, plain http or https URLs can be used to share links to
|
||||||
|
the list. When the URL is approached with a `Accept: application/json`
|
||||||
|
header or has `.json` appended to the end of the URL, it should return a
|
||||||
|
JSON object containing a `room_uri` property which references the room.
|
||||||
|
Currently this would be a `matrix.to` URI, however in future it could be
|
||||||
|
a Matrix-schemed URI instead. When not approached with the intent of
|
||||||
|
JSON, the service could return a user-friendly page describing what is
|
||||||
|
included in the ban list.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
The `entity` described by the state events can contain `*` and `?` to
|
||||||
|
match zero or more and one or more characters respectively. Note that
|
||||||
|
rules against rooms can describe a room ID or room alias - the
|
||||||
|
subscriber is responsible for resolving the alias to a room ID if
|
||||||
|
desired.
|
||||||
|
|
||||||
|
{{m\_policy\_rule\_user\_event}}
|
||||||
|
|
||||||
|
{{m\_policy\_rule\_room\_event}}
|
||||||
|
|
||||||
|
{{m\_policy\_rule\_server\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
As described above, the client behaviour is deliberately left undefined.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Servers have no additional requirements placed on them by this module.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
This module could be used to build a system of shared blacklists, which
|
||||||
|
may create a divide within established communities if not carefully
|
||||||
|
deployed. This may well not be a suitable solution for all communities.
|
||||||
|
|
||||||
|
Depending on how implementations handle subscriptions, user IDs may be
|
||||||
|
linked to policy lists and therefore expose the views of that user. For
|
||||||
|
example, a client implementation which joins the user to the policy room
|
||||||
|
would expose the user's ID to observers of the policy room. In future,
|
||||||
|
[MSC1228](https://github.com/matrix-org/matrix-doc/pulls/1228) and
|
||||||
|
[MSC1777](https://github.com/matrix-org/matrix-doc/pulls/1777) (or
|
||||||
|
similar) could help solve this concern.
|
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 280
|
||||||
|
---
|
||||||
|
|
||||||
|
### OpenID
|
||||||
|
|
||||||
|
This module allows users to verify their identity with a third party
|
||||||
|
service. The third party service does need to be matrix-aware in that it
|
||||||
|
will need to know to resolve matrix homeservers to exchange the user's
|
||||||
|
token for identity information.
|
||||||
|
|
||||||
|
{{openid\_cs\_http\_api}}
|
@ -0,0 +1,76 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 60
|
||||||
|
---
|
||||||
|
|
||||||
|
### Presence
|
||||||
|
|
||||||
|
Each user has the concept of presence information. This encodes:
|
||||||
|
|
||||||
|
- Whether the user is currently online
|
||||||
|
- How recently the user was last active (as seen by the server)
|
||||||
|
- Whether a given client considers the user to be currently idle
|
||||||
|
- Arbitrary information about the user's current status (e.g. "in a
|
||||||
|
meeting").
|
||||||
|
|
||||||
|
This information is collated from both per-device (`online`, `idle`,
|
||||||
|
`last_active`) and per-user (status) data, aggregated by the user's
|
||||||
|
homeserver and transmitted as an `m.presence` event. Presence events are
|
||||||
|
sent to interested parties where users share a room membership.
|
||||||
|
|
||||||
|
User's presence state is represented by the `presence` key, which is an
|
||||||
|
enum of one of the following:
|
||||||
|
|
||||||
|
- `online` : The default state when the user is connected to an event
|
||||||
|
stream.
|
||||||
|
- `unavailable` : The user is not reachable at this time e.g. they are
|
||||||
|
idle.
|
||||||
|
- `offline` : The user is not connected to an event stream or is
|
||||||
|
explicitly suppressing their profile information from being sent.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{presence\_events}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients can manually set/get their presence using the HTTP APIs listed
|
||||||
|
below.
|
||||||
|
|
||||||
|
{{presence\_cs\_http\_api}}
|
||||||
|
|
||||||
|
##### Last active ago
|
||||||
|
|
||||||
|
The server maintains a timestamp of the last time it saw a pro-active
|
||||||
|
event from the user. A pro-active event may be sending a message to a
|
||||||
|
room or changing presence state to `online`. This timestamp is presented
|
||||||
|
via a key called `last_active_ago` which gives the relative number of
|
||||||
|
milliseconds since the pro-active event.
|
||||||
|
|
||||||
|
To reduce the number of presence updates sent to clients the server may
|
||||||
|
include a `currently_active` boolean field when the presence state is
|
||||||
|
`online`. When true, the server will not send further updates to the
|
||||||
|
last active time until an update is sent to the client with either a)
|
||||||
|
`currently_active` set to false or b) a presence state other than
|
||||||
|
`online`. During this period clients must consider the user to be
|
||||||
|
currently active, irrespective of the last active time.
|
||||||
|
|
||||||
|
The last active time must be up to date whenever the server gives a
|
||||||
|
presence event to the client. The `currently_active` mechanism should
|
||||||
|
purely be used by servers to stop sending continuous presence updates,
|
||||||
|
as opposed to disabling last active tracking entirely. Thus clients can
|
||||||
|
fetch up to date last active times by explicitly requesting the presence
|
||||||
|
for a given user.
|
||||||
|
|
||||||
|
##### Idle timeout
|
||||||
|
|
||||||
|
The server will automatically set a user's presence to `unavailable` if
|
||||||
|
their last active time was over a threshold value (e.g. 5 minutes).
|
||||||
|
Clients can manually set a user's presence to `unavailable`. Any
|
||||||
|
activity that bumps the last active time on any of the user's clients
|
||||||
|
will cause the server to automatically set their presence to `online`.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Presence information is shared with all users who share a room with the
|
||||||
|
target user. In large public rooms this could be undesirable.
|
@ -0,0 +1,721 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 130
|
||||||
|
---
|
||||||
|
|
||||||
|
### Push Notifications
|
||||||
|
|
||||||
|
+--------------------+ +-------------------+
|
||||||
|
Matrix HTTP | | | |
|
||||||
|
Notification Protocol | App Developer | | Device Vendor |
|
||||||
|
| | | |
|
||||||
|
+-------------------+ | +----------------+ | | +---------------+ |
|
||||||
|
| | | | | | | | | |
|
||||||
|
| Matrix homeserver +-----> Push Gateway +------> Push Provider | |
|
||||||
|
| | | | | | | | | |
|
||||||
|
+-^-----------------+ | +----------------+ | | +----+----------+ |
|
||||||
|
| | | | | |
|
||||||
|
Matrix | | | | | |
|
||||||
|
Client/Server API + | | | | |
|
||||||
|
| | +--------------------+ +-------------------+
|
||||||
|
| +--+-+ |
|
||||||
|
| | <-------------------------------------------+
|
||||||
|
+---+ |
|
||||||
|
| | Provider Push Protocol
|
||||||
|
+----+
|
||||||
|
|
||||||
|
Mobile Device or Client
|
||||||
|
|
||||||
|
This module adds support for push notifications. Homeservers send
|
||||||
|
notifications of events to user-configured HTTP endpoints. Users may
|
||||||
|
also configure a number of rules that determine which events generate
|
||||||
|
notifications. These are all stored and managed by the user's
|
||||||
|
homeserver. This allows user-specific push settings to be reused between
|
||||||
|
client applications.
|
||||||
|
|
||||||
|
The above diagram shows the flow of push notifications being sent to a
|
||||||
|
handset where push notifications are submitted via the handset vendor,
|
||||||
|
such as Apple's APNS or Google's GCM. This happens as follows:
|
||||||
|
|
||||||
|
1. The client app signs in to a homeserver.
|
||||||
|
2. The client app registers with its vendor's Push Provider and obtains
|
||||||
|
a routing token of some kind.
|
||||||
|
3. The mobile app uses the Client/Server API to add a 'pusher',
|
||||||
|
providing the URL of a specific Push Gateway which is configured for
|
||||||
|
that application. It also provides the routing token it has acquired
|
||||||
|
from the Push Provider.
|
||||||
|
4. The homeserver starts sending HTTP requests to the Push Gateway
|
||||||
|
using the supplied URL. The Push Gateway relays this notification to
|
||||||
|
the Push Provider, passing the routing token along with any
|
||||||
|
necessary private credentials the provider requires to send push
|
||||||
|
notifications.
|
||||||
|
5. The Push Provider sends the notification to the device.
|
||||||
|
|
||||||
|
Definitions for terms used in this section are below:
|
||||||
|
|
||||||
|
Push Provider
|
||||||
|
A push provider is a service managed by the device vendor which can send
|
||||||
|
notifications directly to the device. Google Cloud Messaging (GCM) and
|
||||||
|
Apple Push Notification Service (APNS) are two examples of push
|
||||||
|
providers.
|
||||||
|
|
||||||
|
Push Gateway
|
||||||
|
A push gateway is a server that receives HTTP event notifications from
|
||||||
|
homeservers and passes them on to a different protocol such as APNS for
|
||||||
|
iOS devices or GCM for Android devices. Clients inform the homeserver
|
||||||
|
which Push Gateway to send notifications to when it sets up a Pusher.
|
||||||
|
|
||||||
|
Pusher
|
||||||
|
A pusher is a worker on the homeserver that manages the sending of HTTP
|
||||||
|
notifications for a user. A user can have multiple pushers: one per
|
||||||
|
device.
|
||||||
|
|
||||||
|
Push Rule
|
||||||
|
A push rule is a single rule that states under what *conditions* an
|
||||||
|
event should be passed onto a push gateway and *how* the notification
|
||||||
|
should be presented. These rules are stored on the user's homeserver.
|
||||||
|
They are manually configured by the user, who can create and view them
|
||||||
|
via the Client/Server API.
|
||||||
|
|
||||||
|
Push Ruleset
|
||||||
|
A push ruleset *scopes a set of rules according to some criteria*. For
|
||||||
|
example, some rules may only be applied for messages from a particular
|
||||||
|
sender, a particular room, or by default. The push ruleset contains the
|
||||||
|
entire set of scopes and rules.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients MUST configure a Pusher before they will receive push
|
||||||
|
notifications. There is a single API endpoint for this, as described
|
||||||
|
below.
|
||||||
|
|
||||||
|
{{pusher\_cs\_http\_api}}
|
||||||
|
|
||||||
|
##### Listing Notifications
|
||||||
|
|
||||||
|
A client can retrieve a list of events that it has been notified about.
|
||||||
|
This may be useful so that users can see a summary of what important
|
||||||
|
messages they have received.
|
||||||
|
|
||||||
|
{{notifications\_cs\_http\_api}}
|
||||||
|
|
||||||
|
##### Receiving notifications
|
||||||
|
|
||||||
|
Servers MUST include the number of unread notifications in a client's
|
||||||
|
`/sync` stream, and MUST update it as it changes. Notifications are
|
||||||
|
determined by the push rules which apply to an event.
|
||||||
|
|
||||||
|
When the user updates their read receipt (either by using the API or by
|
||||||
|
sending an event), notifications prior to and including that event MUST
|
||||||
|
be marked as read.
|
||||||
|
|
||||||
|
##### Push Rules
|
||||||
|
|
||||||
|
A push rule is a single rule that states under what *conditions* an
|
||||||
|
event should be passed onto a push gateway and *how* the notification
|
||||||
|
should be presented. There are different "kinds" of push rules and each
|
||||||
|
rule has an associated priority. Every push rule MUST have a `kind` and
|
||||||
|
`rule_id`. The `rule_id` is a unique string within the kind of rule and
|
||||||
|
its' scope: `rule_ids` do not need to be unique between rules of the
|
||||||
|
same kind on different devices. Rules may have extra keys depending on
|
||||||
|
the value of `kind`.
|
||||||
|
|
||||||
|
The different `kind`s of rule, in the order that they are checked, are:
|
||||||
|
|
||||||
|
Override Rules `override`
|
||||||
|
The highest priority rules are user-configured overrides.
|
||||||
|
|
||||||
|
Content-specific Rules `content`
|
||||||
|
These configure behaviour for (unencrypted) messages that match certain
|
||||||
|
patterns. Content rules take one parameter: `pattern`, that gives the
|
||||||
|
glob pattern to match against. This is treated in the same way as
|
||||||
|
`pattern` for `event_match`.
|
||||||
|
|
||||||
|
Room-specific Rules `room`
|
||||||
|
These rules change the behaviour of all messages for a given room. The
|
||||||
|
`rule_id` of a room rule is always the ID of the room that it affects.
|
||||||
|
|
||||||
|
Sender-specific rules `sender`
|
||||||
|
These rules configure notification behaviour for messages from a
|
||||||
|
specific Matrix user ID. The `rule_id` of Sender rules is always the
|
||||||
|
Matrix user ID of the user whose messages they'd apply to.
|
||||||
|
|
||||||
|
Underride rules `underride`
|
||||||
|
These are identical to `override` rules, but have a lower priority than
|
||||||
|
`content`, `room` and `sender` rules.
|
||||||
|
|
||||||
|
Rules with the same `kind` can specify an ordering priority. This
|
||||||
|
determines which rule is selected in the event of multiple matches. For
|
||||||
|
example, a rule matching "tea" and a separate rule matching "time" would
|
||||||
|
both match the sentence "It's time for tea". The ordering of the rules
|
||||||
|
would then resolve the tiebreak to determine which rule is executed.
|
||||||
|
Only `actions` for highest priority rule will be sent to the Push
|
||||||
|
Gateway.
|
||||||
|
|
||||||
|
Each rule can be enabled or disabled. Disabled rules never match. If no
|
||||||
|
rules match an event, the homeserver MUST NOT notify the Push Gateway
|
||||||
|
for that event. Homeservers MUST NOT notify the Push Gateway for events
|
||||||
|
that the user has sent themselves.
|
||||||
|
|
||||||
|
###### Actions
|
||||||
|
|
||||||
|
All rules have an associated list of `actions`. An action affects if and
|
||||||
|
how a notification is delivered for a matching event. The following
|
||||||
|
actions are defined:
|
||||||
|
|
||||||
|
`notify`
|
||||||
|
This causes each matching event to generate a notification.
|
||||||
|
|
||||||
|
`dont_notify`
|
||||||
|
This prevents each matching event from generating a notification
|
||||||
|
|
||||||
|
`coalesce`
|
||||||
|
This enables notifications for matching events but activates homeserver
|
||||||
|
specific behaviour to intelligently coalesce multiple events into a
|
||||||
|
single notification. Not all homeservers may support this. Those that do
|
||||||
|
not support it should treat it as the `notify` action.
|
||||||
|
|
||||||
|
`set_tweak`
|
||||||
|
Sets an entry in the `tweaks` dictionary key that is sent in the
|
||||||
|
notification request to the Push Gateway. This takes the form of a
|
||||||
|
dictionary with a `set_tweak` key whose value is the name of the tweak
|
||||||
|
to set. It may also have a `value` key which is the value to which it
|
||||||
|
should be set.
|
||||||
|
|
||||||
|
Actions that have no parameters are represented as a string. Otherwise,
|
||||||
|
they are represented as a dictionary with a key equal to their name and
|
||||||
|
other keys as their parameters, e.g.
|
||||||
|
`{ "set_tweak": "sound", "value": "default" }`
|
||||||
|
|
||||||
|
####### Tweaks
|
||||||
|
|
||||||
|
The `set_tweak` action is used to add an entry to the 'tweaks'
|
||||||
|
dictionary that is sent in the notification request to the Push Gateway.
|
||||||
|
The following tweaks are defined:
|
||||||
|
|
||||||
|
`sound`
|
||||||
|
A string representing the sound to be played when this notification
|
||||||
|
arrives. A value of `default` means to play a default sound. A device
|
||||||
|
may choose to alert the user by some other means if appropriate, eg.
|
||||||
|
vibration.
|
||||||
|
|
||||||
|
`highlight`
|
||||||
|
A boolean representing whether or not this message should be highlighted
|
||||||
|
in the UI. This will normally take the form of presenting the message in
|
||||||
|
a different colour and/or style. The UI might also be adjusted to draw
|
||||||
|
particular attention to the room in which the event occurred. If a
|
||||||
|
`highlight` tweak is given with no value, its value is defined to be
|
||||||
|
`true`. If no highlight tweak is given at all then the value of
|
||||||
|
`highlight` is defined to be false.
|
||||||
|
|
||||||
|
Tweaks are passed transparently through the homeserver so client
|
||||||
|
applications and Push Gateways may agree on additional tweaks. For
|
||||||
|
example, a tweak may be added to specify how to flash the notification
|
||||||
|
light on a mobile device.
|
||||||
|
|
||||||
|
###### Conditions
|
||||||
|
|
||||||
|
`override` and `underride` rules MAY have a list of 'conditions'. All
|
||||||
|
conditions must hold true for an event in order for the rule to match. A
|
||||||
|
rule with no conditions always matches. The following conditions are
|
||||||
|
defined:
|
||||||
|
|
||||||
|
`event_match`
|
||||||
|
This is a glob pattern match on a field of the event. Parameters:
|
||||||
|
|
||||||
|
- `key`: The dot-separated field of the event to match, e.g.
|
||||||
|
`content.body`
|
||||||
|
- `pattern`: The glob-style pattern to match against. Patterns with no
|
||||||
|
special glob characters should be treated as having asterisks
|
||||||
|
prepended and appended when testing the condition.
|
||||||
|
|
||||||
|
`contains_display_name`
|
||||||
|
This matches unencrypted messages where `content.body` contains the
|
||||||
|
owner's display name in that room. This is a separate rule because
|
||||||
|
display names may change and as such it would be hard to maintain a rule
|
||||||
|
that matched the user's display name. This condition has no parameters.
|
||||||
|
|
||||||
|
`room_member_count`
|
||||||
|
This matches the current number of members in the room. Parameters:
|
||||||
|
|
||||||
|
- `is`: A decimal integer optionally prefixed by one of, `==`, `<`,
|
||||||
|
`>`, `>=` or `<=`. A prefix of `<` matches rooms where the member
|
||||||
|
count is strictly less than the given number and so forth. If no
|
||||||
|
prefix is present, this parameter defaults to `==`.
|
||||||
|
|
||||||
|
`sender_notification_permission`
|
||||||
|
This takes into account the current power levels in the room, ensuring
|
||||||
|
the sender of the event has high enough power to trigger the
|
||||||
|
notification.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- `key`: A string that determines the power level the sender must have
|
||||||
|
to trigger notifications of a given type, such as `room`. Refer to
|
||||||
|
the [m.room.power\_levels]() event schema for information about what
|
||||||
|
the defaults are and how to interpret the event. The `key` is used
|
||||||
|
to look up the power level required to send a notification type from
|
||||||
|
the `notifications` object in the power level event content.
|
||||||
|
|
||||||
|
Unrecognised conditions MUST NOT match any events, effectively making
|
||||||
|
the push rule disabled.
|
||||||
|
|
||||||
|
`room`, `sender` and `content` rules do not have conditions in the same
|
||||||
|
way, but instead have predefined conditions. In the cases of `room` and
|
||||||
|
`sender` rules, the `rule_id` of the rule determines its behaviour.
|
||||||
|
|
||||||
|
###### Predefined Rules
|
||||||
|
|
||||||
|
Homeservers can specify "server-default rules" which operate at a lower
|
||||||
|
priority than "user-defined rules". The `rule_id` for all server-default
|
||||||
|
rules MUST start with a dot (".") to identify them as "server-default".
|
||||||
|
The following server-default rules are specified:
|
||||||
|
|
||||||
|
####### Default Override Rules
|
||||||
|
|
||||||
|
######## `.m.rule.master`
|
||||||
|
|
||||||
|
Matches all events. This can be enabled to turn off all push
|
||||||
|
notifications other than those generated by override rules set by the
|
||||||
|
user. By default this rule is disabled.
|
||||||
|
|
||||||
|
Definition
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.master",
|
||||||
|
"default": true,
|
||||||
|
"enabled": false,
|
||||||
|
"conditions": [],
|
||||||
|
"actions": [
|
||||||
|
"dont_notify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.suppress_notices`
|
||||||
|
|
||||||
|
Matches messages with a `msgtype` of `notice`.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.suppress_notices",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "content.msgtype",
|
||||||
|
"pattern": "m.notice",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"dont_notify",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.invite_for_me`
|
||||||
|
|
||||||
|
Matches any invites to a new room for this user.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.invite_for_me",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "type",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.room.member"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "content.membership",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "invite"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "state_key",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "[the user's Matrix ID]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.member_event`
|
||||||
|
|
||||||
|
Matches any `m.room.member_event`.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.member_event",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "type",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.room.member"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"dont_notify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.contains_display_name`
|
||||||
|
|
||||||
|
Matches any message whose content is unencrypted and contains the user's
|
||||||
|
current display name in the room in which it was sent.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.contains_display_name",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "contains_display_name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.tombstone`
|
||||||
|
|
||||||
|
Matches any state event whose type is `m.room.tombstone`. This is
|
||||||
|
intended to notify users of a room when it is upgraded, similar to what
|
||||||
|
an `@room` notification would accomplish.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.tombstone",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "type",
|
||||||
|
"pattern": "m.room.tombstone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "state_key",
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.roomnotif`
|
||||||
|
|
||||||
|
Matches any message whose content is unencrypted and contains the text
|
||||||
|
`@room`, signifying the whole room should be notified of the event.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.roomnotif",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "content.body",
|
||||||
|
"pattern": "@room"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "sender_notification_permission",
|
||||||
|
"key": "room"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
####### Default Content Rules
|
||||||
|
|
||||||
|
######## `.m.rule.contains_user_name`
|
||||||
|
|
||||||
|
Matches any message whose content is unencrypted and contains the local
|
||||||
|
part of the user's Matrix ID, separated by word boundaries.
|
||||||
|
|
||||||
|
Definition (as a `content` rule):
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.contains_user_name",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"pattern": "[the local part of the user's Matrix ID]",
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
####### Default Underride Rules
|
||||||
|
|
||||||
|
######## `.m.rule.call`
|
||||||
|
|
||||||
|
Matches any incoming VOIP call.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.call",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "type",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.call.invite"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "ring"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.encrypted_room_one_to_one`
|
||||||
|
|
||||||
|
Matches any encrypted event sent in a room with exactly two members.
|
||||||
|
Unlike other push rules, this rule cannot be matched against the content
|
||||||
|
of the event by nature of it being encrypted. This causes the rule to be
|
||||||
|
an "all or nothing" match where it either matches *all* events that are
|
||||||
|
encrypted (in 1:1 rooms) or none.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.encrypted_room_one_to_one",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "room_member_count",
|
||||||
|
"is": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "type",
|
||||||
|
"pattern": "m.room.encrypted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.room_one_to_one`
|
||||||
|
|
||||||
|
Matches any message sent in a room with exactly two members.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.room_one_to_one",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "room_member_count",
|
||||||
|
"is": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "type",
|
||||||
|
"pattern": "m.room.message"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.message`
|
||||||
|
|
||||||
|
Matches all chat messages.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.message",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "type",
|
||||||
|
"pattern": "m.room.message"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
######## `.m.rule.encrypted`
|
||||||
|
|
||||||
|
Matches all encrypted events. Unlike other push rules, this rule cannot
|
||||||
|
be matched against the content of the event by nature of it being
|
||||||
|
encrypted. This causes the rule to be an "all or nothing" match where it
|
||||||
|
either matches *all* events that are encrypted (in group rooms) or none.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.encrypted",
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "event_match",
|
||||||
|
"key": "type",
|
||||||
|
"pattern": "m.room.encrypted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
##### Push Rules: API
|
||||||
|
|
||||||
|
Clients can retrieve, add, modify and remove push rules globally or
|
||||||
|
per-device using the APIs below.
|
||||||
|
|
||||||
|
{{pushrules\_cs\_http\_api}}
|
||||||
|
|
||||||
|
##### Push Rules: Events
|
||||||
|
|
||||||
|
When a user changes their push rules a `m.push_rules` event is sent to
|
||||||
|
all clients in the `account_data` section of their next `/sync` request.
|
||||||
|
The content of the event is the current push rules for the user.
|
||||||
|
|
||||||
|
{{m\_push\_rules\_event}}
|
||||||
|
|
||||||
|
###### Examples
|
||||||
|
|
||||||
|
To create a rule that suppresses notifications for the room with ID
|
||||||
|
`!dj234r78wl45Gh4D:matrix.org`:
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"actions" : ["dont_notify"]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To suppress notifications for the user `@spambot:matrix.org`:
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"actions" : ["dont_notify"]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To always notify for messages that contain the work 'cake' and set a
|
||||||
|
specific sound (with a rule\_id of `SSByZWFsbHkgbGlrZSBjYWtl`):
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"pattern": "cake",
|
||||||
|
"actions" : ["notify", {"set_sound":"cakealarm.wav"}]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To add a rule suppressing notifications for messages starting with
|
||||||
|
'cake' but ending with 'lie', superseding the previous rule:
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" -d \
|
||||||
|
'{
|
||||||
|
"pattern": "cake*lie",
|
||||||
|
"actions" : ["notify"]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To add a custom sound for notifications messages containing the word
|
||||||
|
'beer' in any rooms with 10 members or fewer (with greater importance
|
||||||
|
than the room, sender and content rules):
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "https://example.com/_matrix/client/%CLIENT_MAJOR_VERSION%/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"conditions": [
|
||||||
|
{"kind": "event_match", "key": "content.body", "pattern": "beer" },
|
||||||
|
{"kind": "room_member_count", "is": "<=10"}
|
||||||
|
],
|
||||||
|
"actions" : [
|
||||||
|
"notify",
|
||||||
|
{"set_sound":"beeroclock.wav"}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
#### Push Gateway behaviour
|
||||||
|
|
||||||
|
##### Recommendations for APNS
|
||||||
|
|
||||||
|
The exact format for sending APNS notifications is flexible and up to
|
||||||
|
the client app and its' push gateway to agree on. As APNS requires that
|
||||||
|
the sender has a private key owned by the app developer, each app must
|
||||||
|
have its own push gateway. It is recommended that:
|
||||||
|
|
||||||
|
- The APNS token be base64 encoded and used as the pushkey.
|
||||||
|
- A different app\_id be used for apps on the production and sandbox
|
||||||
|
APS environments.
|
||||||
|
- APNS push gateways do not attempt to wait for errors from the APNS
|
||||||
|
gateway before returning and instead to store failures and return
|
||||||
|
'rejected' responses next time that pushkey is used.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Clients specify the Push Gateway URL to use to send event notifications
|
||||||
|
to. This URL should be over HTTPS and *never* over HTTP.
|
||||||
|
|
||||||
|
As push notifications will pass through a Push Provider, message content
|
||||||
|
shouldn't be sent in the push itself where possible. Instead, Push
|
||||||
|
Gateways should send a "sync" command to instruct the client to get new
|
||||||
|
events from the homeserver directly.
|
@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 50
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fully read markers
|
||||||
|
|
||||||
|
The history for a given room may be split into three sections: messages
|
||||||
|
the user has read (or indicated they aren't interested in them),
|
||||||
|
messages the user might have read some but not others, and messages the
|
||||||
|
user hasn't seen yet. The "fully read marker" (also known as a "read
|
||||||
|
marker") marks the last event of the first section, whereas the user's
|
||||||
|
read receipt marks the last event of the second section.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
The user's fully read marker is kept as an event in the room's [account
|
||||||
|
data](#client-config). The event may be read to determine the user's
|
||||||
|
current fully read marker location in the room, and just like other
|
||||||
|
account data events the event will be pushed down the event stream when
|
||||||
|
updated.
|
||||||
|
|
||||||
|
The fully read marker is kept under an `m.fully_read` event. If the
|
||||||
|
event does not exist on the user's account data, the fully read marker
|
||||||
|
should be considered to be the user's read receipt location.
|
||||||
|
|
||||||
|
{{m\_fully\_read\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
The client cannot update fully read markers by directly modifying the
|
||||||
|
`m.fully_read` account data event. Instead, the client must make use of
|
||||||
|
the read markers API to change the values.
|
||||||
|
|
||||||
|
The read markers API can additionally update the user's read receipt
|
||||||
|
(`m.read`) location in the same operation as setting the fully read
|
||||||
|
marker location. This is because read receipts and read markers are
|
||||||
|
commonly updated at the same time, and therefore the client might wish
|
||||||
|
to save an extra HTTP call. Providing an `m.read` location performs the
|
||||||
|
same task as a request to `/receipt/m.read/$event:example.org`.
|
||||||
|
|
||||||
|
{{read\_markers\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
The server MUST prevent clients from setting `m.fully_read` directly in
|
||||||
|
room account data. The server must additionally ensure that it treats
|
||||||
|
the presence of `m.read` in the `/read_markers` request the same as how
|
||||||
|
it would for a request to `/receipt/m.read/$event:example.org`.
|
||||||
|
|
||||||
|
Upon updating the `m.fully_read` event due to a request to
|
||||||
|
`/read_markers`, the server MUST send the updated account data event
|
||||||
|
through to the client via the event stream (eg: `/sync`), provided any
|
||||||
|
applicable filters are also satisfied.
|
@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 40
|
||||||
|
---
|
||||||
|
|
||||||
|
### Receipts
|
||||||
|
|
||||||
|
This module adds in support for receipts. These receipts are a form of
|
||||||
|
acknowledgement of an event. This module defines a single
|
||||||
|
acknowledgement: `m.read` which indicates that the user has read up to a
|
||||||
|
given event.
|
||||||
|
|
||||||
|
Sending a receipt for each event can result in sending large amounts of
|
||||||
|
traffic to a homeserver. To prevent this from becoming a problem,
|
||||||
|
receipts are implemented using "up to" markers. This marker indicates
|
||||||
|
that the acknowledgement applies to all events "up to and including" the
|
||||||
|
event specified. For example, marking an event as "read" would indicate
|
||||||
|
that the user had read all events *up to* the referenced event. See the
|
||||||
|
[Receiving notifications](#receiving-notifications) section for more
|
||||||
|
information on how read receipts affect notification counts.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
Each `user_id`, `receipt_type` pair must be associated with only a
|
||||||
|
single `event_id`.
|
||||||
|
|
||||||
|
{{m\_receipt\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
In `/sync`, receipts are listed under the `ephemeral` array of events
|
||||||
|
for a given room. New receipts that come down the event streams are
|
||||||
|
deltas which update existing mappings. Clients should replace older
|
||||||
|
receipt acknowledgements based on `user_id` and `receipt_type` pairs.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
Client receives m.receipt:
|
||||||
|
user = @alice:example.com
|
||||||
|
receipt_type = m.read
|
||||||
|
event_id = $aaa:example.com
|
||||||
|
|
||||||
|
Client receives another m.receipt:
|
||||||
|
user = @alice:example.com
|
||||||
|
receipt_type = m.read
|
||||||
|
event_id = $bbb:example.com
|
||||||
|
|
||||||
|
The client should replace the older acknowledgement for $aaa:example.com with
|
||||||
|
this one for $bbb:example.com
|
||||||
|
|
||||||
|
Clients should send read receipts when there is some certainty that the
|
||||||
|
event in question has been **displayed** to the user. Simply receiving
|
||||||
|
an event does not provide enough certainty that the user has seen the
|
||||||
|
event. The user SHOULD need to *take some action* such as viewing the
|
||||||
|
room that the event was sent to or dismissing a notification in order
|
||||||
|
for the event to count as "read". Clients SHOULD NOT send read receipts
|
||||||
|
for events sent by their own user.
|
||||||
|
|
||||||
|
A client can update the markers for its user by interacting with the
|
||||||
|
following HTTP APIs.
|
||||||
|
|
||||||
|
{{receipts\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
For efficiency, receipts SHOULD be batched into one event per room
|
||||||
|
before delivering them to clients.
|
||||||
|
|
||||||
|
Receipts are sent across federation as EDUs with type `m.receipt`. The
|
||||||
|
format of the EDUs are:
|
||||||
|
|
||||||
|
{
|
||||||
|
<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.
|
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 260
|
||||||
|
---
|
||||||
|
|
||||||
|
### Reporting Content
|
||||||
|
|
||||||
|
Users may encounter content which they find inappropriate and should be
|
||||||
|
able to report it to the server administrators or room moderators for
|
||||||
|
review. This module defines a way for users to report content.
|
||||||
|
|
||||||
|
Content is reported based upon a negative score, where -100 is "most
|
||||||
|
offensive" and 0 is "inoffensive".
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
{{report\_content\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Servers are free to handle the reported content however they desire.
|
||||||
|
This may be a dedicated room to alert server administrators to the
|
||||||
|
reported content or some other mechanism for notifying the appropriate
|
||||||
|
people.
|
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 170
|
||||||
|
---
|
||||||
|
|
||||||
|
### Room Previews
|
||||||
|
|
||||||
|
It is sometimes desirable to offer a preview of a room, where a user can
|
||||||
|
"lurk" and read messages posted to the room, without joining the room.
|
||||||
|
This can be particularly effective when combined with [Guest Access]().
|
||||||
|
|
||||||
|
Previews are implemented via the `world_readable` [Room History
|
||||||
|
Visibility](). setting, along with a special version of the [GET
|
||||||
|
/events](#get-matrix-client-%CLIENT_MAJOR_VERSION%-events) endpoint.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
A client wishing to view a room without joining it should call [GET
|
||||||
|
/rooms/:room\_id/initialSync](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-initialsync),
|
||||||
|
followed by [GET /events](#peeking_events_api). Clients will need to do
|
||||||
|
this in parallel for each room they wish to view.
|
||||||
|
|
||||||
|
Clients can of course also call other endpoints such as [GET
|
||||||
|
/rooms/:room\_id/messages](#get-matrix-client-%CLIENT_MAJOR_VERSION%-rooms-roomid-messages)
|
||||||
|
and [GET /search](#get-matrix-client-%CLIENT_MAJOR_VERSION%-search) to
|
||||||
|
access events outside the `/events` stream.
|
||||||
|
|
||||||
|
{{peeking\_events\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
For clients which have not joined a room, servers are required to only
|
||||||
|
return events where the room state at the event had the
|
||||||
|
`m.room.history_visibility` state event present with
|
||||||
|
`history_visibility` value `world_readable`.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Clients may wish to display to their users that rooms which are
|
||||||
|
`world_readable` *may* be showing messages to non-joined users. There is
|
||||||
|
no way using this module to find out whether any non-joined guest users
|
||||||
|
*do* see events in the room, or to list or count any lurking users.
|
@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 310
|
||||||
|
---
|
||||||
|
|
||||||
|
### Room Upgrades
|
||||||
|
|
||||||
|
From time to time, a room may need to be upgraded to a different room
|
||||||
|
version for a variety for reasons. This module defines a way for rooms
|
||||||
|
to upgrade to a different room version when needed.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{m\_room\_tombstone\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients which understand `m.room.tombstone` events and the `predecessor`
|
||||||
|
field on `m.room.create` events should communicate to the user that the
|
||||||
|
room was upgraded. One way of accomplishing this would be hiding the old
|
||||||
|
room from the user's room list and showing banners linking between the
|
||||||
|
old and new room - ensuring that permalinks work when referencing the
|
||||||
|
old room. Another approach may be to virtually merge the rooms such that
|
||||||
|
the old room's timeline seamlessly continues into the new timeline
|
||||||
|
without the user having to jump between the rooms.
|
||||||
|
|
||||||
|
{{room\_upgrades\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
When the client requests to upgrade a known room to a known version, the
|
||||||
|
server:
|
||||||
|
|
||||||
|
1. Checks that the user has permission to send `m.room.tombstone`
|
||||||
|
events in the room.
|
||||||
|
|
||||||
|
2. Creates a replacement room with a `m.room.create` event containing a
|
||||||
|
`predecessor` field and the applicable `room_version`.
|
||||||
|
|
||||||
|
3. Replicates transferable state events to the new room. The exact
|
||||||
|
details for what is transferred is left as an implementation detail,
|
||||||
|
however the recommended state events to transfer are:
|
||||||
|
|
||||||
|
- `m.room.server_acl`
|
||||||
|
- `m.room.encryption`
|
||||||
|
- `m.room.name`
|
||||||
|
- `m.room.avatar`
|
||||||
|
- `m.room.topic`
|
||||||
|
- `m.room.guest_access`
|
||||||
|
- `m.room.history_visibility`
|
||||||
|
- `m.room.join_rules`
|
||||||
|
- `m.room.power_levels`
|
||||||
|
|
||||||
|
Membership events should not be transferred to the new room due to
|
||||||
|
technical limitations of servers not being able to impersonate
|
||||||
|
people from other homeservers. Additionally, servers should not
|
||||||
|
transfer state events which are sensitive to who sent them, such as
|
||||||
|
events outside of the Matrix namespace where clients may rely on the
|
||||||
|
sender to match certain criteria.
|
||||||
|
|
||||||
|
4. Moves any local aliases to the new room.
|
||||||
|
|
||||||
|
5. Sends a `m.room.tombstone` event to the old room to indicate that it
|
||||||
|
is not intended to be used any further.
|
||||||
|
|
||||||
|
6. If possible, the power levels in the old room should also be
|
||||||
|
modified to prevent sending of events and inviting new users. For
|
||||||
|
example, setting `events_default` and `invite` to the greater of
|
||||||
|
`50` and `users_default + 1`.
|
||||||
|
|
||||||
|
When a user joins the new room, the server should automatically
|
||||||
|
transfer/replicate some of the user's personalized settings such as
|
||||||
|
notifications, tags, etc.
|
@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 150
|
||||||
|
---
|
||||||
|
|
||||||
|
### Server Side Search
|
||||||
|
|
||||||
|
The search API allows clients to perform full text search across events
|
||||||
|
in all rooms that the user has been in, including those that they have
|
||||||
|
left. Only events that the user is allowed to see will be searched, e.g.
|
||||||
|
it won't include events in rooms that happened after you left.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
There is a single HTTP API for performing server-side search, documented
|
||||||
|
below.
|
||||||
|
|
||||||
|
{{search\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Search Categories
|
||||||
|
|
||||||
|
The search API allows clients to search in different categories of
|
||||||
|
items. Currently the only specified category is `room_events`.
|
||||||
|
|
||||||
|
##### `room_events`
|
||||||
|
|
||||||
|
This category covers all events that the user is allowed to see,
|
||||||
|
including events in rooms that they have left. The search is performed
|
||||||
|
on certain keys of certain event types.
|
||||||
|
|
||||||
|
The supported keys to search over are:
|
||||||
|
|
||||||
|
- `content.body` in `m.room.message`
|
||||||
|
- `content.name` in `m.room.name`
|
||||||
|
- `content.topic` in `m.room.topic`
|
||||||
|
|
||||||
|
The search will *not* include rooms that are end to end encrypted.
|
||||||
|
|
||||||
|
The results include a `rank` key that can be used to sort the results by
|
||||||
|
relevancy. The higher the `rank` the more relevant the result is.
|
||||||
|
|
||||||
|
The value of `count` gives an approximation of the total number of
|
||||||
|
results. Homeservers may give an estimate rather than an exact value for
|
||||||
|
this field.
|
||||||
|
|
||||||
|
#### Ordering
|
||||||
|
|
||||||
|
The client can specify the ordering that the server returns results in.
|
||||||
|
The two allowed orderings are:
|
||||||
|
|
||||||
|
- `rank`, which returns the most relevant results first.
|
||||||
|
- `recent`, which returns the most recent results first.
|
||||||
|
|
||||||
|
The default ordering is `rank`.
|
||||||
|
|
||||||
|
#### Groups
|
||||||
|
|
||||||
|
The client can request that the results are returned along with grouping
|
||||||
|
information, e.g. grouped by `room_id`. In this case the response will
|
||||||
|
contain a group entry for each distinct value of `room_id`. Each group
|
||||||
|
entry contains at least a list of the `event_ids` that are in that
|
||||||
|
group, as well as potentially other metadata about the group.
|
||||||
|
|
||||||
|
The current required supported groupings are:
|
||||||
|
|
||||||
|
- `room_id`
|
||||||
|
- `sender`
|
||||||
|
|
||||||
|
#### Pagination
|
||||||
|
|
||||||
|
The server may return a `next_batch` key at various places in the
|
||||||
|
response. These are used to paginate the results. To fetch more results,
|
||||||
|
the client should send the *same* request to the server with a
|
||||||
|
`next_batch` query parameter set to that of the token.
|
||||||
|
|
||||||
|
The scope of the pagination is defined depending on where the
|
||||||
|
`next_batch` token was returned. For example, using a token inside a
|
||||||
|
group will return more results from within that group.
|
||||||
|
|
||||||
|
The currently supported locations for the `next_batch` token are:
|
||||||
|
|
||||||
|
- `search_categories.<category>.next_batch`
|
||||||
|
- `search_categories.<category>.groups.<group_key>.<group_id>.next_batch`
|
||||||
|
|
||||||
|
A server need not support pagination, even if there are more matching
|
||||||
|
results. In that case, they must not return a `next_batch` token in the
|
||||||
|
response.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
The server must only return results that the user has permission to see.
|
@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 80
|
||||||
|
---
|
||||||
|
|
||||||
|
### Send-to-Device messaging<span id="module:to_device"></span>
|
||||||
|
|
||||||
|
This module provides a means by which clients can exchange signalling
|
||||||
|
messages without them being stored permanently as part of a shared
|
||||||
|
communication history. A message is delivered exactly once to each
|
||||||
|
client device.
|
||||||
|
|
||||||
|
The primary motivation for this API is exchanging data that is
|
||||||
|
meaningless or undesirable to persist in the room DAG - for example,
|
||||||
|
one-time authentication tokens or key data. It is not intended for
|
||||||
|
conversational data, which should be sent using the normal \_ API for
|
||||||
|
consistency throughout Matrix.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
To send a message to other devices, a client should call
|
||||||
|
`/sendToDevice`\_. Only one message can be sent to each device per
|
||||||
|
transaction, and they must all have the same event type. The device ID
|
||||||
|
in the request body can be set to `*` to request that the message be
|
||||||
|
sent to all known devices.
|
||||||
|
|
||||||
|
If there are send-to-device messages waiting for a client, they will be
|
||||||
|
returned by \_, as detailed in Extensions to /sync\_. Clients should
|
||||||
|
inspect the `type` of each returned event, and ignore any they do not
|
||||||
|
understand.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Servers should store pending messages for local users until they are
|
||||||
|
successfully delivered to the destination device. When a client calls \_
|
||||||
|
with an access token which corresponds to a device with pending
|
||||||
|
messages, the server should list the pending messages, in order of
|
||||||
|
arrival, in the response body.
|
||||||
|
|
||||||
|
When the client calls `/sync` again with the `next_batch` token from the
|
||||||
|
first response, the server should infer that any send-to-device messages
|
||||||
|
in that response have been delivered successfully, and delete them from
|
||||||
|
the store.
|
||||||
|
|
||||||
|
If there is a large queue of send-to-device messages, the server should
|
||||||
|
limit the number sent in each `/sync` response. 100 messages is
|
||||||
|
recommended as a reasonable limit.
|
||||||
|
|
||||||
|
If the client sends messages to users on remote domains, those messages
|
||||||
|
should be sent on to the remote servers via
|
||||||
|
[federation](../server_server/%SERVER_RELEASE_LABEL%.html#send-to-device-messaging).
|
||||||
|
|
||||||
|
#### Protocol definitions
|
||||||
|
|
||||||
|
{{to\_device\_cs\_http\_api}}
|
||||||
|
|
||||||
|
##### Extensions to /sync
|
||||||
|
|
||||||
|
This module adds the following properties to the \_ response:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr class="header">
|
||||||
|
<th>Parameter</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><p>to_device</p></td>
|
||||||
|
<td><p>ToDevice</p></td>
|
||||||
|
<td><p>Optional. Information on the send-to-device messages for the client device.</p></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
`ToDevice`
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr class="header">
|
||||||
|
<th>Parameter</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td>events</td>
|
||||||
|
<td>[Event]</td>
|
||||||
|
<td>List of send-to-device messages.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
`Event`
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr class="header">
|
||||||
|
<th>Parameter</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><p>content</p></td>
|
||||||
|
<td><p>EventContent</p></td>
|
||||||
|
<td><p>The content of this event. The fields in this object will vary depending on the type of event.</p></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><p>sender</p></td>
|
||||||
|
<td><p>string</p></td>
|
||||||
|
<td><p>The Matrix user ID of the user who sent this event.</p></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td>type</td>
|
||||||
|
<td>string</td>
|
||||||
|
<td>The type of event.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
{
|
||||||
|
"next_batch": "s72595_4483_1934",
|
||||||
|
"rooms": {"leave": {}, "join": {}, "invite": {}},
|
||||||
|
"to_device": {
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"sender": "@alice:example.com",
|
||||||
|
"type": "m.new_device",
|
||||||
|
"content": {
|
||||||
|
"device_id": "XYZABCDE",
|
||||||
|
"rooms": ["!726s6s6q:example.com"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 290
|
||||||
|
---
|
||||||
|
|
||||||
|
### Server Access Control Lists (ACLs) for rooms
|
||||||
|
|
||||||
|
In some scenarios room operators may wish to prevent a malicious or
|
||||||
|
untrusted server from participating in their room. Sending an
|
||||||
|
[m.room.server\_acl]() state event into a room is an effective way to
|
||||||
|
prevent the server from participating in the room at the federation
|
||||||
|
level.
|
||||||
|
|
||||||
|
Server ACLs can also be used to make rooms only federate with a limited
|
||||||
|
set of servers, or retroactively make the room no longer federate with
|
||||||
|
any other server, similar to setting the `m.federate` value on the
|
||||||
|
[m.room.create]() event.
|
||||||
|
|
||||||
|
{{m\_room\_server\_acl\_event}}
|
||||||
|
|
||||||
|
Note
|
||||||
|
|
||||||
|
Port numbers are not supported because it is unclear to parsers whether
|
||||||
|
a port number should be matched or an IP address literal. Additionally,
|
||||||
|
it is unlikely that one would trust a server running on a particular
|
||||||
|
domain's port but not a different port, especially considering the
|
||||||
|
server host can easily change ports.
|
||||||
|
|
||||||
|
Note
|
||||||
|
|
||||||
|
CIDR notation is not supported for IP addresses because Matrix does not
|
||||||
|
encourage the use of IPs for identifying servers. Instead, a blanket
|
||||||
|
`allow_ip_literals` is provided to cover banning them.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients are not expected to perform any additional duties beyond sending
|
||||||
|
the event. Clients should describe changes to the server ACLs to the
|
||||||
|
user in the user interface, such as in the timeline.
|
||||||
|
|
||||||
|
Clients may wish to kick affected users from the room prior to denying a
|
||||||
|
server access to the room to help prevent those servers from
|
||||||
|
participating and to provide feedback to the users that they have been
|
||||||
|
excluded from the room.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Servers MUST prevent blacklisted servers from sending events or
|
||||||
|
participating in the room when an [m.room.server\_acl]() event is
|
||||||
|
present in the room state. Which APIs are specifically affected are
|
||||||
|
described in the Server-Server API specification.
|
||||||
|
|
||||||
|
Servers should still send events to denied servers if they are still
|
||||||
|
residents of the room.
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Server ACLs are only effective if every server in the room honours them.
|
||||||
|
Servers that do not honour the ACLs may still permit events sent by
|
||||||
|
denied servers into the room, leaking them to other servers in the room.
|
||||||
|
To effectively enforce an ACL in a room, the servers that do not honour
|
||||||
|
the ACLs should be denied in the room as well.
|
@ -0,0 +1,68 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 320
|
||||||
|
---
|
||||||
|
|
||||||
|
### Server Notices
|
||||||
|
|
||||||
|
Homeserver hosts often want to send messages to users in an official
|
||||||
|
capacity, or have resource limits which affect a user's ability to use
|
||||||
|
the homeserver. For example, the homeserver may be limited to a certain
|
||||||
|
number of active users per month and has exceeded that limit. To
|
||||||
|
communicate this failure to users, the homeserver would use the Server
|
||||||
|
Notices room.
|
||||||
|
|
||||||
|
The aesthetics of the room (name, topic, avatar, etc) are left as an
|
||||||
|
implementation detail. It is recommended that the homeserver decorate
|
||||||
|
the room such that it looks like an official room to users.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
Notices are sent to the client as normal `m.room.message` events with a
|
||||||
|
`msgtype` of `m.server_notice` in the server notices room. Events with a
|
||||||
|
`m.server_notice` `msgtype` outside of the server notice room must be
|
||||||
|
ignored by clients.
|
||||||
|
|
||||||
|
The specified values for `server_notice_type` are:
|
||||||
|
|
||||||
|
`m.server_notice.usage_limit_reached`
|
||||||
|
The server has exceeded some limit which requires the server
|
||||||
|
administrator to intervene. The `limit_type` describes the kind of limit
|
||||||
|
reached. The specified values for `limit_type` are:
|
||||||
|
|
||||||
|
`monthly_active_user`
|
||||||
|
The server's number of active users in the last 30 days has exceeded the
|
||||||
|
maximum. New connections are being refused by the server. What defines
|
||||||
|
"active" is left as an implementation detail, however servers are
|
||||||
|
encouraged to treat syncing users as "active".
|
||||||
|
|
||||||
|
{{m\_room\_message\_m\_server\_notice\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients can identify the server notices room by the `m.server_notice`
|
||||||
|
tag on the room. Active notices are represented by the [pinned
|
||||||
|
events](#m-room-pinned-events) in the server notices room. Server notice
|
||||||
|
events pinned in that room should be shown to the user through special
|
||||||
|
UI and not through the normal pinned events interface in the client. For
|
||||||
|
example, clients may show warning banners or bring up dialogs to get the
|
||||||
|
user's attention. Events which are not server notice events and are
|
||||||
|
pinned in the server notices room should be shown just like any other
|
||||||
|
pinned event in a room.
|
||||||
|
|
||||||
|
The client must not expect to be able to reject an invite to join the
|
||||||
|
server notices room. Attempting to reject the invite must result in a
|
||||||
|
`M_CANNOT_LEAVE_SERVER_NOTICE_ROOM` error. Servers should not prevent
|
||||||
|
the user leaving the room after joining the server notices room, however
|
||||||
|
the same error code must be used if the server will prevent leaving the
|
||||||
|
room.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Servers should manage exactly 1 server notices room per user. Servers
|
||||||
|
must identify this room to clients with the `m.server_notice` tag.
|
||||||
|
Servers should invite the target user rather than automatically join
|
||||||
|
them to the server notice room.
|
||||||
|
|
||||||
|
How servers send notices to clients, and which user they use to send the
|
||||||
|
events, is left as an implementation detail for the server.
|
@ -0,0 +1,309 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 220
|
||||||
|
---
|
||||||
|
|
||||||
|
### SSO client login/authentication
|
||||||
|
|
||||||
|
Single Sign-On (SSO) is a generic term which refers to protocols which
|
||||||
|
allow users to log into applications via a single web-based
|
||||||
|
authentication portal. Examples include OpenID Connect, "Central
|
||||||
|
Authentication Service" (CAS) and SAML.
|
||||||
|
|
||||||
|
This module allows a Matrix homeserver to delegate user authentication
|
||||||
|
to an external authentication server supporting one of these protocols.
|
||||||
|
In this process, there are three systems involved:
|
||||||
|
|
||||||
|
> - A Matrix client, using the APIs defined this specification, which
|
||||||
|
> is seeking to authenticate a user to a Matrix homeserver.
|
||||||
|
> - A Matrix homeserver, implementing the APIs defined in this
|
||||||
|
> specification, but which is delegating user authentication to the
|
||||||
|
> authentication server.
|
||||||
|
> - An "authentication server", which is responsible for
|
||||||
|
> authenticating the user.
|
||||||
|
|
||||||
|
This specification is concerned only with communication between the
|
||||||
|
Matrix client and the homeserver, and is independent of the SSO protocol
|
||||||
|
used to communicate with the authentication server. Different Matrix
|
||||||
|
homeserver implementations might support different SSO protocols.
|
||||||
|
|
||||||
|
Clients and homeservers implementing the SSO flow will need to consider
|
||||||
|
both [login]() and [user-interactive authentication](). The flow is
|
||||||
|
similar in both cases, but there are slight differences.
|
||||||
|
|
||||||
|
Typically, SSO systems require a single "callback" URI to be configured
|
||||||
|
at the authentication server. Once the user is authenticated, their
|
||||||
|
browser is redirected to that URI. It is up to the Matrix homeserver
|
||||||
|
implementation to implement a suitable endpoint. For example, for CAS
|
||||||
|
authentication the homeserver should provide a means for the
|
||||||
|
administrator to configure where the CAS server is and the REST
|
||||||
|
endpoints which consume the ticket.
|
||||||
|
|
||||||
|
#### Client login via SSO
|
||||||
|
|
||||||
|
An overview of the process is as follows:
|
||||||
|
|
||||||
|
1. The Matrix client calls `GET /login`\_ to find the supported login
|
||||||
|
types, and the homeserver includes a flow with
|
||||||
|
`"type": "m.login.sso"` in the response.
|
||||||
|
2. To initiate the `m.login.sso` login type, the Matrix client
|
||||||
|
instructs the user's browser to navigate to the
|
||||||
|
`/login/sso/redirect`\_ endpoint on the user's homeserver.
|
||||||
|
3. The homeserver responds with an HTTP redirect to the SSO user
|
||||||
|
interface, which the browser follows.
|
||||||
|
4. The authentication server and the homeserver interact to verify the
|
||||||
|
user's identity and other authentication information, potentially
|
||||||
|
using a number of redirects.
|
||||||
|
5. The browser is directed to the `redirectUrl` provided by the client
|
||||||
|
with a `loginToken` query parameter for the client to log in with.
|
||||||
|
6. The client exchanges the login token for an access token by calling
|
||||||
|
the `/login`\_ endpoint with a `type` of `m.login.token`.
|
||||||
|
|
||||||
|
For native applications, typically steps 1 to 4 are carried out by
|
||||||
|
opening an embedded web view.
|
||||||
|
|
||||||
|
These steps are illustrated as follows:
|
||||||
|
|
||||||
|
Matrix Client Matrix Homeserver Auth Server
|
||||||
|
| | |
|
||||||
|
|-------------(0) GET /login----------->| |
|
||||||
|
|<-------------login types--------------| |
|
||||||
|
| | |
|
||||||
|
| Webview | |
|
||||||
|
| | | |
|
||||||
|
|----->| | |
|
||||||
|
| |--(1) GET /login/sso/redirect-->| |
|
||||||
|
| |<---------(2) 302---------------| |
|
||||||
|
| | | |
|
||||||
|
| |<========(3) Authentication process================>|
|
||||||
|
| | | |
|
||||||
|
| |<--(4) redirect to redirectUrl--| |
|
||||||
|
|<-----| | |
|
||||||
|
| | |
|
||||||
|
|---(5) POST /login with login token--->| |
|
||||||
|
|<-------------access token-------------| |
|
||||||
|
|
||||||
|
Note
|
||||||
|
|
||||||
|
In the older [r0.4.0
|
||||||
|
version](https://matrix.org/docs/spec/client_server/r0.4.0.html#cas-based-client-login)
|
||||||
|
of this specification it was possible to authenticate via CAS when the
|
||||||
|
homeserver provides a `m.login.cas` login flow. This specification
|
||||||
|
deprecates the use of `m.login.cas` to instead prefer `m.login.sso`,
|
||||||
|
which is the same process with the only change being which redirect
|
||||||
|
endpoint to use: for `m.login.cas`, use `/cas/redirect` and for
|
||||||
|
`m.login.sso` use `/sso/redirect` (described below). The endpoints are
|
||||||
|
otherwise the same.
|
||||||
|
|
||||||
|
##### Client behaviour
|
||||||
|
|
||||||
|
The client starts the process by instructing the browser to navigate to
|
||||||
|
`/login/sso/redirect`\_ with an appropriate `redirectUrl`. Once
|
||||||
|
authentication is successful, the browser will be redirected to that
|
||||||
|
`redirectUrl`.
|
||||||
|
|
||||||
|
{{sso\_login\_redirect\_cs\_http\_api}}
|
||||||
|
|
||||||
|
###### Security considerations
|
||||||
|
|
||||||
|
1. CSRF attacks via manipulation of parameters on the `redirectUrl`
|
||||||
|
|
||||||
|
Clients should validate any requests to the `redirectUrl`. In
|
||||||
|
particular, it may be possible for attackers to falsify any query
|
||||||
|
parameters, leading to cross-site request forgery (CSRF) attacks.
|
||||||
|
|
||||||
|
For example, consider a web-based client at
|
||||||
|
`https://client.example.com`, which wants to initiate SSO login on
|
||||||
|
the homeserver at `server.example.org`. It does this by storing the
|
||||||
|
homeserver name in a query parameter for the `redirectUrl`: it
|
||||||
|
redirects to
|
||||||
|
`https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=server.example.org`.
|
||||||
|
|
||||||
|
An attacker could trick a victim into following a link to
|
||||||
|
`https://server.example.org/login/sso/redirect?redirectUrl=https://client.example.com?hs=evil.com`,
|
||||||
|
which would result in the client sending a login token for the
|
||||||
|
victim's account to the attacker-controlled site `evil.com`.
|
||||||
|
|
||||||
|
To guard against this, clients MUST NOT store state (such as the
|
||||||
|
address of the homeserver being logged into) anywhere it can be
|
||||||
|
modified by external processes.
|
||||||
|
|
||||||
|
Instead, the state could be stored in
|
||||||
|
[localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
|
||||||
|
or in a cookie.
|
||||||
|
|
||||||
|
2. For added security, clients SHOULD include a unique identifier in
|
||||||
|
the `redirectUrl` and reject any callbacks that do not contain a
|
||||||
|
recognised identifier, to guard against unsolicited login attempts
|
||||||
|
and replay attacks.
|
||||||
|
|
||||||
|
##### Server behaviour
|
||||||
|
|
||||||
|
###### Redirecting to the Authentication server
|
||||||
|
|
||||||
|
The server should handle
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect` as follows:
|
||||||
|
|
||||||
|
1. It should build a suitable request for the SSO system.
|
||||||
|
2. It should store enough state that the flow can be securely resumed
|
||||||
|
after the SSO process completes. One way to do this is by storing a
|
||||||
|
cookie which is stored in the user's browser, by adding a
|
||||||
|
`Set-Cookie` header to the response.
|
||||||
|
3. It should redirect the user's browser to the SSO login page with the
|
||||||
|
appropriate parameters.
|
||||||
|
|
||||||
|
See also the "Security considerations" below.
|
||||||
|
|
||||||
|
###### Handling the callback from the Authentication server
|
||||||
|
|
||||||
|
Note that there will normally be a single callback URI which is used for
|
||||||
|
both login and user-interactive authentication: it is up to the
|
||||||
|
homeserver implementation to distinguish which is taking place.
|
||||||
|
|
||||||
|
The homeserver should validate the response from the SSO system: this
|
||||||
|
may require additional calls to the authentication server, and/or may
|
||||||
|
require checking a signature on the response.
|
||||||
|
|
||||||
|
The homeserver then proceeds as follows:
|
||||||
|
|
||||||
|
1. The homeserver MUST map the user details received from the
|
||||||
|
authentication server to a valid [Matrix user
|
||||||
|
identifier](../appendices.html#user-identifiers). The guidance in
|
||||||
|
[Mapping from other character
|
||||||
|
sets](../appendices.html#mapping-from-other-character-sets) may be
|
||||||
|
useful.
|
||||||
|
2. If the generated user identifier represents a new user, it should be
|
||||||
|
registered as a new user.
|
||||||
|
3. The homeserver should generate a short-term login token. This is an
|
||||||
|
opaque token, suitable for use with the `m.login.token` type of the
|
||||||
|
`/login`\_ API. The lifetime of this token SHOULD be limited to
|
||||||
|
around five seconds.
|
||||||
|
4. The homeserver adds a query parameter of `loginToken`, with the
|
||||||
|
value of the generated login token, to the `redirectUrl` given in
|
||||||
|
the `/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`
|
||||||
|
request. (Note: `redirectURL` may or may not include existing query
|
||||||
|
parameters. If it already includes one or more `loginToken`
|
||||||
|
parameters, they should be removed before adding the new one.)
|
||||||
|
5. The homeserver redirects the user's browser to the URI thus built.
|
||||||
|
|
||||||
|
##### Security considerations
|
||||||
|
|
||||||
|
1. Homeservers should ensure that login tokens are not sent to
|
||||||
|
malicious clients.
|
||||||
|
|
||||||
|
For example, consider a homeserver at `server.example.org`. An
|
||||||
|
attacker tricks a victim into following a link to
|
||||||
|
`https://server.example.org/login/sso/redirect?redirectUrl=https://evil.com`,
|
||||||
|
resulting in a login token being sent to the attacker-controlled
|
||||||
|
site `evil.com`. This is a form of cross-site request forgery
|
||||||
|
(CSRF).
|
||||||
|
|
||||||
|
To mitigate this, Homeservers SHOULD confirm with the user that they
|
||||||
|
are happy to grant access to their matrix account to the site named
|
||||||
|
in the `redirectUrl`. This can be done either *before* redirecting
|
||||||
|
to the SSO login page when handling the
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`
|
||||||
|
endpoint, or *after* login when handling the callback from the
|
||||||
|
authentication server. (If the check is performed before
|
||||||
|
redirecting, it is particularly important that the homeserver guards
|
||||||
|
against unsolicited authentication attempts as below).
|
||||||
|
|
||||||
|
It may be appropriate to whitelist a set of known-trusted client
|
||||||
|
URLs in this process. In particular, the homeserver's own [login
|
||||||
|
fallback]() implementation could be excluded.
|
||||||
|
|
||||||
|
2. For added security, homeservers SHOULD guard against unsolicited
|
||||||
|
authentication attempts by tracking pending requests. One way to do
|
||||||
|
this is to set a cookie when handling
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`, which
|
||||||
|
is checked and cleared when handling the callback from the
|
||||||
|
authentication server.
|
||||||
|
|
||||||
|
#### SSO during User-Interactive Authentication
|
||||||
|
|
||||||
|
[User-interactive authentication]() is used by client-server endpoints
|
||||||
|
which require additional confirmation of the user's identity (beyond
|
||||||
|
holding an access token). Typically this means that the user must
|
||||||
|
re-enter their password, but for homeservers which delegate to an SSO
|
||||||
|
server, this means redirecting to the authentication server during
|
||||||
|
user-interactive auth.
|
||||||
|
|
||||||
|
The implemementation of this is based on the [Fallback]() mechanism for
|
||||||
|
user-interactive auth.
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients do not need to take any particular additional steps beyond
|
||||||
|
ensuring that the fallback mechanism has been implemented, and treating
|
||||||
|
the `m.login.sso` authentication type the same as any other unknown type
|
||||||
|
(i.e. they should open a browser window for
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web?session=<session_id>`.
|
||||||
|
Once the flow has completed, the client retries the request with the
|
||||||
|
session only.)
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
##### Redirecting to the Authentication server
|
||||||
|
|
||||||
|
The server should handle
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web`
|
||||||
|
in much the same way as
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/login/sso/redirect`, which is to
|
||||||
|
say:
|
||||||
|
|
||||||
|
1. It should build a suitable request for the SSO system.
|
||||||
|
2. It should store enough state that the flow can be securely resumed
|
||||||
|
after the SSO process completes. One way to do this is by storing a
|
||||||
|
cookie which is stored in the user's browser, by adding a
|
||||||
|
`Set-Cookie` header to the response.
|
||||||
|
3. It should redirect the user's browser to the SSO login page with the
|
||||||
|
appropriate parameters.
|
||||||
|
|
||||||
|
See also the "Security considerations" below.
|
||||||
|
|
||||||
|
###### Handling the callback from the Authentication server
|
||||||
|
|
||||||
|
Note that there will normally be a single callback URI which is used for
|
||||||
|
both login and user-interactive authentication: it is up to the
|
||||||
|
homeserver implementation to distinguish which is taking place.
|
||||||
|
|
||||||
|
The homeserver should validate the response from the SSO system: this
|
||||||
|
may require additional calls to the authentication server, and/or may
|
||||||
|
require checking a signature on the response.
|
||||||
|
|
||||||
|
The homeserver then returns the [user-interactive authentication
|
||||||
|
fallback completion]() page to the user's browser.
|
||||||
|
|
||||||
|
###### Security considerations
|
||||||
|
|
||||||
|
1. Confirming the operation
|
||||||
|
|
||||||
|
The homeserver SHOULD confirm that the user is happy for the
|
||||||
|
operation to go ahead. The goal of the user-interactive
|
||||||
|
authentication operation is to guard against a compromised
|
||||||
|
`access_token` being used to take over the user's account. Simply
|
||||||
|
redirecting the user to the SSO system is insufficient, since they
|
||||||
|
may not realise what is being asked of them, or the SSO system may
|
||||||
|
even confirm the authentication automatically.
|
||||||
|
|
||||||
|
For example, the homeserver might serve a page with words to the
|
||||||
|
effect of:
|
||||||
|
|
||||||
|
> A client is trying to remove a device from your account. To
|
||||||
|
> confirm this action, re-authenticate with single sign-on. If you
|
||||||
|
> did not expect this, your account may be compromised!
|
||||||
|
|
||||||
|
This confirmation could take place before redirecting to the SSO
|
||||||
|
authentication page (when handling the
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web`
|
||||||
|
endpoint), or *after* authentication when handling the callback from
|
||||||
|
the authentication server. (If the check is performed before
|
||||||
|
redirecting, it is particularly important that the homeserver guards
|
||||||
|
against unsolicited authentication attempts as below).
|
||||||
|
|
||||||
|
2. For added security, homeservers SHOULD guard against unsolicited
|
||||||
|
authentication attempts by tracking pending requests. One way to do
|
||||||
|
this is to set a cookie when handling
|
||||||
|
`/_matrix/client/%CLIENT_MAJOR_VERSION%/auth/m.login.sso/fallback/web`,
|
||||||
|
which is checked and cleared when handling the callback from the
|
||||||
|
authentication server.
|
@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 250
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sticker Messages
|
||||||
|
|
||||||
|
This module allows users to send sticker messages in to rooms or direct
|
||||||
|
messaging sessions.
|
||||||
|
|
||||||
|
Sticker messages are specialised image messages that are displayed
|
||||||
|
without controls (e.g. no "download" link, or light-box view on click,
|
||||||
|
as would be displayed for for [m.image]() events).
|
||||||
|
|
||||||
|
Sticker messages are intended to provide simple "reaction" events in the
|
||||||
|
message timeline. The matrix client should provide some mechanism to
|
||||||
|
display the sticker "body" e.g. as a tooltip on hover, or in a modal
|
||||||
|
when the sticker image is clicked.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
Sticker events are received as a single `m.sticker` event in the
|
||||||
|
`timeline` section of a room, in a `/sync`.
|
||||||
|
|
||||||
|
{{m\_sticker\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
Clients supporting this message type should display the image content
|
||||||
|
from the event URL directly in the timeline.
|
||||||
|
|
||||||
|
A thumbnail image should be provided in the `info` object. This is
|
||||||
|
largely intended as a fallback for clients that do not fully support the
|
||||||
|
`m.sticker` event type. In most cases it is fine to set the thumbnail
|
||||||
|
URL to the same URL as the main event content.
|
||||||
|
|
||||||
|
It is recommended that sticker image content should be 512x512 pixels in
|
||||||
|
size or smaller. The dimensions of the image file should be twice the
|
||||||
|
intended display size specified in the `info` object in order to assist
|
||||||
|
rendering sharp images on higher DPI screens.
|
@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 180
|
||||||
|
---
|
||||||
|
|
||||||
|
### Room Tagging
|
||||||
|
|
||||||
|
Users can add tags to rooms. Tags are namespaced strings used to label
|
||||||
|
rooms. A room may have multiple tags. Tags are only visible to the user
|
||||||
|
that set them but are shared across all their devices.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
The tags on a room are received as single `m.tag` event in the
|
||||||
|
`account_data` section of a room. The content of the `m.tag` event is a
|
||||||
|
`tags` key whose value is an object mapping the name of each tag to
|
||||||
|
another object.
|
||||||
|
|
||||||
|
The JSON object associated with each tag gives information about the
|
||||||
|
tag, e.g how to order the rooms with a given tag.
|
||||||
|
|
||||||
|
Ordering information is given under the `order` key as a number between
|
||||||
|
0 and 1. The numbers are compared such that 0 is displayed first.
|
||||||
|
Therefore a room with an `order` of `0.2` would be displayed before a
|
||||||
|
room with an `order` of `0.7`. If a room has a tag without an `order`
|
||||||
|
key then it should appear after the rooms with that tag that have an
|
||||||
|
`order` key.
|
||||||
|
|
||||||
|
The name of a tag MUST NOT exceed 255 bytes.
|
||||||
|
|
||||||
|
The tag namespace is defined as follows:
|
||||||
|
|
||||||
|
- The namespace `m.*` is reserved for tags defined in the Matrix
|
||||||
|
specification. Clients must ignore any tags in this namespace they
|
||||||
|
don't understand.
|
||||||
|
- The namespace `u.*` is reserved for user-defined tags. The portion
|
||||||
|
of the string after the `u.` is defined to be the display name of
|
||||||
|
this tag. No other semantics should be inferred from tags in this
|
||||||
|
namespace.
|
||||||
|
- A client or app willing to use special tags for advanced
|
||||||
|
functionality should namespace them similarly to state keys:
|
||||||
|
`tld.name.*`
|
||||||
|
- Any tag in the `tld.name.*` form but not matching the namespace of
|
||||||
|
the current client should be ignored
|
||||||
|
- Any tag not matching the above rules should be interpreted as a user
|
||||||
|
tag from the `u.*` namespace, as if the name had already had `u.`
|
||||||
|
stripped from the start (ie. the name of the tag is used as the
|
||||||
|
display name directly). These non-namespaced tags are supported for
|
||||||
|
historical reasons. New tags should use one of the defined
|
||||||
|
namespaces above.
|
||||||
|
|
||||||
|
Several special names are listed in the specification: The following
|
||||||
|
tags are defined in the `m.*` namespace:
|
||||||
|
|
||||||
|
- `m.favourite`: The user's favourite rooms. These should be shown
|
||||||
|
with higher precedence than other rooms.
|
||||||
|
- `m.lowpriority`: These should be shown with lower precedence than
|
||||||
|
others.
|
||||||
|
- `m.server_notice`: Used to identify [Server Notice
|
||||||
|
Rooms](#module-server-notices).
|
||||||
|
|
||||||
|
{{m\_tag\_event}}
|
||||||
|
|
||||||
|
#### Client Behaviour
|
||||||
|
|
||||||
|
{{tags\_cs\_http\_api}}
|
@ -0,0 +1,235 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 140
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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. See the
|
||||||
|
[m.room.member](#m-room-member) schema for more information.
|
||||||
|
|
||||||
|
#### 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\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
Upon receipt of an `/invite`, the server is expected to look up the
|
||||||
|
third party identifier with the provided identity server. If the lookup
|
||||||
|
yields a result for a Matrix User ID then the normal invite process can
|
||||||
|
be initiated. This process ends up looking like this:
|
||||||
|
|
||||||
|
+---------+ +-------------+ +-----------------+
|
||||||
|
| Client | | Homeserver | | IdentityServer |
|
||||||
|
+---------+ +-------------+ +-----------------+
|
||||||
|
| | |
|
||||||
|
| POST /invite | |
|
||||||
|
|------------------------------------>| |
|
||||||
|
| | |
|
||||||
|
| | GET /lookup |
|
||||||
|
| |--------------------------------------------------->|
|
||||||
|
| | |
|
||||||
|
| | User ID result |
|
||||||
|
| |<---------------------------------------------------|
|
||||||
|
| | |
|
||||||
|
| | Invite process for the discovered User ID |
|
||||||
|
| |------------------------------------------ |
|
||||||
|
| | | |
|
||||||
|
| |<----------------------------------------- |
|
||||||
|
| | |
|
||||||
|
| Complete the /invite request | |
|
||||||
|
|<------------------------------------| |
|
||||||
|
| | |
|
||||||
|
|
||||||
|
However, if the lookup does not yield a bound User ID, the homeserver
|
||||||
|
must store the invite on the identity server and emit a valid
|
||||||
|
`m.room.third_party_invite` event to the room. This process ends up
|
||||||
|
looking like this:
|
||||||
|
|
||||||
|
+---------+ +-------------+ +-----------------+
|
||||||
|
| Client | | Homeserver | | IdentityServer |
|
||||||
|
+---------+ +-------------+ +-----------------+
|
||||||
|
| | |
|
||||||
|
| POST /invite | |
|
||||||
|
|------------------------------------>| |
|
||||||
|
| | |
|
||||||
|
| | GET /lookup |
|
||||||
|
| |-------------------------------------------------------------->|
|
||||||
|
| | |
|
||||||
|
| | "no users" result |
|
||||||
|
| |<--------------------------------------------------------------|
|
||||||
|
| | |
|
||||||
|
| | POST /store-invite |
|
||||||
|
| |-------------------------------------------------------------->|
|
||||||
|
| | |
|
||||||
|
| | Information needed for the m.room.third_party_invite |
|
||||||
|
| |<--------------------------------------------------------------|
|
||||||
|
| | |
|
||||||
|
| | Emit m.room.third_party_invite to the room |
|
||||||
|
| |------------------------------------------- |
|
||||||
|
| | | |
|
||||||
|
| |<------------------------------------------ |
|
||||||
|
| | |
|
||||||
|
| Complete the /invite request | |
|
||||||
|
|<------------------------------------| |
|
||||||
|
| | |
|
||||||
|
|
||||||
|
All homeservers MUST verify the signature in the event's
|
||||||
|
`content.third_party_invite.signed` object.
|
||||||
|
|
||||||
|
The third party user will then need to verify their identity, which
|
||||||
|
results in a call from the identity server to the homeserver that bound
|
||||||
|
the third party identifier to a user. The homeserver then exchanges the
|
||||||
|
`m.room.third_party_invite` event in the room for a complete
|
||||||
|
`m.room.member` event for `membership: invite` for the user that has
|
||||||
|
bound the third party identifier.
|
||||||
|
|
||||||
|
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, given H1, H2, and H3 as homeservers, UserA as a user of H1,
|
||||||
|
and an identity server IS, the full sequence for a third party invite
|
||||||
|
would look like the following. This diagram assumes H1 and H2 are
|
||||||
|
residents of the room while H3 is attempting to join.
|
||||||
|
|
||||||
|
+-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+
|
||||||
|
| UserA | | ThirdPartyUser | | H1 | | H2 | | H3 | | IS |
|
||||||
|
+-------+ +-----------------+ +-----+ +-----+ +-----+ +-----+
|
||||||
|
| | | | | |
|
||||||
|
| POST /invite for ThirdPartyUser | | | |
|
||||||
|
|----------------------------------->| | | |
|
||||||
|
| | | | | |
|
||||||
|
| | | GET /lookup | | |
|
||||||
|
| | |---------------------------------------------------------------------------------------------->|
|
||||||
|
| | | | | |
|
||||||
|
| | | | Lookup results (empty object) |
|
||||||
|
| | |<----------------------------------------------------------------------------------------------|
|
||||||
|
| | | | | |
|
||||||
|
| | | POST /store-invite | | |
|
||||||
|
| | |---------------------------------------------------------------------------------------------->|
|
||||||
|
| | | | | |
|
||||||
|
| | | | Token, keys, etc for third party invite |
|
||||||
|
| | |<----------------------------------------------------------------------------------------------|
|
||||||
|
| | | | | |
|
||||||
|
| | | (Federation) Emit m.room.third_party_invite | | |
|
||||||
|
| | |----------------------------------------------->| | |
|
||||||
|
| | | | | |
|
||||||
|
| Complete /invite request | | | |
|
||||||
|
|<-----------------------------------| | | |
|
||||||
|
| | | | | |
|
||||||
|
| | Verify identity | | | |
|
||||||
|
| |-------------------------------------------------------------------------------------------------------------------->|
|
||||||
|
| | | | | |
|
||||||
|
| | | | | POST /3pid/onbind |
|
||||||
|
| | | | |<---------------------------|
|
||||||
|
| | | | | |
|
||||||
|
| | | PUT /exchange_third_party_invite/:roomId | |
|
||||||
|
| | |<-----------------------------------------------------------------| |
|
||||||
|
| | | | | |
|
||||||
|
| | | Verify the request | | |
|
||||||
|
| | |------------------- | | |
|
||||||
|
| | | | | | |
|
||||||
|
| | |<------------------ | | |
|
||||||
|
| | | | | |
|
||||||
|
| | | (Federation) Emit m.room.member for invite | | |
|
||||||
|
| | |----------------------------------------------->| | |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | |
|
||||||
|
| | | (Federation) Emit the m.room.member event sent to H2 | |
|
||||||
|
| | |----------------------------------------------------------------->| |
|
||||||
|
| | | | | |
|
||||||
|
| | | Complete /exchange_third_party_invite/:roomId request | |
|
||||||
|
| | |----------------------------------------------------------------->| |
|
||||||
|
| | | | | |
|
||||||
|
| | | | | Participate in the room |
|
||||||
|
| | | | |------------------------ |
|
||||||
|
| | | | | | |
|
||||||
|
| | | | |<----------------------- |
|
||||||
|
| | | | | |
|
||||||
|
|
||||||
|
Note that when H1 sends the `m.room.member` event to H2 and H3 it does
|
||||||
|
not have to block on either server's receipt of the event. Likewise, H1
|
||||||
|
may complete the `/exchange_third_party_invite/:roomId` request at the
|
||||||
|
same time as sending the `m.room.member` event to H2 and H3.
|
||||||
|
Additionally, H3 may complete the `/3pid/onbind` request it got from IS
|
||||||
|
at any time - the completion is not shown in the diagram.
|
||||||
|
|
||||||
|
H1 MUST verify the request from H3 to ensure the `signed` property is
|
||||||
|
correct as well as the `key_validity_url` as still being valid. This is
|
||||||
|
done by making a request to the [identity server
|
||||||
|
/isvalid](../identity_service/%IDENTITY_RELEASE_LABEL%.html#get-matrix-identity-v2-pubkey-isvalid)
|
||||||
|
endpoint, using the provided URL rather than constructing a new one. The
|
||||||
|
query string and response for the provided URL must match the Identity
|
||||||
|
Service Specification.
|
||||||
|
|
||||||
|
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 privacy 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.
|
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 270
|
||||||
|
---
|
||||||
|
|
||||||
|
### Third Party Networks
|
||||||
|
|
||||||
|
Application services can provide access to third party networks via
|
||||||
|
bridging. This allows Matrix users to communicate with users on other
|
||||||
|
communication platforms, with messages ferried back and forth by the
|
||||||
|
application service. A single application service may bridge multiple
|
||||||
|
third party networks, and many individual locations within those
|
||||||
|
networks. A single third party network location may be bridged to
|
||||||
|
multiple Matrix rooms.
|
||||||
|
|
||||||
|
#### Third Party Lookups
|
||||||
|
|
||||||
|
A client may wish to provide a rich interface for joining third party
|
||||||
|
locations and connecting with third party users. Information necessary
|
||||||
|
for such an interface is provided by third party lookups.
|
||||||
|
|
||||||
|
{{third\_party\_lookup\_cs\_http\_api}}
|
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 30
|
||||||
|
---
|
||||||
|
|
||||||
|
### Typing Notifications
|
||||||
|
|
||||||
|
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](index.html#event-graphs) but still have a `room_id` key.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{m\_typing\_event}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
When a client receives an `m.typing` event, it MUST use the user ID list
|
||||||
|
to **REPLACE** its knowledge of every user who is currently typing. The
|
||||||
|
reason for this is that the server *does not remember* users who are not
|
||||||
|
currently typing as that list gets big quickly. The client should mark
|
||||||
|
as not typing any user ID who is not in that list.
|
||||||
|
|
||||||
|
It is recommended that clients store a `boolean` indicating whether the
|
||||||
|
user is typing or not. Whilst this value is `true` a timer should fire
|
||||||
|
periodically every N seconds to send a typing HTTP request. The value of
|
||||||
|
N is recommended to be no more than 20-30 seconds. This request should
|
||||||
|
be re-sent by the client to continue informing the server the user is
|
||||||
|
still typing. As subsequent requests will replace older requests, a
|
||||||
|
safety margin of 5 seconds before the expected timeout runs out is
|
||||||
|
recommended. When the user stops typing, the state change of the
|
||||||
|
`boolean` to `false` should trigger another HTTP request to inform the
|
||||||
|
server that the user has stopped typing.
|
||||||
|
|
||||||
|
{{typing\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Clients may not wish to inform everyone in a room that they are typing
|
||||||
|
and instead only specific users in the room.
|
@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
type: module
|
||||||
|
weight: 20
|
||||||
|
---
|
||||||
|
|
||||||
|
### Voice over IP
|
||||||
|
|
||||||
|
This module outlines how two users in a room can set up a Voice over IP
|
||||||
|
(VoIP) call to each other. Voice and video calls are built upon the
|
||||||
|
WebRTC 1.0 standard. Call signalling is achieved by sending [message
|
||||||
|
events]() to the room. In this version of the spec, only two-party
|
||||||
|
communication is supported (e.g. between two peers, or between a peer
|
||||||
|
and a multi-point conferencing unit). This means that clients MUST only
|
||||||
|
send call events to rooms with exactly two participants.
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
|
||||||
|
{{voip\_events}}
|
||||||
|
|
||||||
|
#### Client behaviour
|
||||||
|
|
||||||
|
A call is set up with message events exchanged as follows:
|
||||||
|
|
||||||
|
Caller Callee
|
||||||
|
[Place Call]
|
||||||
|
m.call.invite ----------->
|
||||||
|
m.call.candidate -------->
|
||||||
|
[..candidates..] -------->
|
||||||
|
[Answers call]
|
||||||
|
<--------------- m.call.answer
|
||||||
|
[Call is active and ongoing]
|
||||||
|
<--------------- m.call.hangup
|
||||||
|
|
||||||
|
Or a rejected call:
|
||||||
|
|
||||||
|
Caller Callee
|
||||||
|
m.call.invite ------------>
|
||||||
|
m.call.candidate --------->
|
||||||
|
[..candidates..] --------->
|
||||||
|
[Rejects call]
|
||||||
|
<-------------- m.call.hangup
|
||||||
|
|
||||||
|
Calls are negotiated according to the WebRTC specification.
|
||||||
|
|
||||||
|
##### Glare
|
||||||
|
|
||||||
|
"Glare" is a problem which occurs when two users call each other at
|
||||||
|
roughly the same time. This results in the call failing to set up as
|
||||||
|
there already is an incoming/outgoing call. A glare resolution algorithm
|
||||||
|
can be used to determine which call to hangup and which call to answer.
|
||||||
|
If both clients implement the same algorithm then they will both select
|
||||||
|
the same call and the call will be successfully connected.
|
||||||
|
|
||||||
|
As calls are "placed" to rooms rather than users, the glare resolution
|
||||||
|
algorithm outlined below is only considered for calls which are to the
|
||||||
|
same room. The algorithm is as follows:
|
||||||
|
|
||||||
|
- If an `m.call.invite` to a room is received whilst the client is
|
||||||
|
**preparing to send** an `m.call.invite` to the same room:
|
||||||
|
- the client should cancel its outgoing call and instead
|
||||||
|
automatically accept the incoming call on behalf of the user.
|
||||||
|
- If an `m.call.invite` to a room is received **after the client has
|
||||||
|
sent** an `m.call.invite` to the same room and is waiting for a
|
||||||
|
response:
|
||||||
|
- the client should perform a lexicographical comparison of the
|
||||||
|
call IDs of the two calls and use the *lesser* of the two calls,
|
||||||
|
aborting the greater. If the incoming call is the lesser, the
|
||||||
|
client should accept this call on behalf of the user.
|
||||||
|
|
||||||
|
The call setup should appear seamless to the user as if they had simply
|
||||||
|
placed a call and the other party had accepted. This means any media
|
||||||
|
stream that had been setup for use on a call should be transferred and
|
||||||
|
used for the call that replaces it.
|
||||||
|
|
||||||
|
#### Server behaviour
|
||||||
|
|
||||||
|
The homeserver MAY provide a TURN server which clients can use to
|
||||||
|
contact the remote party. The following HTTP API endpoints will be used
|
||||||
|
by clients in order to get information about the TURN server.
|
||||||
|
|
||||||
|
{{voip\_cs\_http\_api}}
|
||||||
|
|
||||||
|
#### Security considerations
|
||||||
|
|
||||||
|
Calls should only be placed to rooms with one other user in them. If
|
||||||
|
they are placed to group chat rooms it is possible that another user
|
||||||
|
will intercept and answer the call.
|
@ -0,0 +1,14 @@
|
|||||||
|
{{/*
|
||||||
|
|
||||||
|
This template is used to embed module documentation in the client-server API spec.
|
||||||
|
|
||||||
|
It searches the site for pages of type "module", sorts them by weight, and
|
||||||
|
emits the page's rendered content.
|
||||||
|
|
||||||
|
*/}}
|
||||||
|
|
||||||
|
{{ $modules := where site.Pages "Type" "module" }}
|
||||||
|
|
||||||
|
{{ range $modules.ByWeight }}
|
||||||
|
{{ .Content }}
|
||||||
|
{{ end }}
|
Loading…
Reference in New Issue